Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile.gmloader
Original file line number Diff line number Diff line change
Expand Up @@ -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} \
Expand Down
9 changes: 9 additions & 0 deletions gmloader/configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ 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);
get_if_exists("force_platform", force_platform);
}
Expand All @@ -21,6 +24,9 @@ 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. */
force_platform = "os_android";
}
Expand Down Expand Up @@ -52,6 +58,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());
}
3 changes: 3 additions & 0 deletions gmloader/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ struct gml_config {
bool show_cursor;
bool disable_controller;
bool disable_depth;
bool disable_extensions;
bool disable_rumble;
float rumble_scale;
bool disable_texhack;
std::string force_platform;

Expand Down
12 changes: 12 additions & 0 deletions gmloader/gamepad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
30 changes: 28 additions & 2 deletions gmloader/input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -302,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];
Expand All @@ -329,6 +328,33 @@ 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 double MAX_RUMBLE_D = 65535.0;

double left = (double)yoyo_gamepads[slot].motors[0];
double right = (double)yoyo_gamepads[slot].motors[1];

// Apply 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.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_D);
uint16_t right_motor = (uint16_t)(right * MAX_RUMBLE_D);
int duration_ms = (left_motor || right_motor) ? 10000 : 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

10000 ms is 10 s
It seems a bit mucho ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GameMaker doesn't pass a duration in its arguments, instead handing control to the developer to explicitly turn rumble off. Therefore we need to pass a duration to SDL in its place, but since we don't know how long a developer may want rumble to last, I went with a larger threshold opting for safety. I think if a dev has rumble lasting longer than a few seconds they're insane, but you never know.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, thanks for the explanation. That's what I suspected :)


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]);
Expand Down
109 changes: 81 additions & 28 deletions gmloader/libyoyo.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <string>
#include <stdlib.h>

#include "platform.h"
#include "so_util.h"
Expand Down Expand Up @@ -66,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",
Expand All @@ -87,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;
Expand Down Expand Up @@ -151,29 +149,80 @@ 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)
ABI_ATTR static void window_handle(RValue *ret, void *self, void *other, int argc, RValue *args)
{
ret->kind = VALUE_REAL;
ret->rvalue.val = 0;
warning("-- Called window_handle stub ! --\n");
ret->kind = VALUE_INT64;
ret->rvalue.v64 = 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;
}
// 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);

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;
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<const char*>(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;
}
}

ABI_ATTR static void window_handle(RValue *ret, void *self, void *other, int argc, RValue *args)
ABI_ATTR static void video_open_reimpl(RValue *ret, void *self, void *other, int argc, RValue *args)
{
warning("-- Called window_handle stub ! --\n");
ret->kind = VALUE_INT64;
ret->rvalue.v64 = 0;
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<const char*>(args[0].rvalue.str->m_thing));
}
CreateAsynEventWithDSMap(ds_map, 70);
}
}

void patch_libyoyo(so_module *mod)
Expand Down Expand Up @@ -235,10 +284,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);
Expand Down Expand Up @@ -276,6 +327,8 @@ 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_");
}
Expand Down
10 changes: 9 additions & 1 deletion gmloader/libyoyo.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

#include "configuration.h"

#define MASK_KIND_RVALUE 0x0ffffff
typedef enum RValueType {
VALUE_REAL = 0,
Expand Down Expand Up @@ -63,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;

Expand Down Expand Up @@ -278,12 +281,17 @@ 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);
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();
Loading