diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/ac.c | 3 | ||||
| -rw-r--r-- | src/alias.c | 1 | ||||
| -rw-r--r-- | src/alias.h | 5 | ||||
| -rw-r--r-- | src/bind.c | 3 | ||||
| -rw-r--r-- | src/bitbuf.h | 5 | ||||
| -rw-r--r-- | src/con_.c | 50 | ||||
| -rw-r--r-- | src/con_.h | 5 | ||||
| -rw-r--r-- | src/democustom.c | 7 | ||||
| -rw-r--r-- | src/demorec.c | 32 | ||||
| -rw-r--r-- | src/dictmaptree.h | 2 | ||||
| -rw-r--r-- | src/engineapi.c | 9 | ||||
| -rw-r--r-- | src/ent.c | 8 | ||||
| -rw-r--r-- | src/fixes.c | 3 | ||||
| -rw-r--r-- | src/fov.c | 10 | ||||
| -rw-r--r-- | src/gamedata.c | 4 | ||||
| -rw-r--r-- | src/gamedata.h | 8 | ||||
| -rw-r--r-- | src/gameinfo.c | 5 | ||||
| -rw-r--r-- | src/hook.c | 6 | ||||
| -rw-r--r-- | src/kvsys.c | 3 | ||||
| -rw-r--r-- | src/l4dmm.c | 2 | ||||
| -rw-r--r-- | src/l4dreset.c | 39 | ||||
| -rw-r--r-- | src/l4dwarp.c | 6 | ||||
| -rw-r--r-- | src/mem.h | 12 | ||||
| -rw-r--r-- | src/nomute.c | 8 | ||||
| -rw-r--r-- | src/os-win32.h | 3 | ||||
| -rw-r--r-- | src/sst.c | 28 | 
26 files changed, 105 insertions, 162 deletions
@@ -230,8 +230,7 @@ static void VCALLCONV hook_DispatchInputEvent(void *this,  static bool find_DispatchInputEvent(void) {  #ifdef _WIN32 -	// Crazy pointer-chasing path to get to DispatchInputEvent (to log keypresses -	// and their associated binds): +	// Crazy pointer-chasing path to get to DispatchInputEvent:  	// IGameUIFuncs interface  	// -> CGameUIFuncs::GetDesktopResolution vfunc  	//  -> IGame/CGame (first mov into ECX) diff --git a/src/alias.c b/src/alias.c index e390cd1..c660367 100644 --- a/src/alias.c +++ b/src/alias.c @@ -76,7 +76,6 @@ static bool find_alias_head(con_cmdcb alias_cb) {  	for (const uchar *p = insns; p - insns < 64;) {  		// alias command with no args calls ConMsg() then loads the head pointer  		// that asm looks like: call <reg>; mov <reg>, dword ptr [x] -		// (we don't care about the exact registers)  		if (p[0] == X86_MISCMW && (p[1] & 0xF8) == 0xD0 &&  				p[2] == X86_MOVRMW && (p[3] & 0xC7) == 0x05) {  			_alias_head = mem_loadptr(p + 4); diff --git a/src/alias.h b/src/alias.h index bc125eb..c13f342 100644 --- a/src/alias.h +++ b/src/alias.h @@ -19,13 +19,16 @@  struct alias {  	struct alias *next; -	char name[32]; // TIL this has a hard limit :^) +	char name[32];  	char *value;  };  extern struct alias **_alias_head;  #define alias_head (*_alias_head) // act as a global +/* Clears all aliases from the engine's internal list. */  void alias_nuke(void); + +/* Removes a specific named alias from the engine's internal list. */  void alias_rm(const char *name);  #endif @@ -39,8 +39,7 @@ static bool find_keyinfo(con_cmdcb klbc_cb) {  #ifdef _WIN32  	const uchar *insns = (const uchar *)klbc_cb;  	for (const uchar *p = insns; p - insns < 32;) { -		// key_listboundkeys command, in its loop through each possible index, -		// does a mov from that index into a register, something like: +		// key_listboundkeys loops through each index, moving into a register:  		// mov <reg>, dword ptr [<reg> * 8 + s_pKeyInfo]  		if (p[0] == X86_MOVRMW && (p[1] & 0xC7) == 4 /* SIB + imm32 */ &&  				(p[2] & 0xC7) == 0xC5 /* [immediate + reg * 8] */) { diff --git a/src/bitbuf.h b/src/bitbuf.h index 8700af3..a2ee60f 100644 --- a/src/bitbuf.h +++ b/src/bitbuf.h @@ -1,5 +1,5 @@  /* - * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * Copyright © 2023 Michael Smith <mikesmiffy128@gmail.com>   *   * Permission to use, copy, modify, and/or distribute this software for any   * purpose with or without fee is hereby granted, provided that the above @@ -49,8 +49,7 @@ static inline void bitbuf_appendbits(struct bitbuf *bb, bitbuf_cell x,  	// OR into the existing cell (lower bits were already set!)  	bb->buf_as_cells[idx] |= x << shift;  	// assign the next cell (that also clears the upper bits for the next OR) -	// note: if nbits fits in the first cell, this just 0s the next cell, which -	// is absolutely fine +	// if nbits fits in the first cell, this zeros the next cell, which is fine  	bb->buf_as_cells[idx + 1] = x >> (bitbuf_cell_bits - shift);  	bb->curbit += nbits;  } @@ -38,24 +38,13 @@   * Don't get set on fire.                                                     *  \******************************************************************************/ -// given to us by the engine to unregister cvars in bulk on plugin unload -static int dllid; - -// external spaghetti variable, exists only because valve are bad +static int dllid; // from AllocateDLLIdentifier(), lets us unregister in bulk  int con_cmdclient; -// these have to be extern because of varargs nonsense - they get wrapped in a -// macro for the actual api (con_colourmsg) -void *_con_iface; -void (*_con_colourmsgf)(void *this, const struct rgba *c, const char *fmt, -		...) _CON_PRINTF(3, 4); - -// bootstrap hackery, see "GENIUS HACK" comment below :)  DECL_VFUNC(void *, FindCommandBase_p2, 13, const char *)  DECL_VFUNC(void *, FindCommand_nonp2, 14, const char *)  DECL_VFUNC(void *, FindVar_nonp2, 12, const char *) -// rest of these are figured out from gamedata after initial game detection  DECL_VFUNC_DYN(int, AllocateDLLIdentifier)  DECL_VFUNC_DYN(void, RegisterConCommand, /*ConCommandBase*/ void *)  DECL_VFUNC_DYN(void, UnregisterConCommands, int) @@ -71,35 +60,31 @@ DECL_VFUNC_DYN(void, CallGlobalChangeCallbacks, struct con_var *, const char *,  typedef void (*ConsoleColorPrintf_func)(void *, const struct rgba *,  		const char *, ...); +// these have to be extern for con_colourmsg(), due to varargs nonsense +void *_con_iface; +ConsoleColorPrintf_func _con_colourmsgf; +  static inline void initval(struct con_var *v) { -	// v->strlen is set to defaultval len in _DEF_CVAR so we don't need to call -	// strlen() on each string :) -	v->strval = extmalloc(v->strlen); +	v->strval = extmalloc(v->strlen); // note: strlen is preset in _DEF_CVAR()  	memcpy(v->strval, v->defaultval, v->strlen);  } -// generated by build/codegen.c, defines regcmds() and freevars() -#include <cmdinit.gen.h> +#include <cmdinit.gen.h> // generated by build/codegen.c -// to try and be like the engine even though it's probably not actually -// required, we call the Internal* virtual functions by actual virtual lookup. -// since the vtables are filled dynamically (below), we store this index; other -// indices are just offset from this one since the 3-or-4 functions are all -// right next to each other. the #defines allow us to still use the nice vcall -// stuff. +// to try and match the engine even though it's probably not strictly required, +// we call the Internal* virtual functions via the actual vtable. since vtables +// are built dynamically (below), we store this index; other indices are just +// offset from it since these 3-or-4 functions are all right next to each other.  static int vtidx_InternalSetValue;  #define vtidx_InternalSetFloatValue (vtidx_InternalSetValue + 1)  #define vtidx_InternalSetIntValue (vtidx_InternalSetValue + 2)  #define vtidx_InternalSetColorValue (vtidx_InternalSetValue + 3) -// implementation of virtual functions for our vars and commands below... -  static void VCALLCONV dtor(void *_) {} // we don't use constructors/destructors  static bool VCALLCONV IsCommand_cmd(void *this) { return true; }  static bool VCALLCONV IsCommand_var(void *this) { return false; } -// flag stuff  static bool VCALLCONV IsFlagSet_cmd(struct con_cmd *this, int flags) {  	return !!(this->base.flags & flags);  } @@ -125,7 +110,6 @@ static int VCALLCONV GetFlags_var(struct con_var *this) {  	return this->parent->base.flags;  } -// basic registration stuff  static const char *VCALLCONV GetName_cmd(struct con_cmd *this) {  	return this->base.name;  } @@ -154,9 +138,8 @@ static bool VCALLCONV ClampValue(struct con_var *this, float *f) {  	return false;  } -// command-specific stuff  int VCALLCONV AutoCompleteSuggest(void *this, const char *partial, -		/* CUtlVector */ void *commands) { +		/*CUtlVector*/ void *commands) {  	// TODO(autocomplete): implement this if needed later  	return 0;  } @@ -164,11 +147,10 @@ bool VCALLCONV CanAutoComplete(void *this) {  	return false;  }  void VCALLCONV Dispatch(struct con_cmd *this, const struct con_cmdargs *args) { -	// only try cb, cbv1 and iface should never get used by us +	// only try cb; cbv1 and iface should never get used by us  	if (this->use_newcb && this->cb) this->cb(args);  } -// var-specific stuff  static void VCALLCONV ChangeStringValue(struct con_var *this, const char *s,  		float oldf) {  	char *old = alloca(this->strlen); @@ -232,8 +214,7 @@ DECL_VFUNC_DYN(void, InternalSetFloatValue, float)  DECL_VFUNC_DYN(void, InternalSetIntValue, int)  DECL_VFUNC_DYN(void, InternalSetColorValue, struct rgba) -// Hack: IConVar things get this-adjusted pointers, we just reverse the offset -// to get the top pointer. +// 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)); @@ -379,8 +360,7 @@ void con_init(void) {  	*pv++ = (void *)&InternalSetFloatValue_impl;  	*pv++ = (void *)&InternalSetIntValue_impl;  	if (GAMETYPE_MATCHES(L4D2x) || GAMETYPE_MATCHES(Portal2)) { // ugh, annoying -		// This is InternalSetColorValue, but that's basically the same thing, -		// when you think about it. +		// InternalSetColorValue, literally the same machine instructions as int  		*pv++ = (void *)&InternalSetIntValue_impl;  	}  	*pv++ = (void *)&ClampValue;; @@ -79,10 +79,7 @@ typedef void (*con_cmdcbv1)(void);  typedef int (*con_complcb)(const char *part,  		char cmds[CON_CMD_MAXCOMPLETE][CON_CMD_MAXCOMPLLEN]); -/* - * These are called by the plugin load/unload functions; they have no use - * elsewhere. - */ +/* These are called on plugin load/unload. They should not be used elsewhere. */  bool con_detect(int pluginver);  void con_init(void);  void con_disconnect(void); diff --git a/src/democustom.c b/src/democustom.c index c2c6ae1..0ecbaa3 100644 --- a/src/democustom.c +++ b/src/democustom.c @@ -76,11 +76,8 @@ void democustom_write(const void *buf, int len) {  	bitbuf_reset(&bb);  } -// This finds the CDemoRecorder::WriteMessages() function, which takes a raw -// network packet, wraps it up in the appropriate demo framing format and writes -// it out to the demo file being recorded.  static bool find_WriteMessages(void) { -	// TODO(compat): probably rewrite this to just scan for a call instruction! +	// TODO(compat): rewrite this to just scan for a call instruction!  	const uchar *insns = (*(uchar ***)demorecorder)[vtidx_RecordPacket];  	// RecordPacket calls WriteMessages pretty much right away:  	// 56           push  esi @@ -101,8 +98,6 @@ static bool find_WriteMessages(void) {  #endif  	if (!memcmp(insns, bytes, sizeof(bytes))) {  		ssize off = mem_loadoffset(insns + sizeof(bytes)); -		// ... and then offset is relative to the address of whatever is _after_ -		// the call instruction... because x86.  		WriteMessages = (WriteMessages_func)(insns + sizeof(bytes) + 4 + off);  		return true;  	} diff --git a/src/demorec.c b/src/demorec.c index ee57c12..07e51d1 100644 --- a/src/demorec.c +++ b/src/demorec.c @@ -73,9 +73,6 @@ static void VCALLCONV hook_SetSignonState(void *this_, int state) {  typedef void (*VCALLCONV StopRecording_func)(void *);  static StopRecording_func orig_StopRecording;  static void VCALLCONV hook_StopRecording(void *this) { -	// This can be called any number of times in a row, generally twice per load -	// and once per explicit disconnect. Each time the engine sets demonum to 0 -	// and recording to false.  	bool wasrecording = *recording;  	int lastnum = *demonum;  	orig_StopRecording(this); @@ -164,20 +161,17 @@ static void hook_stop_cb(const struct con_cmdargs *args) {  	wantstop = false;  } -// This finds the "demorecorder" global variable (the engine-wide CDemoRecorder -// instance).  static inline bool find_demorecorder(void) {  #ifdef _WIN32  	const uchar *insns = (const uchar *)orig_stop_cb; -	// The "stop" command calls the virtual function demorecorder.IsRecording(), -	// so just look for the load of the "this" pointer into ECX +	// The stop command loads `demorecorder` into ECX to call IsRecording()  	for (const uchar *p = insns; p - insns < 32;) {  		if (p[0] == X86_MOVRMW && p[1] == X86_MODRM(0, 1, 5)) {  			void **indirect = mem_loadptr(p + 2);  			demorecorder = *indirect;  			return true;  		} -		NEXT_INSN(p, "demorecorder object"); +		NEXT_INSN(p, "global demorecorder object");  	}  #else  #warning TODO(linux): implement linux equivalent (cdecl!) @@ -185,11 +179,9 @@ static inline bool find_demorecorder(void) {  	return false;  } -// This finds "m_bRecording" and "m_nDemoNumber" using the pointer to the -// original "StopRecording" demorecorder function. -static inline bool find_recmembers(void *stoprecording) { +static inline bool find_recmembers(void *StopRecording) {  #ifdef _WIN32 -	const uchar *insns = (uchar *)stoprecording; +	const uchar *insns = (uchar *)StopRecording;  	for (const uchar *p = insns; p - insns < 128;) {  		// m_nDemoNumber = 0 -> mov dword ptr [<reg> + off], 0  		// XXX: might end up wanting constants for the MRM field masks? @@ -210,15 +202,12 @@ static inline bool find_recmembers(void *stoprecording) {  	return false;  } -// This finds "m_szDemoBaseName" using the pointer to the original -// "StartRecording" demorecorder function. -static inline bool find_demoname(void *startrecording) { +static inline bool find_demoname(void *StartRecording) {  #ifdef _WIN32 -	const uchar *insns = (uchar *)startrecording; +	const uchar *insns = (uchar *)StartRecording;  	for (const uchar *p = insns; p - insns < 32;) { -		// the function immediately calls Q_strncpy and copies into a buffer -		// offset from `this` - look for a LEA instruction some time *before* -		// the first call takes place +		// the function immediately does a Q_strncpy() into a buffer offset from +		// `this` - look for a LEA some time *before* the first call instruction  		if (p[0] == X86_CALL) return false;  		if (p[0] == X86_LEA && (p[1] & 0xC0) == 0x80) {  			demorec_basename = mem_offset(demorecorder, mem_load32(p + 2)); @@ -235,9 +224,8 @@ static inline bool find_demoname(void *startrecording) {  bool demorec_start(const char *name) {  	bool was = *recording;  	if (was) return false; -	// easiest way to do this, though dumb, is to just call the record command -	// callback that we already have a hold of. note: this args object is very -	// incomplete, but is enough to make the command work +	// dumb but easy way to do this: call the record command callback. note: +	// this args object is very incomplete by enough to make the command work  	struct con_cmdargs args = {.argc = 2, .argv = {0, name, 0}};  	orig_record_cb(&args);  	if (!was && *recording) *demonum = 0; // same logic as in the hook diff --git a/src/dictmaptree.h b/src/dictmaptree.h index 8a354fe..190683c 100644 --- a/src/dictmaptree.h +++ b/src/dictmaptree.h @@ -22,7 +22,7 @@  /*   * Valve's dict/map/tree structures come in various shapes and sizes, so here we - * do the generic macro thing for future proofing. For now we just define a + * do the generic macro thing for future-proofing. For now we just define a   * CUtlDict (map with string keys) of pointers, with ushort indices, which is   * sufficient for server entity factory lookup, and probably some other stuff.   * Functions for actually modifying the dicts/maps/trees aren't implemented. diff --git a/src/engineapi.c b/src/engineapi.c index d4ee742..831836b 100644 --- a/src/engineapi.c +++ b/src/engineapi.c @@ -1,5 +1,5 @@  /* - * Copyright © 2022 Michael Smith <mikesmiffy128@gmail.com> + * Copyright © 2023 Michael Smith <mikesmiffy128@gmail.com>   * Copyright © 2023 Willian Henrique <wsimanbrazil@yahoo.com.br>   *   * Permission to use, copy, modify, and/or distribute this software for any @@ -37,15 +37,14 @@ ifacefactory factory_client = 0, factory_server = 0, factory_engine = 0,  struct VEngineClient *engclient;  struct VEngineServer *engserver; -// this seems to be very stable, thank goodness -DECL_VFUNC(void *, GetGlobalVars, 1) +DECL_VFUNC(void *, GetGlobalVars, 1) // seems to be very stable, thank goodness  void *globalvars;  void *inputsystem, *vgui;  DECL_VFUNC_DYN(void *, GetAllServerClasses) -#include <entpropsinit.gen.h> +#include <entpropsinit.gen.h> // generated by build/mkentprops.c  bool engineapi_init(int pluginver) {  	if (!con_detect(pluginver)) return false; @@ -101,8 +100,6 @@ bool engineapi_init(int pluginver) {  		}  	} -	// need to do this now; ServerClass network table iteration requires -	// SendProp offsets  	gamedata_init();  	con_init();  	if (!gameinfo_init()) { con_disconnect(); return false; } @@ -69,10 +69,10 @@ static struct CEntityFactoryDictionary *entfactorydict = 0;  static inline bool find_entfactorydict(con_cmdcb dumpentityfactories_cb) {  	const uchar *insns = (const uchar *)dumpentityfactories_cb;  	for (const uchar *p = insns; p - insns < 64;) { -		// the call to EntityFactoryDictionary() is inlined. that returns a -		// static, which is lazy-inited (trivia: this was old MSVC, so it's not -		// threadsafe like C++ requires nowadays). for some reason the init flag -		// is set using OR, and then the instance is put in ECX to call the ctor +		// EntityFactoryDictionary() is inlined, and returns a static, which is +		// lazy-inited (trivia: this was old MSVC, so it's not thread-safe like +		// C++ requires nowadays). for some reason the init flag is set using +		// OR, and then the instance is put in ECX to call the ctor  		if (p[0] == X86_ORMRW && p[6] == X86_MOVECXI && p[11] == X86_CALL) {  			entfactorydict = mem_loadptr(p + 7);  			return true; diff --git a/src/fixes.c b/src/fixes.c index 29d5ab3..b2eaf91 100644 --- a/src/fixes.c +++ b/src/fixes.c @@ -106,7 +106,7 @@ static void l4d2specific(void) {  	// this out. Good meme 8/10.  	unhide("sv_hosting_lobby"); -	// Older versions of L4D2 reset mat_queue_mode to -1 (multicore rendering +	// Older versions of L4D2 reset mat_queue_mode to 0 (multicore rendering  	// off) all the time if gpu_level is 0 (low shader detail), causing lag that  	// can only be fixed by manually fixing the setting in video settings. Newer  	// versions work around this by marking it as ARCHIVE, *breaking* the code @@ -169,6 +169,7 @@ static void l4d1specific(void) {  	// these hidden variables to 0 gets rid of it.  	struct con_var *v = con_findvar("ui_l4d_debug");  	if (v) con_setvari(v, 0); +	// FIXME: this is borked with deferred init. Does gameui load SUPER late?  	v = con_findvar("mm_l4d_debug");  	if (v) con_setvari(v, 0); @@ -44,8 +44,7 @@ static struct con_var *real_fov_desired; // engine's if it has it, or ours  typedef void (*VCALLCONV SetDefaultFOV_func)(void *, int);  static SetDefaultFOV_func orig_SetDefaultFOV;  static void VCALLCONV hook_SetDefaultFOV(void *this, int fov) { -	// the game normally clamps fov_desired on the server side, disregard -	// whatever it tries to set and force our own value instead +	// disregard server-side clamped value and force our own value instead  	orig_SetDefaultFOV(this, con_getvari(real_fov_desired));  } @@ -53,15 +52,14 @@ static bool find_SetDefaultFOV(struct con_cmd *fov) {  	const uchar *insns = (const uchar *)fov->cb;  	int callcnt = 0;  	for (const uchar *p = insns; p - insns < 96;) { -		// fov command source, and consequent asm, calls 4 functions, one of -		// them virtual (i.e. via register). of the 3 direct calls, -		// SetDefaultFOV is the third. +		// The fov command calls 4 functions, one of them virtual. Of the 3 +		// direct calls, SetDefaultFOV() is the third.  		if (p[0] == X86_CALL && ++callcnt == 3) {  			orig_SetDefaultFOV = (SetDefaultFOV_func)(p + 5 +  					mem_loadoffset(p + 1));  			return true;  		} -		NEXT_INSN(p, "SetDefaultFOV"); +		NEXT_INSN(p, "SetDefaultFOV function");  	}  	return false;  } diff --git a/src/gamedata.c b/src/gamedata.c index 639b2f8..78eb620 100644 --- a/src/gamedata.c +++ b/src/gamedata.c @@ -1,5 +1,5 @@  /* - * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * Copyright © 2023 Michael Smith <mikesmiffy128@gmail.com>   *   * Permission to use, copy, modify, and/or distribute this software for any   * purpose with or without fee is hereby granted, provided that the above @@ -24,6 +24,6 @@  #define NVDTOR 2  #endif -#include <gamedatainit.gen.h> +#include <gamedatainit.gen.h> // generated by build/mkgamedata.c  // vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/gamedata.h b/src/gamedata.h index 471e5bb..2e58ef6 100644 --- a/src/gamedata.h +++ b/src/gamedata.h @@ -1,5 +1,5 @@  /* - * Copyright © 2022 Michael Smith <mikesmiffy128@gmail.com> + * Copyright © 2023 Michael Smith <mikesmiffy128@gmail.com>   *   * Permission to use, copy, modify, and/or distribute this software for any   * purpose with or without fee is hereby granted, provided that the above @@ -22,11 +22,11 @@  #else  #define NVDTOR 2  #endif -#include <gamedata.gen.h> -// entprops are built by a different tool, in a different header for simplicity -#include <entprops.gen.h> +#include <gamedata.gen.h> // generated by build/mkgamedata.c +#include <entprops.gen.h> // generated by build/mkentprops.c  #undef NVDTOR +/* Called as part of plugin init to set up various metadata about the game. */  void gamedata_init(void);  #endif diff --git a/src/gameinfo.c b/src/gameinfo.c index d04ae0f..71922a6 100644 --- a/src/gameinfo.c +++ b/src/gameinfo.c @@ -39,7 +39,10 @@  #define PATHSEP "/"  #endif -// TODO(opt): get rid of the rest of the snprintf and strcpy, some day +// ~~TODO(opt): get rid of the rest of the snprintf and strcpy, some day~~ +// TODO(opt): remove almost all this parsing nonsense, it's not needed any more! +// We can simply GetWindowText (and do a little more work on Linux...) and do +// away with absolute paths to DLLs which won't be required with deferred init.  static os_char bindir[PATH_MAX] = {0};  #ifdef _WIN32 @@ -47,9 +47,8 @@ bool hook_init(void) {  void *hook_inline(void *func_, void *target) {  	uchar *func = func_; -	// dumb hack: rather than correcting jmp offsets and having to painstakingly -	// track them all, just look for the underlying thing being jmp-ed to and -	// hook _that_. +	// dumb hack: if we hit some thunk that immediately jumps elsewhere (which +	// seems common for win32 API functions), hook the underlying thing instead.  	while (*func == X86_JMPIW) func += mem_loadoffset(func + 1) + 5;  	if (!os_mprot(func, 5, PAGE_EXECUTE_READWRITE)) return false;  	int len = 0; @@ -76,6 +75,7 @@ void *hook_inline(void *func_, void *target) {  	}  	// for simplicity, just bump alloc the trampoline. no need to free anyway  	if (nexttrampoline - trampolines > sizeof(trampolines) - len - 6) goto nosp; +	// TODO(opt): stop pretending to be thread-safe, it's just slowing us down  	uchar *trampoline = (uchar *)InterlockedExchangeAdd(  			(volatile long *)&nexttrampoline, len + 6);  	// avoid TOCTOU diff --git a/src/kvsys.c b/src/kvsys.c index 8bb140e..25344da 100644 --- a/src/kvsys.c +++ b/src/kvsys.c @@ -60,8 +60,7 @@ void kvsys_free(struct KeyValues *kv) {  // HACK: later versions of L4D2 show an annoying dialog on every plugin_load.  // We can suppress this by catching the message string that's passed from  // engine.dll to gameui.dll through KeyValuesSystem in vstdlib.dll and just -// replacing it with some other arbitrary garbage string. This makes gameui fail -// to match the message and thus do nothing. :) +// replacing it with some other arbitrary string that gameui won't match.  static GetStringForSymbol_func orig_GetStringForSymbol = 0;  static const char *VCALLCONV hook_GetStringForSymbol(void *this, int s) {  	const char *ret = orig_GetStringForSymbol(this, s); diff --git a/src/l4dmm.c b/src/l4dmm.c index 8394038..3c7a07c 100644 --- a/src/l4dmm.c +++ b/src/l4dmm.c @@ -95,7 +95,7 @@ const char *l4dmm_curcampaign(void) {  }  INIT { -	// ugh, we NEED to centralise the library stuff at some point, this sucks +	// XXX: ugh, we NEED to centralise library stuff at some point, this sucks  #ifdef _WIN32  	void *mmlib = GetModuleHandleW(L"matchmaking.dll");  #else diff --git a/src/l4dreset.c b/src/l4dreset.c index 4bffa3c..2245a16 100644 --- a/src/l4dreset.c +++ b/src/l4dreset.c @@ -44,7 +44,7 @@ static int off_callerrecords = -1;  static int off_voteissues;  // Note: the vote callers vector contains these as elements. We don't currently -// do anything with the structure, but keeping it here for reference. +// do anything with the structure, but we're keeping it here for reference.  /*struct CallerRecord {  	u32 steamid_trunc;  	float last_time; @@ -78,8 +78,8 @@ static void reset(void) {  	// reset the vote cooldowns if possible (will skip L4D1). only necessary on  	// versions >2045 and on map 1, but it's easiest to do unconditionally  	if (off_callerrecords != -1) { -		// Basically equivalent to CUtlVector::RemoveAll. The elements have no -		// destructors to call. The resulting state is as if nobody has voted. +		// This is equivalent to CUtlVector::RemoveAll() as there's no +		// destructors to call. The result as is if nobody had ever voted.  		struct CUtlVector *recordvector = mem_offset(*votecontroller,  				off_callerrecords);  		recordvector->sz = 0; @@ -116,8 +116,7 @@ DEF_CCMD_HERE_UNREG(sst_l4d_quickreset,  PREINIT { return GAMETYPE_MATCHES(L4D); } -// This finds the g_voteController variable using the listissues callback, and -// returns a pointer to the rest of the bytes for find_voteissues() below +// Note: this returns a pointer to subsequent bytes for find_voteissues() below  static inline const uchar *find_votecontroller(con_cmdcbv1 listissues_cb) {  	const uchar *insns = (const uchar *)listissues_cb;  #ifdef _WIN32 @@ -136,8 +135,6 @@ static inline const uchar *find_votecontroller(con_cmdcbv1 listissues_cb) {  	return 0;  } -// This finds ListIssues() using the instruction pointer returned by -// find_votecontroller() above, and then uses that to find the vote issue list.  static inline bool find_voteissues(const uchar *insns) {  #ifdef _WIN32  	for (const uchar *p = insns; p - insns < 16;) { @@ -150,10 +147,9 @@ static inline bool find_voteissues(const uchar *insns) {  	}  	return false;  ok:	for (const uchar *p = insns; p - insns < 96;) { -		// There's a virtual call on each actual CVoteIssue in the loop over the -		// list. That entails putting the issue pointer in ECX, which involves -		// loading that pointer from the vector, which exists at an offset from -		// `this`, meaning we can find the offset from the mov into ECX. +		// The loop in ListIssues() calls a member function on each CVoteIssue. +		// Each pointer is loaded from a CUtlVector at an offset from `this`, so +		// we can find that offset from the mov into ECX.  		if (p[0] == X86_MOVRMW && (p[1] & 0xF8) == 0x88) {  			int off = mem_loadoffset(p + 2);  			if (off > 800) { // sanity check: offset is always fairly high @@ -161,9 +157,8 @@ ok:	for (const uchar *p = insns; p - insns < 96;) {  				return true;  			}  		} -		// Further complication: at least in 2045 there's a short jmp over some -		// invalid instruction bytes. I guess there's no reason to ever expect -		// something interesting after an unconditional jmp, so just follow it. +		// Complication: at least 2045 has a short jmp over some garbage bytes. +		// Follow that jmp since there's nothing interesting before the target.  		if (p[0] == X86_JMPI8) {  			p += 2 + ((s8 *)p)[1];  			continue; @@ -176,15 +171,12 @@ ok:	for (const uchar *p = insns; p - insns < 96;) {  	return false;  } -// This finds the caller record vector using a pointer to the -// CVoteController::Spawn function  static inline bool find_votecallers(void *votectrlspawn) {  #ifdef _WIN32  	const uchar *insns = (const uchar *)votectrlspawn;  	for (const uchar *p = insns; p - insns < 64;) { -		// Unsure what the member on this offset actually is (the game seems to -		// want it to be set to 0 to allow votes to happen), but the vector we -		// want seems to consistently be 8 bytes after whatever this is +		// Unsure what this offset points at (it seems to have to be 0 for votes +		// to happen), but the vector of interest always comes 8 bytes later.  		// "mov dword ptr [<reg> + off], 0", mod == 0b11  		if (p[0] == X86_MOVMIW && (p[1] & 0xC0) == 0x80 &&  				mem_load32(p + 6) == 0) { @@ -215,12 +207,11 @@ INIT {  		errmsg_errorx("couldn't find vote issues list offset\n");  		return false;  	} -	// only bother with vote cooldown stuff for L4D2, since all versions of L4D1 -	// have unlimited votes anyway. NOTE: assuming L4D2 always has Spawn in -	// gamedata (which has no reason to stop being true...) +	// Only try cooldown stuff for L4D2, since L4D1 always had unlimited votes. +	// NOTE: assuming L4D2 always has Spawn in gamedata (why wouldn't it?)  	if (GAMETYPE_MATCHES(L4D2)) { -		// g_voteController may have not been initialized yet so we get the -		// vtable from the ent factory +		// g_voteController is invalid if not running a server so get the +		// vtable by inspecting the ent factory code instead  		const struct CEntityFactory *factory = ent_getfactory("vote_controller");  		if (!factory) {  			errmsg_errorx("couldn't find vote controller entity factory"); diff --git a/src/l4dwarp.c b/src/l4dwarp.c index 75c762c..24c3873 100644 --- a/src/l4dwarp.c +++ b/src/l4dwarp.c @@ -1,5 +1,5 @@  /* - * Copyright © 2022 Michael Smith <mikesmiffy128@gmail.com> + * Copyright © 2023 Michael Smith <mikesmiffy128@gmail.com>   *   * Permission to use, copy, modify, and/or distribute this software for any   * purpose with or without fee is hereby granted, provided that the above @@ -45,9 +45,7 @@ DEF_CCMD_HERE_UNREG(sst_l4d_testwarp, "Simulate a bot warping to you",  	void *e = GetBaseEntity(ed->ent_unknown); // is this call required?  	struct vec3f *org = mem_offset(e, off_entpos);  	struct vec3f *ang = mem_offset(e, off_eyeang); -	// L4D idle warps go up to 10 units behind relative to whatever angle the -	// player is facing, lessening the distance based on pitch angle but never -	// displacing vertically +	// L4D idle warps go up to 10 units behind yaw, lessening based on pitch.  	float pitch = ang->x * M_PI / 180, yaw = ang->y * M_PI / 180;  	float shift = -10 * cos(pitch);  	Teleport(e, &(struct vec3f){org->x + shift * cos(yaw), @@ -19,7 +19,7 @@  #include "intdefs.h" -/* retrieves a 32-bit integer from an unaligned pointer */ +/* Retrieves a 32-bit integer from an unaligned pointer. */  static inline u32 mem_load32(const void *p) {  	// XXX: Turns out the pedantically-safe approach below causes most compilers  	// to generate horribly braindead x86 output in at least some cases (and the @@ -31,13 +31,13 @@ static inline u32 mem_load32(const void *p) {  	//return (u32)cp[0] | (u32)cp[1] << 8 | (u32)cp[2] << 16 | (u32)cp[3] << 24;  } -/* retrieves a 64-bit integer from an unaligned pointer */ +/* Retrieves a 64-bit integer from an unaligned pointer. */  static inline u64 mem_load64(const void *p) {  	// this seems not to get butchered as badly in most cases?  	return (u64)mem_load32(p) | (u64)mem_load32((uchar *)p + 4) << 32;  } -/* retrieves a pointer from an unaligned pointer-to-pointer */ +/* Retrieves a pointer from an unaligned pointer-to-pointer. */  static inline void *mem_loadptr(const void *p) {  #if defined(_WIN64) || defined(__x86_64__)  	return (void *)mem_load64(p); @@ -46,15 +46,15 @@ static inline void *mem_loadptr(const void *p) {  #endif  } -/* retreives a signed offset from an unaligned pointer */ +/* Retreives a signed offset from an unaligned pointer. */  static inline ssize mem_loadoffset(const void *p) {  	return (ssize)mem_loadptr(p);  } -/* adds a byte count to a pointer and returns a freely-assignable void pointer */ +/* Adds a byte count to a pointer and returns a freely-assignable pointer. */  static inline void *mem_offset(void *p, int off) { return (char *)p + off; } -/* returns the offset in bytes from one pointer to another (p - q) */ +/* Returns the offset in bytes from one pointer to another (p - q). */  static inline ssize mem_diff(const void *p, const void *q) {  	return (char *)p - (char *)q;  } diff --git a/src/nomute.c b/src/nomute.c index 30f93c3..93cfcd2 100644 --- a/src/nomute.c +++ b/src/nomute.c @@ -1,5 +1,6 @@  /*   * Copyright © 2023 Willian Henrique <wsimanbrazil@yahoo.com.br> + * Copyright © 2023 Michael Smith <mikesmiffy128@gmail.com>   *   * Permission to use, copy, modify, and/or distribute this software for any   * purpose with or without fee is hereby granted, provided that the above @@ -37,10 +38,9 @@ static IDirectSoundVtbl *ds_vt = 0;  static typeof(ds_vt->CreateSoundBuffer) orig_CreateSoundBuffer;  static con_cmdcbv1 snd_restart_cb = 0; -// early init (via VDF) happens before config is loaded, and audio is set up -// after that, so we don't want to run snd_restart the first time the cvar is -// set, unless we were loaded later with plugin_load, in which case audio is -// already up and running and we'll want to restart it every time +// early init via VDF happens before config is loaded and audio is set up after +// that, so we don't want to run snd_restart the first time the cvar is set, +// unless we were loaded later with plugin_load in which case we actually do.  static bool skiprestart;  static void losefocuscb(struct con_var *v) {  	if (!skiprestart) snd_restart_cb(); diff --git a/src/os-win32.h b/src/os-win32.h index 365e2dc..fe61f84 100644 --- a/src/os-win32.h +++ b/src/os-win32.h @@ -98,6 +98,9 @@ static inline void os_randombytes(void *buf, int sz) {  #define fdopen _fdopen  #define dup _dup  #define dup2 _dup2 +#define strdup _strdup +#define fstat _fstat64 +#define lseek _lseeki64  #define O_RDONLY _O_RDONLY  #define O_RDWR _O_RDWR  #define O_CLOEXEC _O_NOINHERIT // and why did they rename this!? @@ -170,11 +170,10 @@ static const char *updatenotes = "\  * various internal cleanup\n\  "; -#include <featureinit.gen.h> +#include <featureinit.gen.h> // generated by build/codegen.c  static void do_featureinit(void) {  	initfeatures(); -	fixes_apply();  	// if we're autoloaded and the external autoupdate script downloaded a new  	// version, let the user know about the cool new stuff! @@ -200,25 +199,20 @@ static VGuiConnect_func orig_VGuiConnect;  static void VCALLCONV hook_VGuiConnect(void *this) {  	orig_VGuiConnect(this);  	do_featureinit(); -	unhook_vtable(*(void ***)vgui, vtidx_VGuiConnect, -			(void *)orig_VGuiConnect); +	fixes_apply(); +	unhook_vtable(*(void ***)vgui, vtidx_VGuiConnect, (void *)orig_VGuiConnect);  }  DECL_VFUNC_DYN(bool, VGuiIsInitialized)  // --- Magical deferred load order hack nonsense! --- -// The engine loads VDF plugins basically right after server.dll, but long -// before most other stuff, which makes hooking certain other stuff a pain. We -// still want to be able to load via VDF as it's the only reasonable way to get -// in before config.cfg, which is needed for any kind of configuration to work -// correctly. -// -// So here, we hook CEngineVGui::Connect() which is one the very last things -// to get called on startup, and use that hook to defer feature init till after -// most of the rest of the game is up and running. That allows us to touch -// pretty much any engine stuff without worrying about load order nonsense. +// VDF plugins load right after server.dll, but long before most other stuff. We +// want to be able to load via VDF so archived cvars in config.cfg can get set, +// but don't want to be so early that most of the game's interfaces haven't been +// brought up yet. Hook CEngineVGui::Connect(), which is called very late in +// startup, in order to init the features properly.  // -// Route credit to Bill for helping figure a lot of this out - mike +// Route credit to bill for helping figure a lot of this out - mike  static bool deferinit(void) {  	if (!vgui) {  		errmsg_warnx("can't use VEngineVGui for deferred feature setup"); @@ -300,7 +294,7 @@ static bool do_load(ifacefactory enginef, ifacefactory serverf) {  	*p++ = (void *)&nop_p_v;		  // OnEdictAllocated  	*p   = (void *)&nop_p_v;		  // OnEdictFreed -	if (!deferinit()) do_featureinit(); +	if (!deferinit()) { do_featureinit(); fixes_apply(); }  	return true;  } @@ -428,6 +422,6 @@ EXPORT const void *CreateInterface(const char *name, int *ret) {  }  // no better place to put this lol -#include <evglue.gen.h> +#include <evglue.gen.h> // generated by src/build/codegen.c  // vi: sw=4 ts=4 noet tw=80 cc=80  | 
