From 3bc3148347c4a4eb05d8f712cee1d441220c86b6 Mon Sep 17 00:00:00 2001 From: Jeod <47716344+JeodC@users.noreply.github.com> Date: Thu, 10 Apr 2025 07:52:03 -0400 Subject: [PATCH 1/8] Add game_change --- gmloader/gameframe.cpp | 1 - gmloader/input.cpp | 1 - gmloader/libyoyo.cpp | 58 +++++++++++++++++++++ gmloader/libyoyo.h | 5 ++ gmloader/main.cpp | 101 +++++++++++++++++++++++++++++++++---- gmloader/texture.cpp | 1 - gmloader/texture_arm32.cpp | 1 - gmloader/texture_arm64.cpp | 1 - 8 files changed, 155 insertions(+), 14 deletions(-) diff --git a/gmloader/gameframe.cpp b/gmloader/gameframe.cpp index 6ee99ab..d88605e 100644 --- a/gmloader/gameframe.cpp +++ b/gmloader/gameframe.cpp @@ -1,7 +1,6 @@ #include "platform.h" #include "so_util.h" #include "libyoyo.h" -#include "configuration.h" static const char* gameframe_stubs[] = { "gameframe_mouse_in_window_raw", diff --git a/gmloader/input.cpp b/gmloader/input.cpp index 65fe596..5d31f82 100644 --- a/gmloader/input.cpp +++ b/gmloader/input.cpp @@ -6,7 +6,6 @@ #include "platform.h" #include "so_util.h" #include "libyoyo.h" -#include "configuration.h" int app_in_focus = 0; int mouse_has_warped = 0; diff --git a/gmloader/libyoyo.cpp b/gmloader/libyoyo.cpp index 3cbf586..4326e33 100644 --- a/gmloader/libyoyo.cpp +++ b/gmloader/libyoyo.cpp @@ -1,5 +1,8 @@ #include #include +#include +#include +#include #include "platform.h" #include "so_util.h" @@ -176,6 +179,60 @@ ABI_ATTR static void window_handle(RValue *ret, void *self, void *other, int arg ret->rvalue.v64 = 0; } +// Implementation of game_change which is not available for android normally + // Takes two arguments: work_dir and launch_params (example: "/chapter1_windows" "-game data.win") + // https://manual.gamemaker.io/beta/en/GameMaker_Language/GML_Reference/General_Game_Control/game_change.htm +ABI_ATTR void game_change_reimpl(RValue *ret, void *self, void *other, int argc, RValue *args) { + char buffer[1024]; + int len = snprintf(buffer, sizeof(buffer), "game_change(): "); + for (int i = 0; i < argc && len < sizeof(buffer); i++) { + const char *arg = (args[i].kind == VALUE_STRING && args[i].rvalue.str && args[i].rvalue.str->m_thing) + ? (char*)args[i].rvalue.str->m_thing + : "INVALID"; + len += snprintf(buffer + len, sizeof(buffer) - len, "%s%s", i > 0 ? ", " : "", arg); + } + warning("%s\n", buffer); + + if (ret) { + ret->kind = VALUE_BOOL; + ret->rvalue.val = 0; + } + + if (argc < 2) { + warning("game_change(): Requires at least two arguments (workdir and params)\n"); + return; + } + + for (int i = 0; i < argc; i++) { + if (args[i].kind != VALUE_STRING || !args[i].rvalue.str || !args[i].rvalue.str->m_thing) { + warning("game_change(): Argument %d is not a valid string\n", i); + return; + } + } + + const char *workdir = static_cast(args[0].rvalue.str->m_thing); + gc_workdir = strdup(workdir); + if (!gc_workdir) { + warning("game_change(): Failed to duplicate workdir\n"); + return; + } + + std::string sub_path = gc_workdir; + if (!sub_path.empty() && sub_path.back() != '/') { + sub_path += '/'; + } + + std::string full_path = gmloader_config.save_dir + sub_path; + warning("game_change(): Resolved game path: '%s'\n", full_path.c_str()); + + relaunch_flag = 1; + + if (ret) { + ret->kind = VALUE_BOOL; + ret->rvalue.val = 1; + } +} + void patch_libyoyo(so_module *mod) { // Load all of the native symbols referenced @@ -276,6 +333,7 @@ void patch_libyoyo(so_module *mod) } Function_Add("window_handle", window_handle, 0, 1); + Function_Add("game_change", game_change_reimpl, 2, 0); so_symbol_fix_ldmia(mod, "_Z11Shader_LoadPhjS_"); } diff --git a/gmloader/libyoyo.h b/gmloader/libyoyo.h index 08b7b8a..36d169c 100644 --- a/gmloader/libyoyo.h +++ b/gmloader/libyoyo.h @@ -1,5 +1,7 @@ #pragma once +#include "configuration.h" + #define MASK_KIND_RVALUE 0x0ffffff typedef enum RValueType { VALUE_REAL = 0, @@ -278,6 +280,9 @@ extern void **g_pGameFileBuffer; extern void **g_ppYYStackTrace; extern int *Extension_Main_number; +extern const char *gc_workdir; +extern int relaunch_flag; + extern void patch_libyoyo(struct so_module *mod); extern void patch_input(struct so_module *mod); extern void patch_gamepad(struct so_module *mod); diff --git a/gmloader/main.cpp b/gmloader/main.cpp index 3555656..fbb435c 100644 --- a/gmloader/main.cpp +++ b/gmloader/main.cpp @@ -1,6 +1,8 @@ #include #include +#include #include +#include #include #include "platform.h" @@ -11,9 +13,13 @@ #include "classes/RunnerJNILib.h" #include "khronos/gles2.h" #include "libyoyo.h" -#include "configuration.h" #include "texture.h" + +int relaunch_flag = 0; +char *program_name = nullptr; +const char* gc_workdir = nullptr; + /* Don't touch this incantation. It serves no practical reason that you can grep this source code for, nothing @@ -103,25 +109,45 @@ static fs::path get_absolute_path(const char* path, fs::path work_dir){ int main(int argc, char *argv[]) { + // Store the program name from argv[0] + if (argc > 0 && argv[0]) { + program_name = argv[0]; + } else + { + fatal_error("Main: Could not determine program name from argv[0]\n"); + return -1; + } + gmloader_config.init_defaults(); fs::path work_dir, config_file_path, save_dir, apk_path; work_dir = fs::canonical(fs::current_path()) / ""; - if (argc > 2 && strcmp(argv[1], "-c") == 0) { - - config_file_path = work_dir / argv[2]; - - if(gmloader_config.parse_file(config_file_path.c_str()) < 0 ){ - warning("Error while loading the config file\n"); + // Check for -a (apk_path override) and -c (config file) + std::string override_apk_path; + for (int i = 1; i < argc; ++i) { + if (strcmp(argv[i], "-a") == 0 && i + 1 < argc) { + override_apk_path = argv[i + 1]; + warning("Main: Using apk_path override from args: '%s'\n", override_apk_path.c_str()); + i++; // Skip the value + } + else if (strcmp(argv[i], "-c") == 0 && i + 1 < argc) { + config_file_path = work_dir / argv[i + 1]; + if (gmloader_config.parse_file(config_file_path.c_str()) < 0) { + warning("Error while loading the config file\n"); + } } + } + // Apply apk_path override if provided + if (!override_apk_path.empty()) { + gmloader_config.apk_path = override_apk_path; } char platform_ov[32]; strncpy(platform_ov, gmloader_config.force_platform.c_str(), sizeof(platform_ov) - 1); platform_ov[sizeof(platform_ov) - 1] = '\0'; - + std::unordered_map platform_map = { {"os_unknown", os_unknown}, {"os_windows", os_windows}, @@ -274,7 +300,7 @@ int main(int argc, char *argv[]) RunnerJNILib::Startup(env, 0, apk_path_arg, save_dir_arg, pkg_dir_arg, 4, 0); setup_ended = 1; - while (cont != 0 && cont != 2 && RunnerJNILib_MoveTaskToBackCalled == 0) { + while (cont != 0 && cont != 2 && RunnerJNILib_MoveTaskToBackCalled == 0 && relaunch_flag == 0) { if (update_inputs(sdl_win) != 1) break; SDL_GetWindowSize(sdl_win, &w, &h); @@ -287,5 +313,62 @@ int main(int argc, char *argv[]) SDL_DestroyWindow(sdl_win); SDL_Quit(); + // Check for relaunch + if (relaunch_flag && gc_workdir) { + warning("Main: Relaunch triggered. workdir='%s'\n", gc_workdir); + + // Extract subfolder prefix from original apk_path (e.g., "assets/") + std::string orig_apk_path = gmloader_config.apk_path; + std::string prefix; + size_t last_slash = orig_apk_path.find_last_of('/'); + if (last_slash != std::string::npos) { + prefix = orig_apk_path.substr(0, last_slash + 1); // Include the slash + } + + // Remove leading and trailing slashes from gc_workdir (may or may not have any) + std::string workdir_clean = gc_workdir; + if (!workdir_clean.empty() && workdir_clean.front() == '/') + workdir_clean = workdir_clean.substr(1); + if (!workdir_clean.empty() && workdir_clean.back() == '/') + workdir_clean = workdir_clean.substr(0, workdir_clean.length() - 1); + std::string new_apk_path = prefix + workdir_clean; + + // Check if the override is valid + bool use_override = (new_apk_path.find("..") == std::string::npos && !workdir_clean.empty()); + if (use_override) { + gmloader_config.apk_path = new_apk_path; + warning("Main: Updated config: apk_path='%s', save_dir='%s'\n", + gmloader_config.apk_path.c_str(), gmloader_config.save_dir.c_str()); + } else { + warning("Main: Ignoring override='%s' (workdir='%s'), using original apk_path='%s'\n", + new_apk_path.c_str(), gc_workdir, gmloader_config.apk_path.c_str()); + } + + // Relaunch: use -a only if override is valid, otherwise just -c + char apk_path_arg[1024]; + char config_path_arg[1024]; + char *argv_relaunch[6]; + int arg_count = 0; + argv_relaunch[arg_count++] = program_name; + argv_relaunch[arg_count++] = (char*)"-c"; + snprintf(config_path_arg, sizeof(config_path_arg), "%s", config_file_path.c_str()); + argv_relaunch[arg_count++] = config_path_arg; + if (use_override) { + snprintf(apk_path_arg, sizeof(apk_path_arg), "%s", gmloader_config.apk_path.c_str()); + argv_relaunch[arg_count++] = (char*)"-a"; + argv_relaunch[arg_count++] = apk_path_arg; + } + argv_relaunch[arg_count] = nullptr; + + warning("Main: Relaunching '%s' with apk_path='%s', save_dir='%s'\n", + program_name, gmloader_config.apk_path.c_str(), gmloader_config.save_dir.c_str()); + fflush(stdout); + fflush(stderr); + if (execv(program_name, argv_relaunch) == -1) { + fatal_error("Main: Failed to relaunch '%s': %s\n", program_name, strerror(errno)); + return -1; + } + } + return 0; } diff --git a/gmloader/texture.cpp b/gmloader/texture.cpp index 9c9028a..354038b 100644 --- a/gmloader/texture.cpp +++ b/gmloader/texture.cpp @@ -6,7 +6,6 @@ #include "so_util.h" #include "io_util.h" #include "libyoyo.h" -#include "configuration.h" #define STB_ONLY_PNG #include "stb_image.h" diff --git a/gmloader/texture_arm32.cpp b/gmloader/texture_arm32.cpp index 7b1028a..0d208e0 100644 --- a/gmloader/texture_arm32.cpp +++ b/gmloader/texture_arm32.cpp @@ -7,7 +7,6 @@ #include "so_util.h" #include "platform.h" #include "libyoyo.h" -#include "configuration.h" #include "texture.h" ABI_ATTR void LoadTextureFromPNG_1(uint32_t *texture, int has_mips) { diff --git a/gmloader/texture_arm64.cpp b/gmloader/texture_arm64.cpp index 08a2607..da60617 100644 --- a/gmloader/texture_arm64.cpp +++ b/gmloader/texture_arm64.cpp @@ -7,7 +7,6 @@ #include "so_util.h" #include "platform.h" #include "libyoyo.h" -#include "configuration.h" #include "texture.h" ABI_ATTR void LoadTextureFromPNG_1(uintptr_t texture, int has_mips) From ad3c4417b551f8b312efde9b4150c44d1416453e Mon Sep 17 00:00:00 2001 From: Jeod <47716344+JeodC@users.noreply.github.com> Date: Sat, 19 Apr 2025 08:11:59 -0400 Subject: [PATCH 2/8] Add a rumble reimpl --- gmloader/configuration.cpp | 7 +++++++ gmloader/configuration.h | 2 ++ gmloader/gamepad.cpp | 12 ++++++++++++ gmloader/input.cpp | 30 +++++++++++++++++++++++++++++- gmloader/libyoyo.h | 3 ++- gmloader/main.cpp | 2 +- 6 files changed, 53 insertions(+), 3 deletions(-) diff --git a/gmloader/configuration.cpp b/gmloader/configuration.cpp index 9e4dd5c..a147b03 100644 --- a/gmloader/configuration.cpp +++ b/gmloader/configuration.cpp @@ -11,6 +11,8 @@ void from_json(const json& j, gml_config& c) { get_if_exists("show_cursor", show_cursor); get_if_exists("disable_controller", disable_controller); get_if_exists("disable_depth", disable_depth); + get_if_exists("disable_rumble", disable_rumble); + get_if_exists("rumble_scale", rumble_scale); get_if_exists("disable_texhack", disable_texhack); get_if_exists("force_platform", force_platform); } @@ -21,6 +23,8 @@ void gml_config::init_defaults(){ show_cursor = true; disable_controller = false; disable_depth = false; + disable_rumble = false; + rumble_scale = 1.0; disable_texhack = true; /* Disabled by default until properly tested. */ force_platform = "os_android"; } @@ -52,6 +56,9 @@ void gml_config::show_config(){ printf("config: show_cursor = %d\n", show_cursor); printf("config: disable_controller = %d\n", disable_controller); printf("config: disable_depth = %d\n", disable_depth); + printf("config: disable_extensions = %d\n", disable_extensions); + printf("config: disable_rumble = %d\n", disable_rumble); + printf("config: rumble_scale = %f\n", rumble_scale); printf("config: disable_texhack = %d\n", disable_texhack); printf("config: force_platform = %s\n", force_platform.c_str()); } \ No newline at end of file diff --git a/gmloader/configuration.h b/gmloader/configuration.h index cb529ec..7f6a155 100644 --- a/gmloader/configuration.h +++ b/gmloader/configuration.h @@ -13,6 +13,8 @@ struct gml_config { bool show_cursor; bool disable_controller; bool disable_depth; + bool disable_rumble; + float rumble_scale; bool disable_texhack; std::string force_platform; diff --git a/gmloader/gamepad.cpp b/gmloader/gamepad.cpp index d3081e8..f096909 100644 --- a/gmloader/gamepad.cpp +++ b/gmloader/gamepad.cpp @@ -219,6 +219,18 @@ ABI_ATTR void gamepad_button_value(RValue *ret, void *self, void *other, int arg ABI_ATTR void gamepad_set_vibration(RValue *ret, void *self, void *other, int argc, RValue *args) { + int id = YYGetInt32(args, 0); + double left = YYGetReal(args, 1); + double right = YYGetReal(args, 2); + + if (!IS_CONTROLLER_BOUNDS) { + ret->rvalue.val = 0.0f; + return; + } + + Gamepad *gamepad = &yoyo_gamepads[id]; + gamepad->motors[0] = left; + gamepad->motors[1] = right; } ABI_ATTR void gamepad_set_axis_deadzone(RValue *ret, void *self, void *other, int argc, RValue *args) diff --git a/gmloader/input.cpp b/gmloader/input.cpp index 5d31f82..48beea0 100644 --- a/gmloader/input.cpp +++ b/gmloader/input.cpp @@ -301,7 +301,7 @@ int update_inputs(SDL_Window *win) } } - // Gamepad Input Code + // Synchronize gamepad<->yoyogamepad states SDL_GameControllerUpdate(); for (int i = 0; i < ARRAY_SIZE(sdl_controllers); i++) { controller_t *controller = &sdl_controllers[i]; @@ -328,6 +328,34 @@ int update_inputs(SDL_Window *win) new_states[k++] = SDL_GameControllerGetButton(controller->controller, SDL_CONTROLLER_BUTTON_DPAD_LEFT); new_states[k++] = SDL_GameControllerGetButton(controller->controller, SDL_CONTROLLER_BUTTON_DPAD_RIGHT); + // Handle rumble + if (!gmloader_config.disable_rumble) { + const uint16_t MAX_RUMBLE = 0xFFFF; + const float MAX_RUMBLE_F = 65535.0f; + + float left = yoyo_gamepads[slot].motors[0]; + float right = yoyo_gamepads[slot].motors[1]; + + // Apply scale + if (gmloader_config.rumble_scale > 0.0f) { + float scale = (float)gmloader_config.rumble_scale; + left *= scale; + right *= scale; + + // Clamp to [0.0, 1.0] + if (left > 1.0f) left = 1.0f; + if (left < 0.0f) left = 0.0f; + if (right > 1.0f) right = 1.0f; + if (right < 0.0f) right = 0.0f; + } + + uint16_t left_motor = (uint16_t)(left * MAX_RUMBLE_F); + uint16_t right_motor = (uint16_t)(right * MAX_RUMBLE_F); + int duration_ms = (left_motor || right_motor) ? 10000 : 0; + + SDL_GameControllerRumble(controller->controller, left_motor, right_motor, duration_ms); + } + for (int j = 0; j < ARRAY_SIZE(yoyo_gamepads[slot].buttons); j++) { // down -> held or up -> cleared yoyo_gamepads[slot].buttons[j] = (double)update_button(new_states[j], (int)yoyo_gamepads[slot].buttons[j]); diff --git a/gmloader/libyoyo.h b/gmloader/libyoyo.h index 36d169c..7715833 100644 --- a/gmloader/libyoyo.h +++ b/gmloader/libyoyo.h @@ -65,9 +65,10 @@ typedef struct Gamepad { double buttons[16]; double deadzone; double axis[4]; + double motors[1]; Gamepad() - : is_available(0), was_available(0), buttons{0}, deadzone(0.1), axis{0} + : is_available(0), was_available(0), buttons{0}, deadzone(0.1), axis{0}, motors{0} { /* ... */ } } Gamepad; diff --git a/gmloader/main.cpp b/gmloader/main.cpp index fbb435c..d05f918 100644 --- a/gmloader/main.cpp +++ b/gmloader/main.cpp @@ -247,7 +247,7 @@ int main(int argc, char *argv[]) printf("apk_path %s save_dir %s pkg_dir %s\n", apk_path_arg->str, save_dir_arg->str, pkg_dir_arg->str); // Initialize SDL with video, audio, joystick, and controller support - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) != 0) { + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC) != 0) { fatal_error("SDL could not initialize! SDL_Error: %s\n", SDL_GetError()); return -1; } From 294cc9997b3d796e9964b4f111f588145cd87ed2 Mon Sep 17 00:00:00 2001 From: Jeod <47716344+JeodC@users.noreply.github.com> Date: Sat, 19 Apr 2025 09:30:27 -0400 Subject: [PATCH 3/8] Create new stubs folder for platform-specific stubs --- Makefile.gmloader | 1 + gmloader/libyoyo.cpp | 29 +--- gmloader/libyoyo.h | 2 + gmloader/main.cpp | 1 + .../stubs_gameframe.cpp} | 0 gmloader/stubs/stubs_steam.cpp | 140 ++++++++++++++++++ 6 files changed, 147 insertions(+), 26 deletions(-) rename gmloader/{gameframe.cpp => stubs/stubs_gameframe.cpp} (100%) create mode 100644 gmloader/stubs/stubs_steam.cpp diff --git a/Makefile.gmloader b/Makefile.gmloader index 0865a37..617f80c 100644 --- a/Makefile.gmloader +++ b/Makefile.gmloader @@ -64,6 +64,7 @@ JNI_OBJS:=$(patsubst %.cpp,build/%.cpp.o,$(wildcard jni/classes/*.cpp)) JNI_OBJS+=$(patsubst %.cpp,build/%.cpp.o,$(wildcard jni/*.cpp)) GMLOADER_OBJS:=$(patsubst %.cpp,build/%.cpp.o,$(wildcard gmloader/*.cpp)) GMLOADER_OBJS+=$(patsubst %.cpp,build/%.cpp.o,$(wildcard gmloader/classes/*.cpp)) +GMLOADER_OBJS+=$(patsubst %.cpp,build/%.cpp.o,$(wildcard gmloader/stubs/*.cpp)) GENERIC_OBJ:=\ ${LOADER_OBJS} \ diff --git a/gmloader/libyoyo.cpp b/gmloader/libyoyo.cpp index 4326e33..574cb4f 100644 --- a/gmloader/libyoyo.cpp +++ b/gmloader/libyoyo.cpp @@ -69,7 +69,10 @@ uint8_t prev_kbd_state[N_KEYS] = {}; uint8_t cur_keys[N_KEYS] = {}; static const char *fake_functs[] = { + // Other stubs "object_set_collisions", + + // PSN Stubs "psn_show_error_dialog", "psn_check_free_space", "psn_get_leaderboard_score_range", @@ -90,14 +93,6 @@ static const char *fake_functs[] = { "psn_name_for_user", "psn_default_user", "psn_user_for_pad", - "steam_utils_is_steam_running_on_steam_deck", - "steam_update", - "steam_is_screenshot_requested", - "steam_send_screenshot", - "steam_initialised", - "steam_stats_ready", - "steam_get_achievement", - "steam_set_achievement", }; double FORCE_PLATFORM = os_android; @@ -154,24 +149,6 @@ ABI_ATTR void force_platform_type_gms2(void *self, int n, RValue *args) args[0].rvalue.val = FORCE_PLATFORM; } -ABI_ATTR static void steam_utils_is_steam_running_on_steam_deck(RValue *ret, void *self, void *other, int argc, RValue *args) -{ - ret->kind = VALUE_REAL; - ret->rvalue.val = 0; -} - -ABI_ATTR static void steam_initialised(RValue *ret, void *self, void *other, int argc, RValue *args) -{ - ret->kind = VALUE_REAL; - ret->rvalue.val = 0; -} - -ABI_ATTR static void steam_stats_ready(RValue *ret, void *self, void *other, int argc, RValue *args) -{ - ret->kind = VALUE_REAL; - ret->rvalue.val = 0; -} - ABI_ATTR static void window_handle(RValue *ret, void *self, void *other, int argc, RValue *args) { warning("-- Called window_handle stub ! --\n"); diff --git a/gmloader/libyoyo.h b/gmloader/libyoyo.h index 7715833..aa1a265 100644 --- a/gmloader/libyoyo.h +++ b/gmloader/libyoyo.h @@ -290,6 +290,8 @@ extern void patch_gamepad(struct so_module *mod); extern void patch_mouse(struct so_module *mod); extern void patch_fmod(struct so_module *mod); extern void patch_gameframe(struct so_module *mod); +extern void patch_steam(struct so_module *mod); extern void patch_texture(struct so_module *mod); extern int update_inputs(struct SDL_Window *sdl_win); + void disable_depth(); \ No newline at end of file diff --git a/gmloader/main.cpp b/gmloader/main.cpp index d05f918..ca25ca5 100644 --- a/gmloader/main.cpp +++ b/gmloader/main.cpp @@ -227,6 +227,7 @@ int main(int argc, char *argv[]) patch_mouse(&libyoyo); patch_fmod(&libyoyo); patch_gameframe(&libyoyo); + patch_steam(&libyoyo); patch_texture(&libyoyo); int *ms_freq = NULL; diff --git a/gmloader/gameframe.cpp b/gmloader/stubs/stubs_gameframe.cpp similarity index 100% rename from gmloader/gameframe.cpp rename to gmloader/stubs/stubs_gameframe.cpp diff --git a/gmloader/stubs/stubs_steam.cpp b/gmloader/stubs/stubs_steam.cpp new file mode 100644 index 0000000..9ef1a49 --- /dev/null +++ b/gmloader/stubs/stubs_steam.cpp @@ -0,0 +1,140 @@ +#include "platform.h" +#include "so_util.h" +#include "libyoyo.h" + +static const char* steam_stubs[] = { + // Steam stubs + // https://github.com/YoYoGames/GMEXT-Steamworks/wiki + + // Management + "steam_init", + "steam_update", + "steam_shutdown", + + // General + "steam_initialised", + "steam_stats_ready", + //"steam_get_app_id", + //"steam_get_user_account_id", + //"steam_get_user_steam_id", + //"steam_get_persona_name", + //"steam_get_user_persona_name", + //"steam_get_user_persona_name_sync", + //"steam_is_user_logged_on", + //"steam_user_get_auth_ticket_for_web_api", + //"steam_user_get_auth_session_ticket", + //"steam_user_cancel_auth_ticket", + "steam_current_game_language", + //"steam_available_languages", + //"steam_is_subscribed", + //"steam_set_warning_message_hook", + + // Overlay + "steam_is_overlay_enabled", + "steam_is_overlay_activated", + //"steam_activate_overlay", + //"steam_activate_overlay_browser", + //"steam_activate_overlay_store", + //"steam_activate_overlay_user", + //"steam_set_overlay_notification_inset", + //"steam_set_overlay_notification_position", + + // Leaderboards + //"steam_create_leaderboard", + //"steam_upload_score", + //"steam_upload_score_ext", + //"steam_upload_score_buffer", + //"steam_upload_score_buffer_ext", + //"steam_download_scores", + //"steam_download_scores_around_user", + //"steam_download_friends_scores", + //"steam_get_leaderboard_entry_count", + //"steam_get_leaderboard_display_type", + + // Stats & Achievements + "steam_set_achievement", + "steam_get_achievement", + "steam_clear_achievement", + //"steam_indicate_achievement_progress", + //"steam_get_achievement_progress_limits_int", + //"steam_get_achievement_progress_limits_float", + "steam_set_stat_int", + "steam_set_stat_float", + "steam_set_stat_avg_rate", + "steam_get_stat_int", + "steam_get_stat_float", + "steam_get_stat_avg_rate", + "steam_reset_all_stats", + "steam_reset_all_stats_achievements", + //"steam_request_global_stats", + //"steam_request_global_achievement_percentages", + //"steam_get_achievement_achieved_percent", + //"steam_get_most_achieved_achievement_info", + //"steam_get_next_most_achieved_achievement_info", + //"steam_get_global_stat_int", + //"steam_get_global_stat_real", + //"steam_get_global_stat_history_int", + //"steam_get_global_stat_history_real", + //"steam_get_number_of_current_players", + + // Cloud + //"steam_is_cloud_enabled_for_app", + //"steam_is_cloud_enabled_for_account", + //"steam_get_quota_total", + //"steam_get_quota_free", + //"steam_file_exists", + //"steam_file_size", + //"steam_file_persisted", + //"steam_file_write", + //"steam_file_write_file", + //"steam_file_read", + //"steam_file_read_buffer", + //"steam_file_write_buffer", + //"steam_file_share", + //"steam_file_delete", + //"steam_get_local_file_change", + //"steam_get_local_file_change_count", + //"steam_file_get_list", + + // UGC + "steam_is_screenshot_requested", + "steam_send_screenshot", + + // Social + //"steam_set_rich_presence", + //"steam_clear_rich_presence", + //"steam_request_friend_rich_presence", + //"steam_get_friend_rich_presence", + //"steam_get_friend_rich_presence_key_count", + //"steam_get_friend_rich_presence_key_by_index", + //"steam_user_set_played_with", + //"steam_get_friends_game_info", + //"steam_get_user_avatar", + //"steam_image_get_size", + //"steam_image_get_rgba", + //"steam_image_get_bgra", + + // Utils + //"steam_show_floating_gamepad_text_input", + //"steam_dismiss_floating_gamepad_text_input", + //"steam_show_gamepad_text_input", + //"steam_get_entered_gamepad_text_input", + //"steam_utils_enable_callbacks", + "steam_utils_is_steam_running_on_steam_deck", + //"steam_utils_is_steam_in_big_picture_mode", + //"steam_utils_set_game_launcher_mode", + //"steam_utils_get_server_real_time", + //"steam_utils_get_steam_ui_language" +}; + +ABI_ATTR void steam_stub(RValue *ret, void *self, void *other, int argc, RValue *args) +{ + ret->kind = VALUE_REAL; + ret->rvalue.val = 0; +} + +void patch_steam(struct so_module *mod) +{ + for (auto &func: steam_stubs) + Function_Add(func, steam_stub, 1, 0); +} \ No newline at end of file From 5dca34fe724a602f8acbfec65bb6700b24c124ab Mon Sep 17 00:00:00 2001 From: Jeod <47716344+JeodC@users.noreply.github.com> Date: Sat, 19 Apr 2025 15:31:02 -0400 Subject: [PATCH 4/8] Allow enabling or disabling extensions via config This is necessary for at least one GMS port (Victory Heat Rally) --- gmloader/configuration.cpp | 2 ++ gmloader/configuration.h | 1 + gmloader/libyoyo.cpp | 10 ++++++---- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/gmloader/configuration.cpp b/gmloader/configuration.cpp index a147b03..d5acd51 100644 --- a/gmloader/configuration.cpp +++ b/gmloader/configuration.cpp @@ -11,6 +11,7 @@ void from_json(const json& j, gml_config& c) { get_if_exists("show_cursor", show_cursor); get_if_exists("disable_controller", disable_controller); get_if_exists("disable_depth", disable_depth); + get_if_exists("disable_extensions", disable_extensions); get_if_exists("disable_rumble", disable_rumble); get_if_exists("rumble_scale", rumble_scale); get_if_exists("disable_texhack", disable_texhack); @@ -23,6 +24,7 @@ void gml_config::init_defaults(){ show_cursor = true; disable_controller = false; disable_depth = false; + disable_extensions = true; disable_rumble = false; rumble_scale = 1.0; disable_texhack = true; /* Disabled by default until properly tested. */ diff --git a/gmloader/configuration.h b/gmloader/configuration.h index 7f6a155..3a02fee 100644 --- a/gmloader/configuration.h +++ b/gmloader/configuration.h @@ -13,6 +13,7 @@ struct gml_config { bool show_cursor; bool disable_controller; bool disable_depth; + bool disable_extensions; bool disable_rumble; float rumble_scale; bool disable_texhack; diff --git a/gmloader/libyoyo.cpp b/gmloader/libyoyo.cpp index 574cb4f..6788e3d 100644 --- a/gmloader/libyoyo.cpp +++ b/gmloader/libyoyo.cpp @@ -269,10 +269,12 @@ void patch_libyoyo(so_module *mod) FIND_SYMBOL(mod, surface_depth_disable, "_Z21F_SurfaceDepthDisableR6RValueP9CInstanceS2_iPS_"); // Disable extension support - FIND_SYMBOL(mod, Extension_Main_number, "Extension_Main_number"); - //hook_symbol(mod, "_Z20Extension_Initializev", (uintptr_t)&dont_init_extensions, 1); - hook_symbol(mod, "_Z20Extension_PrePreparev", (uintptr_t)&dont_init_extensions, 1); - hook_symbol(mod, "_Z14Extension_LoadPhjS_", (uintptr_t)&dont_init_extensions, 1); + if (gmloader_config.disable_extensions == 1) { + FIND_SYMBOL(mod, Extension_Main_number, "Extension_Main_number"); + //hook_symbol(mod, "_Z20Extension_Initializev", (uintptr_t)&dont_init_extensions, 1); + hook_symbol(mod, "_Z20Extension_PrePreparev", (uintptr_t)&dont_init_extensions, 1); + hook_symbol(mod, "_Z14Extension_LoadPhjS_", (uintptr_t)&dont_init_extensions, 1); + } // Hook messages for debug hook_symbol(mod, "_Z11ShowMessagePKc", (uintptr_t)&show_message, 1); From 14b1c5f6a502f96509861982bd90a22b6d5ba0fd Mon Sep 17 00:00:00 2001 From: Jeod <47716344+JeodC@users.noreply.github.com> Date: Sat, 19 Apr 2025 15:59:39 -0400 Subject: [PATCH 5/8] Stub video_open Immediately return that the video ended Tidy up --- gmloader/libyoyo.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/gmloader/libyoyo.cpp b/gmloader/libyoyo.cpp index 6788e3d..9028afe 100644 --- a/gmloader/libyoyo.cpp +++ b/gmloader/libyoyo.cpp @@ -210,6 +210,21 @@ ABI_ATTR void game_change_reimpl(RValue *ret, void *self, void *other, int argc, } } +ABI_ATTR static void video_open_reimpl(RValue *ret, void *self, void *other, int argc, RValue *args) +{ + ret->kind = VALUE_BOOL; + ret->rvalue.val = 1; + + if (CreateAsynEventWithDSMap != NULL && CreateDsMap != NULL && dsMapAddString != NULL) { + int ds_map = CreateDsMap(0); + dsMapAddString(ds_map, "type", "video_end"); + if (argc > 0 && args[0].kind == VALUE_STRING && args[0].rvalue.str && args[0].rvalue.str->m_thing) { + dsMapAddString(ds_map, "path", static_cast(args[0].rvalue.str->m_thing)); + } + CreateAsynEventWithDSMap(ds_map, 70); + } +} + void patch_libyoyo(so_module *mod) { // Load all of the native symbols referenced @@ -313,6 +328,7 @@ void patch_libyoyo(so_module *mod) Function_Add("window_handle", window_handle, 0, 1); Function_Add("game_change", game_change_reimpl, 2, 0); + Function_Add("video_open", video_open_reimpl, 1, 1); so_symbol_fix_ldmia(mod, "_Z11Shader_LoadPhjS_"); } From 5fe7766ca102c678e82ed98448deea3b601d35cd Mon Sep 17 00:00:00 2001 From: Jeod <47716344+JeodC@users.noreply.github.com> Date: Thu, 24 Apr 2025 10:11:02 -0400 Subject: [PATCH 6/8] Update gmloader/main.cpp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cyril Delétré --- gmloader/main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/gmloader/main.cpp b/gmloader/main.cpp index ca25ca5..c37e4bd 100644 --- a/gmloader/main.cpp +++ b/gmloader/main.cpp @@ -136,6 +136,7 @@ int main(int argc, char *argv[]) if (gmloader_config.parse_file(config_file_path.c_str()) < 0) { warning("Error while loading the config file\n"); } + i++; // Skip the value } } From edec9725ce0fd0fd2bf540c9bc511617cc756b1b Mon Sep 17 00:00:00 2001 From: Jeod <47716344+JeodC@users.noreply.github.com> Date: Thu, 24 Apr 2025 10:14:52 -0400 Subject: [PATCH 7/8] Use double until the final conversion --- gmloader/input.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/gmloader/input.cpp b/gmloader/input.cpp index 48beea0..39f409e 100644 --- a/gmloader/input.cpp +++ b/gmloader/input.cpp @@ -331,26 +331,26 @@ int update_inputs(SDL_Window *win) // Handle rumble if (!gmloader_config.disable_rumble) { const uint16_t MAX_RUMBLE = 0xFFFF; - const float MAX_RUMBLE_F = 65535.0f; + const double MAX_RUMBLE_D = 65535.0; - float left = yoyo_gamepads[slot].motors[0]; - float right = yoyo_gamepads[slot].motors[1]; + double left = (double)yoyo_gamepads[slot].motors[0]; + double right = (double)yoyo_gamepads[slot].motors[1]; // Apply scale - if (gmloader_config.rumble_scale > 0.0f) { - float scale = (float)gmloader_config.rumble_scale; + if (gmloader_config.rumble_scale > 0.0) { + double scale = gmloader_config.rumble_scale; left *= scale; right *= scale; // Clamp to [0.0, 1.0] - if (left > 1.0f) left = 1.0f; - if (left < 0.0f) left = 0.0f; - if (right > 1.0f) right = 1.0f; - if (right < 0.0f) right = 0.0f; + if (left > 1.0) left = 1.0; + if (left < 0.0) left = 0.0; + if (right > 1.0) right = 1.0; + if (right < 0.0) right = 0.0; } - uint16_t left_motor = (uint16_t)(left * MAX_RUMBLE_F); - uint16_t right_motor = (uint16_t)(right * MAX_RUMBLE_F); + uint16_t left_motor = (uint16_t)(left * MAX_RUMBLE_D); + uint16_t right_motor = (uint16_t)(right * MAX_RUMBLE_D); int duration_ms = (left_motor || right_motor) ? 10000 : 0; SDL_GameControllerRumble(controller->controller, left_motor, right_motor, duration_ms); From e9225f668cd2950fdef2edf763cf93310f5a0fe3 Mon Sep 17 00:00:00 2001 From: Jeod <47716344+JeodC@users.noreply.github.com> Date: Thu, 24 Apr 2025 10:17:32 -0400 Subject: [PATCH 8/8] Update input.cpp --- gmloader/input.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/gmloader/input.cpp b/gmloader/input.cpp index 39f409e..4a4b0ba 100644 --- a/gmloader/input.cpp +++ b/gmloader/input.cpp @@ -330,7 +330,6 @@ int update_inputs(SDL_Window *win) // Handle rumble if (!gmloader_config.disable_rumble) { - const uint16_t MAX_RUMBLE = 0xFFFF; const double MAX_RUMBLE_D = 65535.0; double left = (double)yoyo_gamepads[slot].motors[0];