mirror of
https://github.com/mkxp-z/mkxp-z.git
synced 2025-09-10 12:02:53 +02:00
Move fake Win32API functions out of if/else chain
This commit is contained in:
parent
9a3f8c98d4
commit
4a428cd3b4
4 changed files with 257 additions and 142 deletions
|
@ -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
|
||||
unsigned long ret = (unsigned long)ApiFunction(param);
|
||||
|
||||
// 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
|
||||
|
||||
// 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
107
src/fake-api.cpp
Normal 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
68
src/fake-api.h
Normal 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
|
|
@ -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]
|
||||
|
|
Loading…
Add table
Reference in a new issue