From 77b430b4fbdeb8fb2e195f0a3260b1edb06ce43f Mon Sep 17 00:00:00 2001 From: Inori Date: Wed, 31 Jul 2019 07:04:13 -0400 Subject: [PATCH] Add dynamic linking (Win32API) --- binding-mri/binding-mri.cpp | 23 +++ binding-mri/binding-util.h | 3 +- binding-mri/meson.build | 3 + binding-mri/minidl-binding.cpp | 251 +++++++++++++++++++++++++++++++++ meson.build | 2 +- meson_options.txt | 1 - src/meson.build | 2 + 7 files changed, 282 insertions(+), 3 deletions(-) create mode 100644 binding-mri/minidl-binding.cpp diff --git a/binding-mri/binding-mri.cpp b/binding-mri/binding-mri.cpp index 327f25e7..e6369c02 100644 --- a/binding-mri/binding-mri.cpp +++ b/binding-mri/binding-mri.cpp @@ -43,6 +43,7 @@ #include #include +#include extern const char module_rpg1[]; extern const char module_rpg2[]; @@ -79,6 +80,8 @@ void graphicsBindingInit(); void fileIntBindingInit(); +void MiniDLBindingInit(); + RB_METHOD(mriPrint); RB_METHOD(mriP); RB_METHOD(mkxpDataDirectory); @@ -117,6 +120,8 @@ static void mriBindingInit() fileIntBindingInit(); + MiniDLBindingInit(); + if (rgssVer >= 3) { _rb_define_module_function(rb_mKernel, "rgss_main", mriRgssMain); @@ -508,6 +513,24 @@ static void runRMXPScripts(BacktraceData &btData) fname = newStringUTF8(buf, len); btData.scriptNames.insert(buf, scriptName); +#ifdef __WIN32__ + // Quick hacky fix for getting the current window + // from Win32API FindWindowEX calls + // This will be replaced with a hook to FindWindowEX + if(!strcmp(scriptName, "Win32API")) + { + SDL_SysWMinfo wminfo = {0}; + SDL_GetWindowWMInfo(shState->sdlWindow(), &wminfo); + if (wminfo.info.win.window) + { + VALUE s = rb_str_new2("@@RGSSWINDOW="); + s = rb_str_append(s, rb_inspect(ULONG2NUM((unsigned long)wminfo.info.win.window))); + s = rb_str_append(s, rb_str_new2(";return @@RGSSWINDOW")); + string = rb_funcall(string, rb_intern("sub"), 2, rb_str_new2("raise \"Can't find RGSS player window\""), s); + } + } +#endif + int state; evalString(string, fname, &state); if (state) diff --git a/binding-mri/binding-util.h b/binding-mri/binding-util.h index 730fc78c..2a34d893 100644 --- a/binding-mri/binding-util.h +++ b/binding-mri/binding-util.h @@ -188,10 +188,11 @@ getPrivateDataCheck(VALUE self, const char *type) void *obj = Check_TypedStruct(self, &type); #else rb_check_type(self, T_DATA); + /* RMXP apparently didn't check for this? const char *ownname = rb_obj_classname(self); if (strcmp(ownname, type)) rb_raise(rb_eTypeError, "Type mismatch between %s and %s", ownname, type); - + */ void *obj = DATA_PTR(self); #endif diff --git a/binding-mri/meson.build b/binding-mri/meson.build index 2d00da5c..b3f09112 100644 --- a/binding-mri/meson.build +++ b/binding-mri/meson.build @@ -45,4 +45,7 @@ binding_source = files( 'filesystem-binding.cpp', 'windowvx-binding.cpp', 'tilemapvx-binding.cpp', + 'minidl-binding.cpp' ) + +bindings = [binding_headers, binding_source] diff --git a/binding-mri/minidl-binding.cpp b/binding-mri/minidl-binding.cpp new file mode 100644 index 00000000..39d14911 --- /dev/null +++ b/binding-mri/minidl-binding.cpp @@ -0,0 +1,251 @@ +// Most of this was taken from Ruby 1.8's Win32API.c, +// it's just as basic but should work fine for the moment + +#include + +#ifdef __WIN32__ + +#include +#define LIBHANDLE HINSTANCE +#define FUNCHANDLE HANDLE + +#else + +#include +#define LIBHANDLE void* +#define FUNCHANDLE void* + +#endif + +#define _T_VOID 0 +#define _T_NUMBER 1 +#define _T_POINTER 2 +#define _T_INTEGER 3 + +typedef void* (*MINIDL_FUNC)(...); + +static void +dl_freelib(LIBHANDLE lib) +{ +#ifdef __WIN32__ + FreeLibrary(lib); +#else + dlclose(lib); +#endif +} + +static LIBHANDLE +dl_loadlib(const char *filename) +{ + LIBHANDLE ret; +#ifdef __WIN32__ + ret = LoadLibrary(filename); +#else + ret = dlopen(filename, RTLD_NOW); +#endif + return ret; +} + +static FUNCHANDLE +dl_getfunc(LIBHANDLE lib, const char *filename) +{ + FUNCHANDLE ret; +#ifdef __WIN32__ + ret = (FUNCHANDLE)GetProcAddress(lib, filename); +#else + ret = dlsym(lib, filename); +#endif + return ret; +} + +static VALUE +MiniDL_alloc(VALUE self) +{ + return Data_Wrap_Struct(self, 0, dl_freelib, 0); +} + +static VALUE +MiniDL_initialize(VALUE self, VALUE libname, VALUE func, VALUE imports, VALUE exports) +{ + SafeStringValue(libname); + SafeStringValue(func); + + + LIBHANDLE hlib = dl_loadlib(RSTRING_PTR(libname)); + if (!hlib) + rb_raise(rb_eRuntimeError, "Failed to load library %s", RSTRING_PTR(libname)); + DATA_PTR(self) = hlib; + + FUNCHANDLE hfunc = dl_getfunc(hlib, RSTRING_PTR(func)); +#ifdef __WIN32__ + if (!hfunc) + { + VALUE func_a = rb_str_new3(func); + func_a = rb_str_cat(func_a, "A", 1); + hfunc = dl_getfunc(hlib, RSTRING_PTR(func_a)); + } +#endif + if (!hfunc) + rb_raise(rb_eRuntimeError, "Failed to find function %s within %s", RSTRING_PTR(func), RSTRING_PTR(libname)); + + + rb_iv_set(self, "_func", OFFT2NUM((unsigned long)hfunc)); + + VALUE ary_imports = rb_ary_new(); + VALUE *entry = RARRAY_PTR(imports); + switch (TYPE(imports)) + { + case T_NIL: + break; + case T_ARRAY: + for (int i = 0; i < RARRAY_LEN(imports); i++) + { + SafeStringValue(entry[i]); + switch (*(char*)RSTRING_PTR(entry[i])) + { + case 'N': case 'n': case 'L': case 'l': + rb_ary_push(ary_imports, INT2FIX(_T_NUMBER)); + break; + + case 'P': case 'p': + rb_ary_push(ary_imports, INT2FIX(_T_POINTER)); + break; + + case 'I': case 'i': + rb_ary_push(ary_imports, INT2FIX(_T_INTEGER)); + break; + } + } + break; + default: + SafeStringValue(imports); + const char *s = RSTRING_PTR(imports); + for (int i = 0; i < RSTRING_LEN(imports); i++) + { + switch (*s++) + { + case 'N': case 'n': case 'L': case 'l': + rb_ary_push(ary_imports, INT2FIX(_T_NUMBER)); + break; + + case 'P': case 'p': + rb_ary_push(ary_imports, INT2FIX(_T_POINTER)); + break; + + case 'I': case 'i': + rb_ary_push(ary_imports, INT2FIX(_T_INTEGER)); + break; + } + } + break; + } + + if (16 < RARRAY_LEN(ary_imports)) + rb_raise(rb_eRuntimeError, "too many parameters: %ld\n", RARRAY_LEN(ary_imports)); + + rb_iv_set(self, "_imports", ary_imports); + int ex; + if (NIL_P(exports)) + { + ex = _T_VOID; + } + else + { + SafeStringValue(exports); + switch(*RSTRING_PTR(exports)) + { + case 'V': case 'v': + ex = _T_VOID; + break; + + case 'N': case 'n': case 'L': case 'l': + ex = _T_NUMBER; + break; + + case 'P': case 'p': + ex = _T_POINTER; + break; + + case 'I': case 'i': + ex = _T_INTEGER; + break; + } + } + rb_iv_set(self, "_exports", INT2FIX(ex)); + return Qnil; +} + +static VALUE +MiniDL_call(int argc, VALUE *argv, VALUE self) +{ + struct { + unsigned long params[16]; + } param; +#define params param.params + VALUE func = rb_iv_get(self, "_func"); + VALUE own_imports = rb_iv_get(self, "_imports"); + VALUE own_exports = rb_iv_get(self, "_exports"); + MINIDL_FUNC ApiFunction = (MINIDL_FUNC)NUM2OFFT(func); + VALUE args; + int items = rb_scan_args(argc, argv, "0*", &args); + int nimport = RARRAY_LEN(own_imports); + if (items != nimport) + rb_raise(rb_eRuntimeError, "wrong number of parameters: expected %d, got %d", + nimport, items); + + for (int i = 0; i < nimport; i++) + { + VALUE str = rb_ary_entry(args, i); + unsigned long lParam = 0; + switch(FIX2INT(rb_ary_entry(own_imports, i))) + { + case _T_POINTER: + if (NIL_P(str)) + { + lParam = 0; + } + else if (FIXNUM_P(str)) + { + lParam = NUM2OFFT(str); + } + else + { + StringValue(str); + rb_str_modify(str); + lParam = (unsigned long)RSTRING_PTR(str); + } + break; + + case _T_NUMBER: case _T_INTEGER: default: + lParam = NUM2OFFT(rb_ary_entry(args, i)); + break; + } + params[i] = lParam; + } + + unsigned long ret = (unsigned long)ApiFunction(param); + switch (FIX2INT(own_exports)) + { + case _T_NUMBER: case _T_INTEGER: + return OFFT2NUM(ret); + + case _T_POINTER: + return rb_str_new2((char*)ret); + + case _T_VOID: default: + return OFFT2NUM(0); + } +} + +void +MiniDLBindingInit() +{ + VALUE cMiniDL = rb_define_class("MiniDL", rb_cObject); + rb_define_alloc_func(cMiniDL, MiniDL_alloc); + rb_define_method(cMiniDL, "initialize", RUBY_METHOD_FUNC(MiniDL_initialize), 4); + rb_define_method(cMiniDL, "call", RUBY_METHOD_FUNC(MiniDL_call), -1); + rb_define_alias(cMiniDL, "Call", "call"); +#ifdef __WIN32__ + rb_define_const(rb_cObject, "Win32API", cMiniDL); +#endif +} diff --git a/meson.build b/meson.build index f381148c..fc0c9f80 100644 --- a/meson.build +++ b/meson.build @@ -15,7 +15,7 @@ subdir(binding_dir) subdir('shader') subdir('assets') -all_sources = [main_headers, main_source, binding_headers, binding_source, processed_shaders, processed_assets] +all_sources = [main, bindings, processed_shaders, processed_assets] include_dirs = [include_directories('src', binding_dir)] linker_args = [] diff --git a/meson_options.txt b/meson_options.txt index f4aea8a0..2a869393 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -2,5 +2,4 @@ option('shared_fluid', type: 'boolean', value: false, description: 'Dynamically option('binding', type: 'combo', value: 'mri', choices: ['mri', 'mruby', 'null'], description: 'Binding type') option('mri_version', type: 'string', value: '2.5', description: 'Version of MRI to link with') option('workdir_current', type: 'boolean', value: false, description: 'Keep current directory on startup') - option('ruby_lib', type: 'string', value: 'ruby', description: 'Name of legacy Ruby library') \ No newline at end of file diff --git a/src/meson.build b/src/meson.build index 35c87715..73dbc40d 100644 --- a/src/meson.build +++ b/src/meson.build @@ -131,3 +131,5 @@ main_source = files( 'midisource.cpp', 'fluid-fun.cpp' ) + +main = [main_source, main_headers] \ No newline at end of file