aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gamedata/matchmaking.txt2
-rw-r--r--src/ac.c16
-rw-r--r--src/accessor.h38
-rw-r--r--src/autojump.c29
-rw-r--r--src/clientcon.c3
-rw-r--r--src/con_.c54
-rw-r--r--src/con_.h12
-rw-r--r--src/democustom.c7
-rw-r--r--src/demorec.c21
-rw-r--r--src/demorec.h5
-rw-r--r--src/engineapi.c11
-rw-r--r--src/engineapi.h24
-rw-r--r--src/ent.c7
-rw-r--r--src/extmalloc.c8
-rw-r--r--src/gameinfo.c2
-rw-r--r--src/gameserver.c5
-rw-r--r--src/hud.c79
-rw-r--r--src/inputhud.c37
-rw-r--r--src/kvsys.c26
-rw-r--r--src/l4d1democompat.c5
-rw-r--r--src/l4daddon.c5
-rw-r--r--src/l4dmm.c20
-rw-r--r--src/l4dreset.c29
-rw-r--r--src/l4dwarp.c27
-rw-r--r--src/sst.c12
-rw-r--r--src/trace.c8
-rw-r--r--src/vcall.h31
-rw-r--r--src/xhair.c2
28 files changed, 269 insertions, 256 deletions
diff --git a/gamedata/matchmaking.txt b/gamedata/matchmaking.txt
index 2c93120..0713b56 100644
--- a/gamedata/matchmaking.txt
+++ b/gamedata/matchmaking.txt
@@ -1,6 +1,8 @@
# IMatchFramework
vtidx_GetMatchNetworkMsgController
L4D 10 # NOTE: probably same for aswarm or p2 except with IAppSystem shift
+
+# IMatchNetworkMsgController
vtidx_GetActiveGameServerDetails
L4D 1
diff --git a/src/ac.c b/src/ac.c
index ea3c08a..00edaed 100644
--- a/src/ac.c
+++ b/src/ac.c
@@ -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);
diff --git a/src/con_.c b/src/con_.c
index 35d4c45..efc5912 100644
--- a/src/con_.c
+++ b/src/con_.c
@@ -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,
diff --git a/src/con_.h b/src/con_.h
index bdd4b87..0dd50a5 100644
--- a/src/con_.h
+++ b/src/con_.h
@@ -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 {
diff --git a/src/ent.c b/src/ent.c
index 118c170..14e0788 100644
--- a/src/ent.c
+++ b/src/ent.c
@@ -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); }
diff --git a/src/hud.c b/src/hud.c
index f402c15..cb360ca 100644
--- a/src/hud.c
+++ b/src/hud.c
@@ -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);
diff --git a/src/sst.c b/src/sst.c
index bd73d44..e02f196 100644
--- a/src/sst.c
+++ b/src/sst.c
@@ -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,