Skip to content
Closed
Show file tree
Hide file tree
Changes from 5 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;
Comment thread
JeodC marked this conversation as resolved.
gamepad->motors[1] = right;
}

ABI_ATTR void gamepad_set_axis_deadzone(RValue *ret, void *self, void *other, int argc, RValue *args)
Expand Down
31 changes: 29 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,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;
Comment thread
JeodC marked this conversation as resolved.
Outdated
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;
Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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];
Comment thread
JeodC marked this conversation as resolved.

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