Move fake Win32API functions out of if/else chain

This commit is contained in:
Inori 2019-08-06 15:33:54 -04:00
parent 9a3f8c98d4
commit 4a428cd3b4
4 changed files with 257 additions and 142 deletions

View file

@ -5,6 +5,7 @@
#if defined(__WIN32__) && defined(USE_ESSENTIALS_FIXES)
#include <windows.h>
#include "sharedstate.h"
#include "fake-api.h"
#endif
#include "binding-util.h"
@ -32,19 +33,38 @@ MiniFFI_alloc(VALUE self)
return Data_Wrap_Struct(self, 0, SDL_UnloadObject, 0);
}
// Probably should do something else for macOS and Linux if I get around to them,
// this would probably be a *very* long list for those
static void*
MiniFFI_GetFunctionHandle(void *lib, const char *func)
{
#if defined(__WIN32__) && defined(USE_ESSENTIALS_FIXES)
#define CAPTURE(name) if (!strcmp(#name, func)) return (void*)&MKXP_##name
CAPTURE(GetCurrentThreadId);
CAPTURE(GetWindowThreadProcessId);
CAPTURE(FindWindowEx);
CAPTURE(GetForegroundWindow);
CAPTURE(GetCursorPos);
CAPTURE(ScreenToClient);
CAPTURE(ScreenToClient);
CAPTURE(SetWindowPos);
CAPTURE(RegisterHotKey);
#endif
return SDL_LoadFunction(lib, func);
}
RB_METHOD(MiniFFI_initialize)
{
VALUE libname, func, imports, exports;
rb_scan_args(argc, argv, "22", &libname, &func, &imports, &exports);
SafeStringValue(libname);
{
VALUE libname, func, imports, exports;
rb_scan_args(argc, argv, "22", &libname, &func, &imports, &exports);
SafeStringValue(libname);
SafeStringValue(func);
void *hlib = SDL_LoadObject(RSTRING_PTR(libname));
if (!hlib)
rb_raise(rb_eRuntimeError, SDL_GetError());
rb_raise(rb_eRuntimeError, SDL_GetError());
DATA_PTR(self) = hlib;
void *hfunc = SDL_LoadFunction(hlib, RSTRING_PTR(func));
void *hfunc = MiniFFI_GetFunctionHandle(hlib, RSTRING_PTR(func));
#ifdef __WIN32__
if (!hfunc)
{
@ -54,7 +74,7 @@ RB_METHOD(MiniFFI_initialize)
}
#endif
if (!hfunc)
rb_raise(rb_eRuntimeError, SDL_GetError());
rb_raise(rb_eRuntimeError, SDL_GetError());
rb_iv_set(self, "_func", OFFT2NUM((unsigned long)hfunc));
@ -65,54 +85,54 @@ RB_METHOD(MiniFFI_initialize)
VALUE *entry;
switch (TYPE(imports))
{
case T_NIL:
case T_NIL:
break;
case T_ARRAY:
entry = RARRAY_PTR(imports);
case T_ARRAY:
entry = RARRAY_PTR(imports);
for (int i = 0; i < RARRAY_LEN(imports); i++)
{
SafeStringValue(entry[i]);
switch (*(char*)RSTRING_PTR(entry[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;
rb_ary_push(ary_imports, INT2FIX(_T_NUMBER));
break;
case 'P': case 'p':
rb_ary_push(ary_imports, INT2FIX(_T_POINTER));
break;
rb_ary_push(ary_imports, INT2FIX(_T_POINTER));
break;
case 'I': case 'i':
rb_ary_push(ary_imports, INT2FIX(_T_INTEGER));
break;
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++)
{
switch (*s++)
{
case 'N': case 'n': case 'L': case 'l':
rb_ary_push(ary_imports, INT2FIX(_T_NUMBER));
break;
rb_ary_push(ary_imports, INT2FIX(_T_NUMBER));
break;
case 'P': case 'p':
rb_ary_push(ary_imports, INT2FIX(_T_POINTER));
break;
rb_ary_push(ary_imports, INT2FIX(_T_POINTER));
break;
case 'I': case 'i':
rb_ary_push(ary_imports, INT2FIX(_T_INTEGER));
break;
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_raise(rb_eRuntimeError, "too many parameters: %ld\n", RARRAY_LEN(ary_imports));
rb_iv_set(self, "_imports", ary_imports);
int ex;
@ -125,19 +145,19 @@ RB_METHOD(MiniFFI_initialize)
SafeStringValue(exports);
switch(*RSTRING_PTR(exports))
{
case 'V': case 'v':
case 'V': case 'v':
ex = _T_VOID;
break;
case 'N': case 'n': case 'L': case 'l':
case 'N': case 'n': case 'L': case 'l':
ex = _T_NUMBER;
break;
case 'P': case 'p':
case 'P': case 'p':
ex = _T_POINTER;
break;
case 'I': case 'i':
case 'I': case 'i':
ex = _T_INTEGER;
break;
}
@ -161,8 +181,8 @@ RB_METHOD(MiniFFI_call)
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);
rb_raise(rb_eRuntimeError, "wrong number of parameters: expected %d, got %d",
nimport, items);
for (int i = 0; i < nimport; i++)
{
@ -170,123 +190,41 @@ RB_METHOD(MiniFFI_call)
unsigned long lParam = 0;
switch(FIX2INT(rb_ary_entry(own_imports, i)))
{
case _T_POINTER:
case _T_POINTER:
if (NIL_P(str))
{
lParam = 0;
}
{
lParam = 0;
}
else if (FIXNUM_P(str))
{
lParam = NUM2OFFT(str);
}
{
lParam = NUM2OFFT(str);
}
else
{
StringValue(str);
rb_str_modify(str);
lParam = (unsigned long)RSTRING_PTR(str);
}
{
StringValue(str);
rb_str_modify(str);
lParam = (unsigned long)RSTRING_PTR(str);
}
break;
case _T_NUMBER: case _T_INTEGER: default:
case _T_NUMBER: case _T_INTEGER: default:
lParam = NUM2OFFT(rb_ary_entry(args, i));
break;
}
params[i] = lParam;
}
unsigned long ret;
#if defined(__WIN32__) && defined(USE_ESSENTIALS_FIXES)
// On Windows, if essentials fixes are enabled, function calls that
// do not work with MKXP will be intercepted here so that the code
// still has its desired effect
// TODO: Move these to actual functions and just redirect the
// function pointers during the initialization stage
// so that we're not going through ugly if statements
// a million times a second in Essentials
unsigned long ret = (unsigned long)ApiFunction(param);
// GetCurrentThreadId, GetWindowThreadProcessId, FindWindowEx,
// and GetForegroundWindow are used for determining whether to
// handle input and for positioning
// It's a super janky system, but I must abide by it
char *fname = RSTRING_PTR(rb_iv_get(self, "_funcname"));
#define func_is(x) !strcmp(fname, x)
#define if_func_is(x) if (func_is(x))
if (func_is("GetCurrentThreadId") || func_is("GetWindowThreadProcessId"))
{
ret = 571; // Dummy
}
else if_func_is("FindWindowEx")
{
ret = 571;
}
else if_func_is("GetForegroundWindow")
{
if (SDL_GetWindowFlags(shState->sdlWindow()) & SDL_WINDOW_INPUT_FOCUS)
{
ret = 571;
}
else
ret = 0;
}
// Mouse support
// FIXME: It worked before but I've broken it somehow,
// but on the plus side everything else works
else if_func_is("GetCursorPos")
{
int *output = (int*)params[0];
int x, y;
SDL_GetGlobalMouseState(&x, &y);
output[0] = x;
output[1] = y;
ret = true;
}
else if_func_is("ScreenToClient")
{
int *output = (int*)params[1];
int x, y;
SDL_GetMouseState(&x, &y);
output[0] = x;
output[1] = y;
ret = true;
}
// Window stuff
else if_func_is("SetWindowPos")
{
// Win32API.restoreScreen calls SetWindowPos with +6 to width
// and +29 to height, I should be fine if I account for this
// I think
SDL_SetWindowSize(shState->sdlWindow(),params[4]-6,params[5]-29);
SDL_SetWindowPosition(shState->sdlWindow(),params[2],params[3]);
return true;
}
else if_func_is("RegisterHotKey") // Don't disable SDL's fullscreen, it works better than SpriteResizer ever did
{
ret = 0;
}
else
{
ret = (unsigned long)ApiFunction(param);
}
#else
ret = (unsigned long)ApiFunction(param);
#endif
switch (FIX2INT(own_exports))
{
case _T_NUMBER: case _T_INTEGER:
case _T_NUMBER: case _T_INTEGER:
return OFFT2NUM(ret);
case _T_POINTER:
case _T_POINTER:
return rb_str_new2((char*)ret);
case _T_VOID: default:
case _T_VOID: default:
return OFFT2NUM(0);
}
}

107
src/fake-api.cpp Normal file
View file

@ -0,0 +1,107 @@
#ifdef __WIN32__
#include <SDL.h>
#include <windows.h>
#include "sharedstate.h"
#include "fake-api.h"
// Essentials, without edits, needs Win32API. Two problems with that:
// 1. Not all Win32API functions work here, and
// 2. It uses Windows libraries.
// So, even on Windows, we need to switch out some of those functions
// with our own in order to trick the game into doing what we want.
// On Windows, we just have to worry about functions that involve
// thread IDs and window handles mostly. On anything else, every
// function that an Essentials game uses has to be impersonated.
// If you're lucky enough to be using a crossplatform library, you
// could just load that since MiniFFI should work anywhere, but
// every single system or windows-specific function you use has
// to be intercepted.
// This would also apply if you were trying to load a macOS .dylib
// from Windows or a linux .so from macOS. It's just not gonna work,
// chief.
// There's probably not much reason to change this outside of
// improving compatibility with games in general, since if you just
// want to fix one specific game and you're smart enough to add to
// this then you're also smart enough to either use MiniFFI on a
// library made for your platform or, better yet, just write the
// functionality into MKXP and don't use MiniFFI/Win32API at all.
// No macOS/Linux support yet.
DWORD __stdcall
MKXP_GetCurrentThreadId(void)
NOP_VAL(DUMMY_VAL)
DWORD __stdcall
MKXP_GetWindowThreadProcessId(HWND hWnd, LPDWORD lpdwProcessId)
NOP_VAL(DUMMY_VAL)
DWORD __stdcall
MKXP_FindWindowEx(HWND hWnd,
HWND hWndChildAfter,
LPCSTR lpszClass,
LPCSTR lpszWindow
)
NOP_VAL(DUMMY_VAL)
DWORD __stdcall
MKXP_GetForegroundWindow(void)
{
if (SDL_GetWindowFlags(shState->sdlWindow()) & SDL_WINDOW_INPUT_FOCUS)
{
return DUMMY_VAL;
}
return 0;
}
// FIXME: Mouse stuff doesn't work right now,
// but at least it's not causing any Ruby exceptions
BOOL __stdcall
MKXP_GetCursorPos(LPPOINT lpPoint)
{
SDL_GetGlobalMouseState((int*)&lpPoint->x, (int*)&lpPoint->y);
return true;
}
BOOL __stdcall
MKXP_ScreenToClient(HWND hWnd, LPPOINT lpPoint)
{
SDL_GetMouseState((int*)&lpPoint->x, (int*)&lpPoint->y);
return true;
}
// TODO: Try and get this, and the other hWnd stuff,
// to just run the real functions with the right window
// handle
BOOL __stdcall
MKXP_SetWindowPos(HWND hWnd,
HWND hWndInsertAfter,
int X,
int Y,
int cx,
int cy,
UINT uFlags)
{
SDL_SetWindowPosition(shState->sdlWindow(), X, Y);
SDL_SetWindowSize(shState->sdlWindow(), cx-6, cy-29);
return true;
}
BOOL __stdcall
MKXP_RegisterHotKey(HWND hWnd,
int id,
UINT fsModifiers,
UINT vk)
NOP_VAL(true)
#endif

68
src/fake-api.h Normal file
View file

@ -0,0 +1,68 @@
#pragma once
#ifdef __WIN32__
#include <windows.h>
/*
#ifndef __WIN32__
typedef unsigned long HWND;
typedef unsigned int DWORD;
typedef unsigned int* LPDWORD;
typedef char* LPCSTR;
typedef int LONG;
typedef bool BOOL;
typedef unsigned int UINT;
typedef struct {
LONG x;
LONG y;
} POINT;
typedef POINT* LPPOINT;
#endif
*/
#define DUMMY_VAL 571
#define NOP \
{ \
return; \
}
#define NOP_VAL(x) \
{ \
return x; \
}
DWORD __stdcall
MKXP_GetCurrentThreadId(void);
DWORD __stdcall
MKXP_GetWindowThreadProcessId(HWND hWnd, LPDWORD lpdwProcessId);
DWORD __stdcall
MKXP_FindWindowEx(HWND hWnd,
HWND hWndChildAfter,
LPCSTR lpszClass,
LPCSTR lpszWindow
);
DWORD __stdcall
MKXP_GetForegroundWindow(void);
BOOL __stdcall
MKXP_GetCursorPos(LPPOINT lpPoint);
BOOL __stdcall
MKXP_ScreenToClient(HWND hWnd, LPPOINT lpPoint);
BOOL __stdcall
MKXP_SetWindowPos(HWND hWnd,
HWND hWndInsertAfter,
int X,
int Y,
int cx,
int cy,
UINT uFlags);
BOOL __stdcall
MKXP_RegisterHotKey(HWND hWnd,
int id,
UINT fsModifiers,
UINT vk);
#endif

View file

@ -86,7 +86,8 @@ main_headers = files(
'tileatlasvx.h',
'sharedmidistate.h',
'fluid-fun.h',
'sdl-util.h'
'sdl-util.h',
'fake-api.h'
)
main_source = files(
@ -133,7 +134,8 @@ main_source = files(
'tileatlasvx.cpp',
'autotilesvx.cpp',
'midisource.cpp',
'fluid-fun.cpp'
'fluid-fun.cpp',
'fake-api.cpp'
)
main = [main_source, main_headers]