diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ac.c | 16 | ||||
-rw-r--r-- | src/accessor.h | 38 | ||||
-rw-r--r-- | src/autojump.c | 29 | ||||
-rw-r--r-- | src/clientcon.c | 3 | ||||
-rw-r--r-- | src/con_.c | 54 | ||||
-rw-r--r-- | src/con_.h | 12 | ||||
-rw-r--r-- | src/democustom.c | 7 | ||||
-rw-r--r-- | src/demorec.c | 21 | ||||
-rw-r--r-- | src/demorec.h | 5 | ||||
-rw-r--r-- | src/engineapi.c | 11 | ||||
-rw-r--r-- | src/engineapi.h | 24 | ||||
-rw-r--r-- | src/ent.c | 7 | ||||
-rw-r--r-- | src/extmalloc.c | 8 | ||||
-rw-r--r-- | src/gameinfo.c | 2 | ||||
-rw-r--r-- | src/gameserver.c | 5 | ||||
-rw-r--r-- | src/hud.c | 79 | ||||
-rw-r--r-- | src/inputhud.c | 37 | ||||
-rw-r--r-- | src/kvsys.c | 26 | ||||
-rw-r--r-- | src/l4d1democompat.c | 5 | ||||
-rw-r--r-- | src/l4daddon.c | 5 | ||||
-rw-r--r-- | src/l4dmm.c | 20 | ||||
-rw-r--r-- | src/l4dreset.c | 29 | ||||
-rw-r--r-- | src/l4dwarp.c | 27 | ||||
-rw-r--r-- | src/sst.c | 12 | ||||
-rw-r--r-- | src/trace.c | 8 | ||||
-rw-r--r-- | src/vcall.h | 31 | ||||
-rw-r--r-- | src/xhair.c | 2 |
27 files changed, 267 insertions, 256 deletions
@@ -281,8 +281,11 @@ struct inputevent { int data, data2, data3; }; -DECL_VFUNC_DYN(void, GetDesktopResolution, int *, int *) -DECL_VFUNC_DYN(void, DispatchAllStoredGameMessages) +struct IGameUIFuncs { void **vtable; }; +struct IGame { void **vtable; }; + +DECL_VFUNC_DYN(struct IGameUIFuncs, void, GetDesktopResolution, int *, int *) +DECL_VFUNC_DYN(struct IGame, void, DispatchAllStoredGameMessages) typedef void (*Key_Event_func)(struct inputevent *); static Key_Event_func orig_Key_Event; @@ -314,13 +317,14 @@ static bool find_Key_Event() { // -> IGame/CGame (first mov into ECX) // -> CGame::DispatchAllStoredGameMessages vfunc // -> First call instruction (either DispatchInputEvent or Key_Event) - void *gameuifuncs = factory_engine("VENGINE_GAMEUIFUNCS_VERSION005", 0); + struct IGameUIFuncs *gameuifuncs = factory_engine( + "VENGINE_GAMEUIFUNCS_VERSION005", 0); if_cold (!gameuifuncs) { errmsg_errorx("couldn't get engine game UI interface"); return false; } - void *cgame; - const uchar *insns = (const uchar *)VFUNC(gameuifuncs, GetDesktopResolution); + struct IGame *cgame; + const uchar *insns = gameuifuncs->vtable[vtidx_GetDesktopResolution]; for (const uchar *p = insns; p - insns < 16;) { if (p[0] == X86_MOVRMW && p[1] == X86_MODRM(0, 1, 5)) { void **indirect = mem_loadptr(p + 2); @@ -332,7 +336,7 @@ static bool find_Key_Event() { errmsg_errorx("couldn't find pointer to CGame instance"); return false; -ok: insns = (const uchar *)VFUNC(cgame, DispatchAllStoredGameMessages); +ok: insns = cgame->vtable[vtidx_DispatchAllStoredGameMessages]; for (const uchar *p = insns; p - insns < 128;) { if (p[0] == X86_CALL) { orig_Key_Event = (Key_Event_func)(p + 5 + mem_loads32(p + 1)); diff --git a/src/accessor.h b/src/accessor.h index dcd9f28..ec63b3c 100644 --- a/src/accessor.h +++ b/src/accessor.h @@ -27,15 +27,16 @@ #endif /* - * Defines a function to offset a pointer from a struct/class to a field based + * Defines a function to offset a pointer from a struct/class to a member based * on a corresponding offset value off_<field>. Such an offset would be * generally defined in gamedata. The function will be named getptr_<field>. * Essentially allows easy access to an opaque thing contained with another * opaque thing. */ -#define DEF_PTR_ACCESSOR(type, field) \ - _ACCESSOR_UNUSED static inline typeof(type) *getptr_##field(void *obj) { \ - return mem_offset(obj, off_##field); \ +#define DEF_PTR_ACCESSOR(class, type, member) \ + _ACCESSOR_UNUSED static inline typeof(type) *getptr_##member( \ + typeof(class) *obj) { \ + return mem_offset(obj, off_##member); \ } /* @@ -43,21 +44,22 @@ * Requires that the field type is complete - that is, either scalar or a fully * defined struct. */ -#define DEF_ACCESSORS(type, field) \ - DEF_PTR_ACCESSOR(type, field) \ - _ACCESSOR_UNUSED static inline typeof(type) get_##field(const void *obj) { \ - return *getptr_##field((void *)obj); \ +#define DEF_ACCESSORS(class, type, member) \ + DEF_PTR_ACCESSOR(class, type, member) \ + _ACCESSOR_UNUSED static inline typeof(type) get_##member( \ + const typeof(class) *obj) { \ + return *getptr_##member((typeof(class) *)obj); \ } \ - _ACCESSOR_UNUSED static inline void set_##field(const void *obj, \ + _ACCESSOR_UNUSED static inline void set_##member(typeof(class) *obj, \ typeof(type) val) { \ - *getptr_##field((void *)obj) = val; \ + *getptr_##member(obj) = val; \ } /* - * Defines an array indexing function arrayidx_<class> which allows offsetting - * an opaque pointer by sz_<class> bytes. This size value would generally be - * defined in gamedata. Allows iterating over structs/classes with sizes that - * vary by game and are thus unknown at compile time. + * Defines an array indexing function arrayidx_<classname> which allows + * offsetting an opaque pointer by sz_<classname> bytes. This size value would + * generally be defined in gamedata. Allows iterating over structs/classes with + * sizes that vary by game and are thus unknown at compile time. * * Note that idx is signed so this can also be used for relative pointer offsets * in either direction. @@ -68,10 +70,10 @@ * single load of the global followed by repeated addition with no need for * multiplication, given that we use LTO, so... don't worry about it! It's fine! */ -#define DEF_ARRAYIDX_ACCESSOR(class) \ - _ACCESSOR_UNUSED static inline struct class *arrayidx_##class(void *array, \ - ssize idx) { \ - return mem_offset(array, idx * sz_##class); \ +#define DEF_ARRAYIDX_ACCESSOR(type, classname) \ + _ACCESSOR_UNUSED static inline typeof(type) *arrayidx_##classname( \ + typeof(type) *array, ssize idx) { \ + return mem_offset(array, idx * sz_##classname); \ } #endif diff --git a/src/autojump.c b/src/autojump.c index a8a21ca..6d0fbda 100644 --- a/src/autojump.c +++ b/src/autojump.c @@ -24,7 +24,6 @@ #include "hook.h" #include "intdefs.h" #include "langext.h" -#include "mem.h" #include "os.h" #include "vcall.h" @@ -33,20 +32,21 @@ REQUIRE_GAMEDATA(off_mv) REQUIRE_GAMEDATA(vtidx_CheckJumpButton) REQUIRE_GLOBAL(factory_client) // note: server will never be null -DEF_ACCESSORS(struct CMoveData *, mv) +struct CGameMovement { void **vtable; }; +DEF_ACCESSORS(struct CGameMovement, struct CMoveData *, mv) -DEF_FEAT_CVAR(sst_autojump, "Jump upon hitting the ground while holding space", 0, - CON_REPLICATE | CON_DEMO) +DEF_FEAT_CVAR(sst_autojump, "Jump upon hitting the ground while holding space", + 0, CON_REPLICATE | CON_DEMO) #define NIDX 256 // *completely* arbitrary lol static bool justjumped[NIDX] = {0}; static inline int handleidx(ulong h) { return h & (1 << 11) - 1; } -static void *gmsv = 0, *gmcl = 0; -typedef bool (*VCALLCONV CheckJumpButton_func)(void *); +static struct CGameMovement *gmsv = 0, *gmcl = 0; +typedef bool (*VCALLCONV CheckJumpButton_func)(struct CGameMovement *); static CheckJumpButton_func origsv, origcl; -static bool VCALLCONV hooksv(void *this) { +static bool VCALLCONV hooksv(struct CGameMovement *this) { struct CMoveData *mv = get_mv(this); int idx = handleidx(mv->playerhandle); if (con_getvari(sst_autojump) && mv->firstrun && !justjumped[idx]) { @@ -57,7 +57,7 @@ static bool VCALLCONV hooksv(void *this) { return ret; } -static bool VCALLCONV hookcl(void *this) { +static bool VCALLCONV hookcl(struct CGameMovement *this) { struct CMoveData *mv = get_mv(this); // FIXME: this will stutter in the rare case where justjumped is true. // currently doing clientside justjumped handling makes multiplayer @@ -68,9 +68,8 @@ static bool VCALLCONV hookcl(void *this) { return justjumped[0] = origcl(this); } -static bool unprot(void *gm) { - void **vtable = mem_loadptr(gm); - bool ret = os_mprot(vtable + vtidx_CheckJumpButton, sizeof(void *), +static bool unprot(struct CGameMovement *gm) { + bool ret = os_mprot(gm->vtable + vtidx_CheckJumpButton, sizeof(void *), PAGE_READWRITE); if (!ret) errmsg_errorsys("couldn't make virtual table writable"); return ret; @@ -99,9 +98,9 @@ INIT { return FEAT_FAIL; } if_cold (!unprot(gmcl)) return FEAT_FAIL; - origsv = (CheckJumpButton_func)hook_vtable(*(void ***)gmsv, + origsv = (CheckJumpButton_func)hook_vtable(gmsv->vtable, vtidx_CheckJumpButton, (void *)&hooksv); - origcl = (CheckJumpButton_func)hook_vtable(*(void ***)gmcl, + origcl = (CheckJumpButton_func)hook_vtable(gmcl->vtable, vtidx_CheckJumpButton, (void *)&hookcl); if (GAMETYPE_MATCHES(Portal1)) { @@ -119,8 +118,8 @@ INIT { } END { - unhook_vtable(*(void ***)gmsv, vtidx_CheckJumpButton, (void *)origsv); - unhook_vtable(*(void ***)gmcl, vtidx_CheckJumpButton, (void *)origcl); + unhook_vtable(gmsv->vtable, vtidx_CheckJumpButton, (void *)origsv); + unhook_vtable(gmcl->vtable, vtidx_CheckJumpButton, (void *)origcl); } // vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/clientcon.c b/src/clientcon.c index 78c8957..767a4cd 100644 --- a/src/clientcon.c +++ b/src/clientcon.c @@ -25,7 +25,8 @@ REQUIRE(ent) REQUIRE_GAMEDATA(vtidx_ClientPrintf) REQUIRE_GLOBAL(engserver) -DECL_VFUNC_DYN(void, ClientPrintf, struct edict *, const char *) +DECL_VFUNC_DYN(struct VEngineServer, void, ClientPrintf, struct edict *, + const char *) void clientcon_msg(struct edict *e, const char *s) { ClientPrintf(engserver, e, s); @@ -41,27 +41,27 @@ static int dllid; // from AllocateDLLIdentifier(), lets us unregister in bulk int con_cmdclient; -DECL_VFUNC(void *, FindCommandBase_p2, 13, const char *) -DECL_VFUNC(void *, FindCommand_nonp2, 14, const char *) -DECL_VFUNC(void *, FindVar_nonp2, 12, const char *) - -DECL_VFUNC_DYN(int, AllocateDLLIdentifier) -DECL_VFUNC_DYN(void, RegisterConCommand, /*ConCommandBase*/ void *) -DECL_VFUNC_DYN(void, UnregisterConCommands, int) -DECL_VFUNC_DYN(struct con_var *, FindVar, const char *) -// DECL_VFUNC(const struct con_var *, FindVar_const, 13, const char *) -DECL_VFUNC_DYN(struct con_cmd *, FindCommand, const char *) -DECL_VFUNC_DYN(void, CallGlobalChangeCallbacks, struct con_var *, const char *, - float) +DECL_VFUNC(struct ICvar, void *, FindCommandBase_p2, 13, const char *) +DECL_VFUNC(struct ICvar, void *, FindCommand_nonp2, 14, const char *) +DECL_VFUNC(struct ICvar, void *, FindVar_nonp2, 12, const char *) + +DECL_VFUNC_DYN(struct ICvar, int, AllocateDLLIdentifier) +DECL_VFUNC_DYN(struct ICvar, void, RegisterConCommand, /*ConCommandBase*/ void *) +DECL_VFUNC_DYN(struct ICvar, void, UnregisterConCommands, int) +DECL_VFUNC_DYN(struct ICvar, struct con_var *, FindVar, const char *) +//DECL_VFUNC(struct ICvar, const struct con_var *, FindVar_const, 13, const char *) +DECL_VFUNC_DYN(struct ICvar, struct con_cmd *, FindCommand, const char *) +DECL_VFUNC_DYN(struct ICvar, void, CallGlobalChangeCallbacks, struct con_var *, + const char *, float) // sad: since adding the cool abstraction, we can't do varargs (because you // can't pass varargs to other varargs of course). we only get a pointer to it // via VFUNC so just declare the typedef here - I don't wanna write any more // macros today. -typedef void (*ConsoleColorPrintf_func)(void *, const struct rgba *, +typedef void (*ConsoleColorPrintf_func)(struct ICvar *, const struct rgba *, const char *, ...); // these have to be extern for con_colourmsg(), due to varargs nonsense -void *_con_iface; +struct ICvar *_con_iface; ConsoleColorPrintf_func _con_colourmsgf; static inline void initval(struct con_var *v) { @@ -136,12 +136,12 @@ static bool VCALLCONV ClampValue(struct con_var *this, float *f) { return false; } -int VCALLCONV AutoCompleteSuggest(void *this, const char *partial, +int VCALLCONV AutoCompleteSuggest(struct con_cmd *this, const char *partial, /*CUtlVector*/ void *commands) { // TODO(autocomplete): implement this if needed later return 0; } -bool VCALLCONV CanAutoComplete(void *this) { +bool VCALLCONV CanAutoComplete(struct con_cmd *this) { return false; } void VCALLCONV Dispatch(struct con_cmd *this, const struct con_cmdargs *args) { @@ -207,34 +207,34 @@ static void VCALLCONV InternalSetIntValue_impl(struct con_var *this, int v) { } } -DECL_VFUNC_DYN(void, InternalSetValue, const char *) -DECL_VFUNC_DYN(void, InternalSetFloatValue, float) -DECL_VFUNC_DYN(void, InternalSetIntValue, int) -DECL_VFUNC_DYN(void, InternalSetColorValue, struct rgba) +DECL_VFUNC_DYN(struct con_var, void, InternalSetValue, const char *) +DECL_VFUNC_DYN(struct con_var, void, InternalSetFloatValue, float) +DECL_VFUNC_DYN(struct con_var, void, InternalSetIntValue, int) +DECL_VFUNC_DYN(struct con_var, void, InternalSetColorValue, struct rgba) // IConVar calls get this-adjusted pointers, so just subtract the offset static void VCALLCONV SetValue_str_thunk(void *thisoff, const char *v) { struct con_var *this = mem_offset(thisoff, -offsetof(struct con_var, vtable_iconvar)); - InternalSetValue(&this->parent->base, v); + InternalSetValue(this->parent, v); } static void VCALLCONV SetValue_f_thunk(void *thisoff, float v) { struct con_var *this = mem_offset(thisoff, -offsetof(struct con_var, vtable_iconvar)); - InternalSetFloatValue(&this->parent->base, v); + InternalSetFloatValue(this->parent, v); } static void VCALLCONV SetValue_i_thunk(void *thisoff, int v) { struct con_var *this = mem_offset(thisoff, -offsetof(struct con_var, vtable_iconvar)); - InternalSetIntValue(&this->parent->base, v); + InternalSetIntValue(this->parent, v); } static void VCALLCONV SetValue_colour_thunk(void *thisoff, struct rgba v) { struct con_var *this = mem_offset(thisoff, -offsetof(struct con_var, vtable_iconvar)); - InternalSetColorValue(&this->parent->base, v); + InternalSetColorValue(this->parent, v); } -// more misc thunks, hopefully these just compile to a sub and a jmp +// more misc thunks, hopefully these just compile to a lea and a jmp static const char *VCALLCONV GetName_thunk(void *thisoff) { struct con_var *this = mem_offset(thisoff, -offsetof(struct con_var, vtable_iconvar)); @@ -247,7 +247,9 @@ static bool VCALLCONV IsFlagSet_thunk(void *thisoff, int flags) { } // dunno what this is actually for... -static int VCALLCONV GetSplitScreenPlayerSlot(void *thisoff) { return 0; } +static int VCALLCONV GetSplitScreenPlayerSlot(struct con_var *thisoff) { + return 0; +} // aand yet another Create nop static void VCALLCONV Create_var(void *thisoff, const char *name, @@ -180,9 +180,10 @@ void con_warn(const char *fmt, ...) _CON_PRINTF(1, 2) __asm__("Warning"); #endif struct rgba; // in engineapi.h - forward declare here to avoid warnings +struct ICvar; // " -extern void *_con_iface; -extern void (*_con_colourmsgf)(void *this, const struct rgba *c, +extern struct ICvar *_con_iface; +extern void (*_con_colourmsgf)(struct ICvar *this, const struct rgba *c, const char *fmt, ...) _CON_PRINTF(3, 4); /* * This provides the same functionality as ConColorMsg which was removed from @@ -233,12 +234,7 @@ extern struct _con_vtab_iconvar_wrap { .parent = &_cvar_##name_, /* bizarre, but how the engine does it */ \ .defaultval = _Generic(value, char *: value, int: #value, \ double: #value), \ - /* N.B. the NOLINT comment below isn't for you, the reader, it's for the - computer, because clangd decided the only way to turn off a bogus - warning is to write a bogus comment. Also note, this comment you're - reading now isn't very useful either, I'm just angry. */ \ - .strlen = _Generic(value, char *: sizeof(value), /*NOLINT*/ \ - default: sizeof(#value)), \ + .strlen = sizeof(_Generic(value, char *: value, default: #value)), \ .fval = _Generic(value, char *: 0, int: value, double: value), \ .ival = _Generic(value, char *: 0, int: value, double: (int)value), \ .hasmin = hasmin_, .minval = (min), .hasmax = hasmax_, .maxval = (max) \ diff --git a/src/democustom.c b/src/democustom.c index 7ca8677..cae58a8 100644 --- a/src/democustom.c +++ b/src/democustom.c @@ -17,16 +17,13 @@ #include <string.h> #include "bitbuf.h" -#include "con_.h" #include "demorec.h" #include "engineapi.h" -#include "errmsg.h" #include "feature.h" #include "gamedata.h" #include "intdefs.h" #include "langext.h" #include "mem.h" -#include "ppmagic.h" #include "vcall.h" #include "x86.h" #include "x86util.h" @@ -88,7 +85,7 @@ void democustom_write(const void *buf, int len) { } static bool find_WriteMessages() { - const uchar *insns = (*(uchar ***)demorecorder)[vtidx_RecordPacket]; + const uchar *insns = (uchar *)demorecorder->vtable[vtidx_RecordPacket]; // RecordPacket calls WriteMessages right away, so just look for a call for (const uchar *p = insns; p - insns < 32;) { if (*p == X86_CALL) { @@ -100,7 +97,7 @@ static bool find_WriteMessages() { return false; } -DECL_VFUNC_DYN(int, GetEngineBuildNumber) +DECL_VFUNC_DYN(struct VEngineClient, int, GetEngineBuildNumber) INIT { // More UncraftedkNowledge: diff --git a/src/demorec.c b/src/demorec.c index 5aacf06..7bdc517 100644 --- a/src/demorec.c +++ b/src/demorec.c @@ -18,6 +18,7 @@ #include <string.h> #include "con_.h" +#include "demorec.h" #include "engineapi.h" #include "errmsg.h" #include "event.h" @@ -29,7 +30,6 @@ #include "langext.h" #include "mem.h" #include "os.h" -#include "ppmagic.h" #include "sst.h" #include "vcall.h" #include "x86.h" @@ -44,7 +44,7 @@ REQUIRE_GAMEDATA(vtidx_RecordPacket) DEF_FEAT_CVAR(sst_autorecord, "Continuously record demos even after reconnecting", 1, CON_ARCHIVE) -void *demorecorder; +struct CDemoRecorder *demorecorder; static int *demonum; static bool *recording; const char *demorec_basename; @@ -59,12 +59,11 @@ DEF_PREDICATE(DemoControlAllowed) DEF_EVENT(DemoRecordStarting) DEF_EVENT(DemoRecordStopped, int) -DECL_VCALL_DYN(SetSignonState, int) +struct CDemoRecorder; -//typedef void (*VCALLCONV SetSignonState_func)(void *, int); +typedef void (*VCALLCONV SetSignonState_func)(struct CDemoRecorder *, int); static SetSignonState_func orig_SetSignonState; -static void VCALLCONV hook_SetSignonState(void *this_, int state) { - struct CDemoRecorder *this = this_; +static void VCALLCONV hook_SetSignonState(struct CDemoRecorder *this, int state) { // NEW fires once every map or save load, but only bumps number if demo file // was left open (i.e. every transition). bump it unconditionally instead! if (state == SIGNONSTATE_NEW) { @@ -81,9 +80,9 @@ static void VCALLCONV hook_SetSignonState(void *this_, int state) { orig_SetSignonState(this, state); } -typedef void (*VCALLCONV StopRecording_func)(void *); +typedef void (*VCALLCONV StopRecording_func)(struct CDemoRecorder *); static StopRecording_func orig_StopRecording; -static void VCALLCONV hook_StopRecording(void *this) { +static void VCALLCONV hook_StopRecording(struct CDemoRecorder *this) { bool wasrecording = *recording; int lastnum = *demonum; orig_StopRecording(this); @@ -99,7 +98,7 @@ static void VCALLCONV hook_StopRecording(void *this) { } } -DECL_VFUNC_DYN(void, StartRecording) +DECL_VFUNC_DYN(struct CDemoRecorder, void, StartRecording) static struct con_cmd *cmd_record, *cmd_stop; static con_cmdcb orig_record_cb, orig_stop_cb; @@ -268,7 +267,7 @@ INIT { errmsg_errorx("couldn't find demo recorder instance"); return FEAT_INCOMPAT; } - void **vtable = mem_loadptr(demorecorder); + void **vtable = demorecorder->vtable; // XXX: 16 is totally arbitrary here! figure out proper bounds later if_cold (!os_mprot(vtable, 16 * sizeof(void *), PAGE_READWRITE)) { errmsg_errorsys("couldn't make virtual table writable"); @@ -298,7 +297,7 @@ END { if_hot (!sst_userunloaded) return; // avoid dumb edge case if someone somehow records and immediately unloads if (*recording && *demonum == 0) *demonum = 1; - void **vtable = mem_loadptr(demorecorder); + void **vtable = demorecorder->vtable; unhook_vtable(vtable, vtidx_SetSignonState, (void *)orig_SetSignonState); unhook_vtable(vtable, vtidx_StopRecording, (void *)orig_StopRecording); cmd_record->cb = orig_record_cb; diff --git a/src/demorec.h b/src/demorec.h index 63009a0..4e33cc5 100644 --- a/src/demorec.h +++ b/src/demorec.h @@ -20,8 +20,9 @@ #include "event.h" -// For internal use by democustom -extern void *demorecorder; +// For internal use by democustom. Consider this opaque. +// XXX: should the struct be put in engineapi or something? +extern struct CDemoRecorder { void **vtable; } *demorecorder; /* * Whether to ignore the value of the sst_autorecord cvar and just keep diff --git a/src/engineapi.c b/src/engineapi.c index 54a6d8f..44713da 100644 --- a/src/engineapi.c +++ b/src/engineapi.c @@ -40,15 +40,16 @@ ifacefactory factory_client = 0, factory_server = 0, factory_engine = 0, struct VEngineClient *engclient; struct VEngineServer *engserver; -void *srvdll; +struct IServerGameDLL *srvdll; -DECL_VFUNC(void *, GetGlobalVars, 1) // seems to be very stable, thank goodness -void *globalvars; +DECL_VFUNC(void, struct CGlobalVars *, GetGlobalVars, 1) // seems very stable +struct CGlobalVars *globalvars; -void *inputsystem, *vgui; +struct IInputSystem *inputsystem; +struct CEngineVGui *vgui; struct CServerPlugin *pluginhandler; -DECL_VFUNC_DYN(void *, GetAllServerClasses) +DECL_VFUNC_DYN(struct IServerGameDLL, struct ServerClass *, GetAllServerClasses) #include <entpropsinit.gen.h> // generated by build/mkentprops.c #include <gamedatainit.gen.h> // generated by build/mkgamedata.c diff --git a/src/engineapi.h b/src/engineapi.h index c7a7e1f..ef716a9 100644 --- a/src/engineapi.h +++ b/src/engineapi.h @@ -38,15 +38,14 @@ extern ifacefactory factory_client, factory_server, factory_engine, // various engine types {{{ -struct VEngineClient { - void **vtable; - /* opaque fields */ -}; - -struct VEngineServer { - void **vtable; - /* opaque fields */ -}; +// Virtual classes with opaque members; vtables exposed for ease of hooking etc. +struct ICvar { void **vtable; }; +struct VEngineClient { void **vtable; }; +struct VClient { void **vtable; }; +struct VEngineServer { void **vtable; }; +struct IServerGameDLL { void **vtable; }; +struct IInputSystem { void **vtable; }; +struct CEngineVGui { void **vtable; }; struct CUtlMemory { void *mem; @@ -124,9 +123,10 @@ struct ServerClass { extern struct VEngineClient *engclient; extern struct VEngineServer *engserver; -extern void *srvdll; -extern void *globalvars; -extern void *inputsystem, *vgui; +extern struct IServerGameDLL *srvdll; +extern struct CGlobalVars *globalvars; +extern struct IInputSystem *inputsystem; +extern struct CEngineVGui *vgui; // XXX: not exactly engine *API* but not curently clear where else to put this struct CPlugin_common { @@ -21,7 +21,6 @@ #include "errmsg.h" #include "feature.h" #include "gamedata.h" -#include "gametype.h" #include "intdefs.h" #include "langext.h" #include "mem.h" @@ -31,10 +30,10 @@ FEATURE() -DECL_VFUNC_DYN(void *, PEntityOfEntIndex, int) +DECL_VFUNC_DYN(struct VEngineServer, struct edict *, PEntityOfEntIndex, int) -DEF_PTR_ACCESSOR(struct edict *, edicts) -DEF_ARRAYIDX_ACCESSOR(edict) +DEF_PTR_ACCESSOR(struct CGlobalVars, struct edict *, edicts) +DEF_ARRAYIDX_ACCESSOR(struct edict, edict) static struct edict **edicts = 0; diff --git a/src/extmalloc.c b/src/extmalloc.c index e3f40b8..08538ea 100644 --- a/src/extmalloc.c +++ b/src/extmalloc.c @@ -25,15 +25,15 @@ #ifdef _WIN32 -__declspec(dllimport) void *g_pMemAlloc; +__declspec(dllimport) struct IMemAlloc *g_pMemAlloc; // this interface has changed a bit between versions but thankfully the basic // functions we care about have always been at the start - nice and easy. // note that since Microsoft are a bunch of crazies, overloads are grouped and // reversed so the vtable order here is maybe not what you'd expect otherwise. -DECL_VFUNC(void *, Alloc, 1, usize) -DECL_VFUNC(void *, Realloc, 3, void *, usize) -DECL_VFUNC(void, Free, 5, void *) +DECL_VFUNC(struct IMemAlloc, void *, Alloc, 1, usize) +DECL_VFUNC(struct IMemAlloc, void *, Realloc, 3, void *, usize) +DECL_VFUNC(struct IMemAlloc, void, Free, 5, void *) void *extmalloc(usize sz) { return Alloc(g_pMemAlloc, sz); } void *extrealloc(void *mem, usize sz) { return Realloc(g_pMemAlloc, mem, sz); } diff --git a/src/gameinfo.c b/src/gameinfo.c index 7432073..a3f4eac 100644 --- a/src/gameinfo.c +++ b/src/gameinfo.c @@ -39,7 +39,7 @@ const os_char *gameinfo_gamedir ; const char *gameinfo_title = title; -DECL_VFUNC_DYN(const char *, GetGameDirectory) +DECL_VFUNC_DYN(struct VEngineClient, const char *, GetGameDirectory) bool gameinfo_init() { if_cold (!has_vtidx_GetGameDirectory) { diff --git a/src/gameserver.c b/src/gameserver.c index 7cd7526..6f3d394 100644 --- a/src/gameserver.c +++ b/src/gameserver.c @@ -28,9 +28,10 @@ FEATURE() REQUIRE_GAMEDATA(vtidx_GetSpawnCount) -DECL_VFUNC_DYN(int, GetSpawnCount) +struct CGameServer; +DECL_VFUNC_DYN(struct CGameServer, int, GetSpawnCount) -static void *sv; +static struct CGameServer *sv; int gameserver_spawncount() { return GetSpawnCount(sv); } @@ -64,40 +64,42 @@ DEF_EVENT(HudPaint, int /*width*/, int /*height*/) // right calling convention (x86 Windows/MSVC is funny about passing structs...) struct handlewrap { ulong x; }; -// CEngineVGui -DECL_VFUNC_DYN(unsigned int, GetPanel, int) - -// vgui::ISchemeManager -DECL_VFUNC_DYN(void *, GetIScheme, struct handlewrap) -// vgui::IScheme -DECL_VFUNC_DYN(struct handlewrap, GetFont, const char *, bool) - -// vgui::ISurface -DECL_VFUNC_DYN(void, DrawSetColor, struct rgba) -DECL_VFUNC_DYN(void, DrawFilledRect, int, int, int, int) -DECL_VFUNC_DYN(void, DrawOutlinedRect, int, int, int, int) -DECL_VFUNC_DYN(void, DrawLine, int, int, int, int) -DECL_VFUNC_DYN(void, DrawPolyLine, int *, int *, int) -DECL_VFUNC_DYN(void, DrawSetTextFont, struct handlewrap) -DECL_VFUNC_DYN(void, DrawSetTextColor, struct rgba) -DECL_VFUNC_DYN(void, DrawSetTextPos, int, int) -DECL_VFUNC_DYN(void, DrawPrintText, hud_wchar *, int, int) -DECL_VFUNC_DYN(void, GetScreenSize, int *, int *) -DECL_VFUNC_DYN(int, GetFontTall, struct handlewrap) -DECL_VFUNC_DYN(int, GetCharacterWidth, struct handlewrap, int) -DECL_VFUNC_DYN(int, GetTextSize, struct handlewrap, const hud_wchar *, - int *, int *) - -// vgui::Panel -DECL_VFUNC_DYN(void, SetPaintEnabled, bool) - -static void *matsurf, *toolspanel, *scheme; - -typedef void (*VCALLCONV Paint_func)(void *); +struct CEngineVGui; +DECL_VFUNC_DYN(struct CEngineVGui, unsigned int, GetPanel, int) + +struct ISchemeManager; +DECL_VFUNC_DYN(struct ISchemeManager, void *, GetIScheme, struct handlewrap) +struct IScheme; +DECL_VFUNC_DYN(struct IScheme, struct handlewrap, GetFont, const char *, bool) + +struct ISurface; +DECL_VFUNC_DYN(struct ISurface, void, DrawSetColor, struct rgba) +DECL_VFUNC_DYN(struct ISurface, void, DrawFilledRect, int, int, int, int) +DECL_VFUNC_DYN(struct ISurface, void, DrawOutlinedRect, int, int, int, int) +DECL_VFUNC_DYN(struct ISurface, void, DrawLine, int, int, int, int) +DECL_VFUNC_DYN(struct ISurface, void, DrawPolyLine, int *, int *, int) +DECL_VFUNC_DYN(struct ISurface, void, DrawSetTextFont, struct handlewrap) +DECL_VFUNC_DYN(struct ISurface, void, DrawSetTextColor, struct rgba) +DECL_VFUNC_DYN(struct ISurface, void, DrawSetTextPos, int, int) +DECL_VFUNC_DYN(struct ISurface, void, DrawPrintText, hud_wchar *, int, int) +DECL_VFUNC_DYN(struct ISurface, void, GetScreenSize, int *, int *) +DECL_VFUNC_DYN(struct ISurface, int, GetFontTall, struct handlewrap) +DECL_VFUNC_DYN(struct ISurface, int, GetCharacterWidth, struct handlewrap, int) +DECL_VFUNC_DYN(struct ISurface, int, GetTextSize, struct handlewrap, + const hud_wchar *, int *, int *) + +struct IPanel { void **vtable; }; +DECL_VFUNC_DYN(struct IPanel, void, SetPaintEnabled, bool) + +static struct ISurface *matsurf; +static struct IPanel *toolspanel; +static struct IScheme *scheme; + +typedef void (*VCALLCONV Paint_func)(struct IPanel *); static Paint_func orig_Paint; -void VCALLCONV hook_Paint(void *this) { - int width, height; +void VCALLCONV hook_Paint(struct IPanel *this) { if (this == toolspanel) { + int width, height; hud_screensize(&width, &height); EMIT_HudPaint(width, height); } @@ -149,8 +151,8 @@ void hud_textsize(ulong font, const ushort *s, int *width, int *height) { GetTextSize(matsurf, (struct handlewrap){font}, s, width, height); } -static bool find_toolspanel(void *enginevgui) { - const uchar *insns = (const uchar *)VFUNC(enginevgui, GetPanel); +static bool find_toolspanel(struct CEngineVGui *enginevgui) { + const uchar *insns = enginevgui->vtable[vtidx_GetPanel]; for (const uchar *p = insns; p - insns < 16;) { // first CALL instruction in GetPanel calls GetRootPanel, which gives a // pointer to the specified panel @@ -172,7 +174,7 @@ INIT { errmsg_errorx("couldn't get MatSystemSurface006 interface"); return FEAT_INCOMPAT; } - void *schememgr = factory_engine("VGUI_Scheme010", 0); + struct ISchemeManager *schememgr = factory_engine("VGUI_Scheme010", 0); if_cold (!schememgr) { errmsg_errorx("couldn't get VGUI_Scheme010 interface"); return FEAT_INCOMPAT; @@ -181,13 +183,12 @@ INIT { errmsg_errorx("couldn't find engine tools panel"); return FEAT_INCOMPAT; } - void **vtable = *(void ***)toolspanel; - if_cold (!os_mprot(vtable + vtidx_Paint, sizeof(void *), + if_cold (!os_mprot(toolspanel->vtable + vtidx_Paint, sizeof(void *), PAGE_READWRITE)) { errmsg_errorsys("couldn't make virtual table writable"); return FEAT_FAIL; } - orig_Paint = (Paint_func)hook_vtable(vtable, vtidx_Paint, + orig_Paint = (Paint_func)hook_vtable(toolspanel->vtable, vtidx_Paint, (void *)&hook_Paint); SetPaintEnabled(toolspanel, true); // 1 is the default, first loaded scheme. should always be sourcescheme.res @@ -198,7 +199,7 @@ INIT { END { // don't unhook toolspanel if exiting: it's already long gone! if_cold (sst_userunloaded) { - unhook_vtable(*(void ***)toolspanel, vtidx_Paint, (void *)orig_Paint); + unhook_vtable(toolspanel->vtable, vtidx_Paint, (void *)orig_Paint); SetPaintEnabled(toolspanel, false); } } diff --git a/src/inputhud.c b/src/inputhud.c index 4e332c4..fdefe6a 100644 --- a/src/inputhud.c +++ b/src/inputhud.c @@ -61,7 +61,7 @@ DEF_FEAT_CVAR_MINMAX(sst_inputhud_y, "Input HUD y position (fraction between screen top and bottom)", 0.95, 0, 1, CON_ARCHIVE) -static void *input; +static struct CInput { void **vtable; } *input; static int heldbuttons = 0, tappedbuttons = 0; static struct rgba colours[3] = { @@ -98,8 +98,8 @@ struct CUserCmd { }; #define vtidx_GetUserCmd_l4dbased vtidx_GetUserCmd -DECL_VFUNC_DYN(struct CUserCmd *, GetUserCmd, int) -DECL_VFUNC_DYN(struct CUserCmd *, GetUserCmd_l4dbased, int, int) +DECL_VFUNC_DYN(struct CInput, struct CUserCmd *, GetUserCmd, int) +DECL_VFUNC_DYN(struct CInput, struct CUserCmd *, GetUserCmd_l4dbased, int, int) typedef void (*VCALLCONV CreateMove_func)(void *, int, float, bool); static CreateMove_func orig_CreateMove; @@ -114,16 +114,17 @@ static void VCALLCONV hook_CreateMove(void *this, int seq, float ft, if (cmd) { heldbuttons = cmd->buttons; tappedbuttons |= cmd->buttons; } } // basically a dupe, but calling the other version of GetUserCmd -static void VCALLCONV hook_CreateMove_l4dbased(void *this, int seq, float ft, - bool active) { +static void VCALLCONV hook_CreateMove_l4dbased(struct CInput *this, int seq, + float ft, bool active) { orig_CreateMove(this, seq, ft, active); struct CUserCmd *cmd = GetUserCmd_l4dbased(this, -1, seq); if (cmd) { heldbuttons = cmd->buttons; tappedbuttons |= cmd->buttons; } } -typedef void (*VCALLCONV DecodeUserCmdFromBuffer_func)(void *, void *, int); -typedef void (*VCALLCONV DecodeUserCmdFromBuffer_l4dbased_func)(void *, int, +typedef void (*VCALLCONV DecodeUserCmdFromBuffer_func)(struct CInput *, void *, int); +typedef void (*VCALLCONV DecodeUserCmdFromBuffer_l4dbased_func)(struct CInput *, + int, void *, int); static union { DecodeUserCmdFromBuffer_func prel4d; DecodeUserCmdFromBuffer_l4dbased_func l4dbased; @@ -131,13 +132,13 @@ static union { #define orig_DecodeUserCmdFromBuffer _orig_DecodeUserCmdFromBuffer.prel4d #define orig_DecodeUserCmdFromBuffer_l4dbased \ _orig_DecodeUserCmdFromBuffer.l4dbased -static void VCALLCONV hook_DecodeUserCmdFromBuffer(void *this, void *reader, - int seq) { +static void VCALLCONV hook_DecodeUserCmdFromBuffer(struct CInput *this, + void *reader, int seq) { orig_DecodeUserCmdFromBuffer(this, reader, seq); struct CUserCmd *cmd = GetUserCmd(this, seq); if (cmd) { heldbuttons = cmd->buttons; tappedbuttons |= cmd->buttons; } } -static void VCALLCONV hook_DecodeUserCmdFromBuffer_l4dbased(void *this, +static void VCALLCONV hook_DecodeUserCmdFromBuffer_l4dbased(struct CInput *this, int slot, void *reader, int seq) { orig_DecodeUserCmdFromBuffer_l4dbased(this, slot, reader, seq); struct CUserCmd *cmd = GetUserCmd_l4dbased(this, slot, seq); @@ -366,13 +367,12 @@ HANDLE_EVENT(HudPaint, int screenw, int screenh) { } // find the CInput "input" global -static inline bool find_input(void* vclient) { +static inline bool find_input(struct VClient *vclient) { #ifdef _WIN32 // the only CHLClient::DecodeUserCmdFromBuffer() does is call a virtual // function, so find its thisptr being loaded into ECX - void* decodeusercmd = - (*(void***)vclient)[vtidx_VClient_DecodeUserCmdFromBuffer]; - for (uchar *p = (uchar *)decodeusercmd; p - (uchar *)decodeusercmd < 32;) { + uchar *insns = vclient->vtable[vtidx_VClient_DecodeUserCmdFromBuffer]; + for (uchar *p = insns; p - insns < 32;) { if (p[0] == X86_MOVRMW && p[1] == X86_MODRM(0, 1, 5)) { void **indirect = mem_loadptr(p + 2); input = *indirect; @@ -387,7 +387,7 @@ static inline bool find_input(void* vclient) { } INIT { - void *vclient; + struct VClient *vclient; if (!(vclient = factory_client("VClient015", 0)) && !(vclient = factory_client("VClient016", 0)) && !(vclient = factory_client("VClient017", 0))) { @@ -398,7 +398,7 @@ INIT { errmsg_errorx("couldn't find input global"); return FEAT_INCOMPAT; } - void **vtable = mem_loadptr(input); + void **vtable = input->vtable; // just unprotect the first few pointers (GetUserCmd is 8) if_cold (!os_mprot(vtable, sizeof(void *) * 8, PAGE_READWRITE)) { errmsg_errorsys("couldn't make virtual table writable"); @@ -446,11 +446,10 @@ INIT { } END { - void **vtable = mem_loadptr(input); - unhook_vtable(vtable, vtidx_CreateMove, (void *)orig_CreateMove); + unhook_vtable(input->vtable, vtidx_CreateMove, (void *)orig_CreateMove); // N.B.: since the orig_ function is in a union, we don't have to worry // about which version we're unhooking - unhook_vtable(vtable, vtidx_DecodeUserCmdFromBuffer, + unhook_vtable(input->vtable, vtidx_DecodeUserCmdFromBuffer, (void *)orig_DecodeUserCmdFromBuffer); } diff --git a/src/kvsys.c b/src/kvsys.c index 7996928..9c0f75c 100644 --- a/src/kvsys.c +++ b/src/kvsys.c @@ -16,8 +16,6 @@ */ #include "abi.h" -#include "con_.h" -#include "engineapi.h" #include "extmalloc.h" #include "errmsg.h" #include "feature.h" @@ -25,19 +23,21 @@ #include "hook.h" #include "kvsys.h" #include "langext.h" -#include "mem.h" #include "os.h" #include "vcall.h" #include "x86.h" FEATURE() -void *KeyValuesSystem(); // vstlib symbol -static void *kvs; +struct IKeyValuesSystem { void **vtable; }; + +struct IKeyValuesSystem *KeyValuesSystem(); // vstlib symbol +static struct IKeyValuesSystem *kvs; static int vtidx_GetSymbolForString = 3, vtidx_GetStringForSymbol = 4; static bool iskvv2 = false; -DECL_VFUNC_DYN(int, GetSymbolForString, const char *, bool) -DECL_VFUNC_DYN(const char *, GetStringForSymbol, int) +DECL_VFUNC_DYN(struct IKeyValuesSystem, int, GetSymbolForString, const char *, + bool) +DECL_VFUNC_DYN(struct IKeyValuesSystem, const char *, GetStringForSymbol, int) const char *kvsys_symtostr(int sym) { return GetStringForSymbol(kvs, sym); } int kvsys_strtosym(const char *s) { return GetSymbolForString(kvs, s, true); } @@ -100,16 +100,16 @@ INIT { // kvs ABI check is probably relevant for other games, but none that we // currently actively support if (GAMETYPE_MATCHES(L4D2x)) { - void **kvsvt = mem_loadptr(kvs); - detectabichange(kvsvt); - if_cold (!os_mprot(kvsvt + vtidx_GetStringForSymbol, sizeof(void *), - PAGE_READWRITE)) { + void **vtable = kvs->vtable; + detectabichange(vtable); + if_cold (!os_mprot(vtable + vtidx_GetStringForSymbol, + sizeof(void *), PAGE_READWRITE)) { errmsg_warnx("couldn't make KeyValuesSystem vtable writable"); errmsg_note("won't be able to prevent any nag messages"); } else { orig_GetStringForSymbol = (GetStringForSymbol_func)hook_vtable( - kvsvt, vtidx_GetStringForSymbol, + vtable, vtidx_GetStringForSymbol, (void *)hook_GetStringForSymbol); } } @@ -118,7 +118,7 @@ INIT { END { if (orig_GetStringForSymbol) { - unhook_vtable(*(void ***)kvs, vtidx_GetStringForSymbol, + unhook_vtable(kvs->vtable, vtidx_GetStringForSymbol, (void *)orig_GetStringForSymbol); } } diff --git a/src/l4d1democompat.c b/src/l4d1democompat.c index 6754d76..8cf0d5f 100644 --- a/src/l4d1democompat.c +++ b/src/l4d1democompat.c @@ -31,11 +31,12 @@ FEATURE("Left 4 Dead 1 demo file backwards compatibility") GAMESPECIFIC(L4D1_1022plus) +struct CDemoFile; // NOTE: not bothering to put this in gamedata since it's actually a constant. // We could optimise the gamedata system further to constant-fold things with no // leaves beyond the GAMESPECIFIC cutoff or whatever. But that sounds annoying. #define off_CDemoFile_protocol 272 -DEF_ACCESSORS(int, CDemoFile_protocol) +DEF_ACCESSORS(struct CDemoFile, int, CDemoFile_protocol) // L4D1 bumps the demo protocol version with every update to the game, which // means whenever there is a security update, you cannot watch old demos. From @@ -106,7 +107,7 @@ static int hook_GetHostVersion() { } static int *this_protocol; -static void VCALLCONV hook_ReadDemoHeader(void *this) { +static void VCALLCONV hook_ReadDemoHeader(struct CDemoFile *this) { // The mid-function hook needs to get the protocol from `this`, but by that // point we won't be able to rely on the ECX register and/or any particular // stack spill layout. So... offset the pointer and stick it in a global. diff --git a/src/l4daddon.c b/src/l4daddon.c index 8e61e05..d05cc80 100644 --- a/src/l4daddon.c +++ b/src/l4daddon.c @@ -54,7 +54,7 @@ static char last_mission[128] = {0}, last_gamemode[128] = {0}; static int last_addonvecsz = 0; static bool last_disallowaddons = false; -DECL_VFUNC_DYN(void, ManageAddonsForActiveSession) +DECL_VFUNC_DYN(struct VEngineClient, void, ManageAddonsForActiveSession) // Crazy full name: FileSystem_ManageAddonsForActiveSession. Hence the acronym. // Note: the 4th parameter was first added in 2.2.0.4 (21 Oct 2020), but we @@ -141,8 +141,7 @@ e: orig_FS_MAFAS(disallowaddons, mission, gamemode, ismutation); static inline bool find_FS_MAFAS() { #ifdef _WIN32 - const uchar *insns = (const uchar *)VFUNC(engclient, - ManageAddonsForActiveSession); + const uchar *insns = engclient->vtable[vtidx_ManageAddonsForActiveSession]; // CEngineClient::ManageAddonsForActiveSession just calls FS_MAFAS for (const uchar *p = insns; p - insns < 32;) { if (p[0] == X86_CALL) { diff --git a/src/l4dmm.c b/src/l4dmm.c index 40d3c57..67af36d 100644 --- a/src/l4dmm.c +++ b/src/l4dmm.c @@ -21,10 +21,8 @@ #include "errmsg.h" #include "feature.h" #include "gamedata.h" -#include "gametype.h" #include "kvsys.h" #include "langext.h" -#include "mem.h" #include "os.h" #include "vcall.h" @@ -34,9 +32,12 @@ REQUIRE(kvsys) REQUIRE_GAMEDATA(vtidx_GetMatchNetworkMsgController) REQUIRE_GAMEDATA(vtidx_GetActiveGameServerDetails) -DECL_VFUNC_DYN(void *, GetMatchNetworkMsgController) -DECL_VFUNC_DYN(struct KeyValues *, GetActiveGameServerDetails, - struct KeyValues *) +struct IMatchFramework; +struct IMatchNetworkMsgController; +DECL_VFUNC_DYN(struct IMatchFramework, struct IMatchNetworkMsgController*, + GetMatchNetworkMsgController) +DECL_VFUNC_DYN(struct IMatchNetworkMsgController, struct KeyValues *, + GetActiveGameServerDetails, struct KeyValues *) // Old L4D1 uses a heavily modified version of the CMatchmaking in Source 2007. // None of it is publicly documented or well-understood but I was able to figure @@ -47,12 +48,14 @@ struct contextval { const char *val; /* other stuff unknown */ }; -DECL_VFUNC(struct contextval *, unknown_contextlookup, 67, const char *) +struct CMatchmaking; +DECL_VFUNC(struct CMatchmaking, struct contextval *, unknown_contextlookup, 67, + const char *) static void *matchfwk; static union { // space saving struct { int sym_game, sym_campaign; }; // "game/campaign" KV lookup - void *oldmmiface; // old L4D1 interface + struct CMatchmaking *oldmmiface; // old L4D1 interface } U; #define oldmmiface U.oldmmiface #define sym_game U.sym_game @@ -77,7 +80,8 @@ const char *l4dmm_curcampaign() { return 0; } #endif - void *ctrlr = GetMatchNetworkMsgController(matchfwk); + struct IMatchNetworkMsgController *ctrlr = + GetMatchNetworkMsgController(matchfwk); struct KeyValues *kv = GetActiveGameServerDetails(ctrlr, 0); if_cold (!kv) return 0; // not in server, probably const char *ret = 0; diff --git a/src/l4dreset.c b/src/l4dreset.c index 84daa65..207e487 100644 --- a/src/l4dreset.c +++ b/src/l4dreset.c @@ -28,8 +28,8 @@ #include "fastfwd.h" #include "feature.h" #include "gamedata.h" -#include "gametype.h" #include "gameserver.h" +#include "gametype.h" #include "hook.h" #include "intdefs.h" #include "langext.h" @@ -55,11 +55,11 @@ REQUIRE_GAMEDATA(vtidx_GameFrame) // note: for L4D1 only, always defined anyway REQUIRE_GAMEDATA(vtidx_GameShutdown) REQUIRE_GAMEDATA(vtidx_OnGameplayStart) -static void **votecontroller; +static struct CVoteController **votecontroller; static int off_callerrecords = -1; static int off_voteissues; -static void *director; // "TheDirector" server global +struct CDirector { void **vtable; } *director; // "TheDirector" server global // Note: the vote callers vector contains these as elements. We don't currently // do anything with the structure, but we're keeping it here for reference. @@ -73,12 +73,13 @@ static void *director; // "TheDirector" server global };*/ struct CVoteIssue; -DECL_VFUNC(const char *, SetIssueDetails, 1 + NVDTOR, const char *) -DECL_VFUNC(const char *, GetDisplayString, 8 + NVDTOR) -DECL_VFUNC(const char *, ExecuteCommand, 9 + NVDTOR) +DECL_VFUNC(struct CVoteIssue, const char *, SetIssueDetails, 1 + NVDTOR, + const char *) +DECL_VFUNC(struct CVoteIssue, const char *, GetDisplayString, 8 + NVDTOR) +DECL_VFUNC(struct CVoteIssue, const char *, ExecuteCommand, 9 + NVDTOR) -DEF_PTR_ACCESSOR(struct CUtlVector, voteissues) -DEF_PTR_ACCESSOR(struct CUtlVector, callerrecords) +DEF_PTR_ACCESSOR(struct CVoteController, struct CUtlVector, voteissues) +DEF_PTR_ACCESSOR(struct CVoteController, struct CUtlVector, callerrecords) static struct CVoteIssue *getissue(const char *textkey) { struct CVoteIssue **issues = getptr_voteissues(*votecontroller)->m.mem; @@ -234,9 +235,9 @@ HANDLE_EVENT(Tick, bool simulating) { } } -typedef void (*VCALLCONV OnGameplayStart_func)(void *this); +typedef void (*VCALLCONV OnGameplayStart_func)(struct CDirector *this); static OnGameplayStart_func orig_OnGameplayStart; -static void VCALLCONV hook_OnGameplayStart(void *this) { +static void VCALLCONV hook_OnGameplayStart(struct CDirector *this) { orig_OnGameplayStart(this); if (nextmapnum) { // if we changed map more than 1 time, cancel the reset. this'll happen @@ -500,8 +501,8 @@ ok: // Director::Update calls UnfreezeTeam after the first jmp instruction return false; } - -DECL_VFUNC_DYN(int, GetEngineBuildNumber) +// XXX: duped def in democustom: should this belong somewhere else? +DECL_VFUNC_DYN(struct VEngineClient, int, GetEngineBuildNumber) INIT { struct con_cmd *cmd_listissues = con_findcmd("listissues"); @@ -533,7 +534,7 @@ INIT { #ifdef _WIN32 // L4D1 has no Linux build, no need to check whether L4D2 if (GAMETYPE_MATCHES(L4D2)) { #endif - vtable = mem_loadptr(director); + vtable = director->vtable; if_cold (!os_mprot(vtable + vtidx_OnGameplayStart, sizeof(*vtable), PAGE_READWRITE)) { errmsg_errorsys("couldn't make virtual table writable"); @@ -544,7 +545,7 @@ INIT { #ifdef _WIN32 // L4D1 has no Linux build! } else /* L4D1 */ { - void *GameFrame = (*(void ***)srvdll)[vtidx_GameFrame]; + void *GameFrame = srvdll->vtable[vtidx_GameFrame]; if_cold (!find_UnfreezeTeam(GameFrame)) { errmsg_errorx("couldn't find UnfreezeTeam function"); return FEAT_INCOMPAT; diff --git a/src/l4dwarp.c b/src/l4dwarp.c index 99d678e..03edb9e 100644 --- a/src/l4dwarp.c +++ b/src/l4dwarp.c @@ -47,9 +47,11 @@ REQUIRE_GAMEDATA(vtidx_AddBoxOverlay2) REQUIRE_GAMEDATA(vtidx_AddLineOverlay) REQUIRE_GAMEDATA(vtidx_Teleport) -DECL_VFUNC_DYN(void, Teleport, const struct vec3f */*pos*/, - const struct vec3f */*pos*/, const struct vec3f */*vel*/) -DECL_VFUNC(const struct vec3f *, OBBMaxs, 2) +// XXX: could make these calls type safe in future? just tricky because the +// entity hierarchy is kind of crazy so it's not clear which type name to pick +DECL_VFUNC_DYN(void, void, Teleport, const struct vec3f */*pos*/, + const struct vec3f */*ang*/, const struct vec3f */*vel*/) +DECL_VFUNC(void, const struct vec3f *, OBBMaxs, 2) // IMPORTANT: padsz parameter is missing in L4D1, but since it's cdecl, we can // still call it just the same (we always pass 0, so there's no difference). @@ -78,17 +80,18 @@ typedef void (*VCALLCONV CTraceFilterSimple_ctor)( #define PLAYERMASK 0x0201420B // debug overlay stuff, only used by sst_l4d_previewwarp -static void *dbgoverlay; -DECL_VFUNC_DYN(void, AddLineOverlay, const struct vec3f *, - const struct vec3f *, int, int, int, bool, float) -DECL_VFUNC_DYN(void, AddBoxOverlay2, const struct vec3f *, +static struct IVDebugOverlay *dbgoverlay; +DECL_VFUNC_DYN(struct IVDebugOverlay, void, AddLineOverlay, + const struct vec3f *, const struct vec3f *, int, int, int, bool, float) +DECL_VFUNC_DYN(struct IVDebugOverlay, void, AddBoxOverlay2, const struct vec3f *, const struct vec3f *, const struct vec3f *, - const struct rgba *, const struct rgba *, float) + const struct vec3f *, const struct rgba *, const struct rgba *, float) -DEF_ACCESSORS(struct vec3f, entpos) -DEF_ACCESSORS(struct vec3f, eyeang) -DEF_ACCESSORS(uint, teamnum) -DEF_PTR_ACCESSOR(void, collision) +// XXX: more type safety stuff here also +DEF_ACCESSORS(void, struct vec3f, entpos) +DEF_ACCESSORS(void, struct vec3f, eyeang) +DEF_ACCESSORS(void, uint, teamnum) +DEF_PTR_ACCESSOR(void, void, collision) static struct vec3f warptarget(void *ent) { struct vec3f org = get_entpos(ent), ang = get_eyeang(ent); @@ -356,16 +356,16 @@ static void do_featureinit() { } } -typedef void (*VCALLCONV VGuiConnect_func)(void *this); +typedef void (*VCALLCONV VGuiConnect_func)(struct CEngineVGui *this); static VGuiConnect_func orig_VGuiConnect; -static void VCALLCONV hook_VGuiConnect(void *this) { +static void VCALLCONV hook_VGuiConnect(struct CEngineVGui *this) { orig_VGuiConnect(this); do_featureinit(); fixes_apply(); - unhook_vtable(*(void ***)vgui, vtidx_VGuiConnect, (void *)orig_VGuiConnect); + unhook_vtable(vgui->vtable, vtidx_VGuiConnect, (void *)orig_VGuiConnect); } -DECL_VFUNC_DYN(bool, VGuiIsInitialized) +DECL_VFUNC_DYN(struct CEngineVGui, bool, VGuiIsInitialized) // --- Magical deferred load order hack nonsense! --- // VDF plugins load right after server.dll, but long before most other stuff. We @@ -386,13 +386,13 @@ static bool deferinit() { // CEngineVGui::IsInitialized() which works everywhere. if (VGuiIsInitialized(vgui)) return false; sst_earlyloaded = true; // let other code know - if_cold (!os_mprot(*(void ***)vgui + vtidx_VGuiConnect, ssizeof(void *), + if_cold (!os_mprot(vgui->vtable + vtidx_VGuiConnect, ssizeof(void *), PAGE_READWRITE)) { errmsg_warnsys("couldn't make CEngineVGui vtable writable for deferred " "feature setup"); goto e; } - orig_VGuiConnect = (VGuiConnect_func)hook_vtable(*(void ***)vgui, + orig_VGuiConnect = (VGuiConnect_func)hook_vtable(vgui->vtable, vtidx_VGuiConnect, (void *)&hook_VGuiConnect); return true; diff --git a/src/trace.c b/src/trace.c index 2fe18c6..1118bdf 100644 --- a/src/trace.c +++ b/src/trace.c @@ -21,6 +21,7 @@ #include "gametype.h" #include "intdefs.h" #include "trace.h" +#include "vcall.h" FEATURE() // TODO(compat): limiting to tested branches for now; support others as needed @@ -35,10 +36,9 @@ struct ray { bool isray, isswept; }; -static void *srvtrace; - -DECL_VFUNC(void, TraceRay, 5, struct ray *, uint /*mask*/, void */*filter*/, - struct CGameTrace *) +static struct IEngineTraceServer *srvtrace; +DECL_VFUNC(struct IEngineTraceServer, void, TraceRay, 5, + struct ray *, uint /*mask*/, void */*filter*/, struct CGameTrace *) static inline bool nonzero(struct vec3f v) { union { struct vec3f v; struct { unsigned int x, y, z; }; } u = {v}; diff --git a/src/vcall.h b/src/vcall.h index 55fcfe6..7f230b0 100644 --- a/src/vcall.h +++ b/src/vcall.h @@ -109,32 +109,33 @@ #define _VCALL_UNUSED #endif -#define _DECL_VFUNC_DYN(ret, conv, name, ...) \ - typedef typeof(ret) (*conv name##_func)(void * __VA_OPT__(,) __VA_ARGS__); \ - static inline _VCALL_UNUSED typeof(ret) name(void *this __VA_OPT__(,) \ - _VCALL_ARGLIST(__VA_ARGS__)) { \ +#define _DECL_VFUNC_DYN(class, ret, conv, name, ...) \ + typedef typeof(ret) (*conv name##_func)(typeof(class) * __VA_OPT__(,) \ + __VA_ARGS__); \ + static inline _VCALL_UNUSED typeof(ret) name( \ + typeof(class) *this __VA_OPT__(,) _VCALL_ARGLIST(__VA_ARGS__)) { \ _VCALL_RET(ret) VCALL(this, name __VA_OPT__(,) \ _VCALL_PASSARGS(__VA_ARGS__)); \ } -#define _DECL_VFUNC(ret, conv, name, idx, ...) \ +#define _DECL_VFUNC(class, ret, conv, name, idx, ...) \ enum { vtidx_##name = (idx) }; \ - _DECL_VFUNC_DYN(ret, conv, name __VA_OPT__(,) __VA_ARGS__) + _DECL_VFUNC_DYN(class, ret, conv, name __VA_OPT__(,) __VA_ARGS__) -/* Define a virtual function with a known index */ -#define DECL_VFUNC(ret, name, idx, ...) \ - _DECL_VFUNC(ret, VCALLCONV, name, idx __VA_OPT__(,) __VA_ARGS__) +/* Define a virtual function with a known index. */ +#define DECL_VFUNC(class, ret, name, idx, ...) \ + _DECL_VFUNC(class, ret, VCALLCONV, name, idx __VA_OPT__(,) __VA_ARGS__) /* Define a virtual function with a known index, without thiscall convention */ -#define DECL_VFUNC_CDECL(ret, name, idx, ...) \ - _DECL_VFUNC(ret, , name, idx __VA_OPT__(,) __VA_ARGS__) +#define DECL_VFUNC_CDEFCL(class, ret, name, idx, ...) \ + _DECL_VFUNC(class, ret, , name, idx __VA_OPT__(,) __VA_ARGS__) /* Define a virtual function with an index defined elsewhere (e.g. gamedata) */ -#define DECL_VFUNC_DYN(ret, name, ...) \ - _DECL_VFUNC_DYN(ret, VCALLCONV, name __VA_OPT__(,) __VA_ARGS__) +#define DECL_VFUNC_DYN(class, ret, name, ...) \ + _DECL_VFUNC_DYN(class, ret, VCALLCONV, name __VA_OPT__(,) __VA_ARGS__) /* Define a virtual function with an index defined elsewhere, without thiscall */ -#define DECL_VFUNC_CDECLDYN(ret, name, ...) \ - _DECL_VFUNC_DYN(ret, , name __VA_OPT__(,) __VA_ARGS__) +#define DECL_VFUNC_CDECLDYN(class, ret, name, ...) \ + _DECL_VFUNC_DYN(class, void, ret, , name __VA_OPT__(,) __VA_ARGS__) #endif diff --git a/src/xhair.c b/src/xhair.c index e0017ba..9d1ee34 100644 --- a/src/xhair.c +++ b/src/xhair.c @@ -26,7 +26,7 @@ FEATURE("custom crosshair drawing") REQUIRE(hud) -DECL_VFUNC_DYN(bool, IsInGame) +DECL_VFUNC_DYN(struct VEngineClient, bool, IsInGame) DEF_FEAT_CVAR(sst_xhair, "Enable custom crosshair", 0, CON_ARCHIVE) DEF_FEAT_CVAR(sst_xhair_colour, |