From 1c4318331663b152b0b298bd2c9e5c971506a86b Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Sat, 10 Jun 2023 16:44:19 +0100 Subject: Prune some comments and tidy up other minor things --- compile.bat | 11 ++++++++--- src/ac.c | 3 +-- src/alias.c | 1 - src/alias.h | 5 ++++- src/bind.c | 3 +-- src/bitbuf.h | 5 ++--- src/con_.c | 50 +++++++++++++++----------------------------------- src/con_.h | 5 +---- src/democustom.c | 7 +------ src/demorec.c | 32 ++++++++++---------------------- src/dictmaptree.h | 2 +- src/engineapi.c | 9 +++------ src/ent.c | 8 ++++---- src/fixes.c | 3 ++- src/fov.c | 10 ++++------ src/gamedata.c | 4 ++-- src/gamedata.h | 8 ++++---- src/gameinfo.c | 5 ++++- src/hook.c | 6 +++--- src/kvsys.c | 3 +-- src/l4dmm.c | 2 +- src/l4dreset.c | 39 +++++++++++++++------------------------ src/l4dwarp.c | 6 ++---- src/mem.h | 12 ++++++------ src/nomute.c | 8 ++++---- src/os-win32.h | 3 +++ src/sst.c | 28 +++++++++++----------------- 27 files changed, 113 insertions(+), 165 deletions(-) diff --git a/compile.bat b/compile.bat index 660ebe7..17dd919 100644 --- a/compile.bat +++ b/compile.bat @@ -17,9 +17,12 @@ set warnings=-Wall -pedantic -Wno-parentheses -Wno-missing-braces ^ -Wno-gnu-zero-variadic-macro-arguments set dbg=0 +:: XXX: -Og would be nice but apparently a bunch of stuff still gets inlined +:: which can be somewhat annoying so -O0 it is. Still using -Og in the linux +:: script; will need to investigate when linux is actually a thing later. if "%dbg%"=="1" ( - set cflags=-Og -g - set ldflags=-Og -g + set cflags=-O0 -g3 + set ldflags=-O0 -g3 ) else ( set cflags=-O2 set ldflags=-O2 @@ -99,8 +102,8 @@ llvm-rc /FO .build\dll.res src\dll.rc || exit /b %CC% -shared -O0 -w -o .build/tier0.dll src/stubs/tier0.c %CC% -shared -O0 -w -o .build/vstdlib.dll src/stubs/vstdlib.c for %%b in (%src%) do ( call :cc %%b || exit /b ) +:: we need different library names for debugging because Microsoft. if "%dbg%"=="1" ( - :: ugh, microsoft. set clibs=-lmsvcrtd -lvcruntimed -lucrtd ) else ( set clibs=-lmsvcrt -lvcruntime -lucrt @@ -113,6 +116,8 @@ del .build\sst.lib %HOSTCC% -O2 -g -include test/test.h -o .build/bitbuf.test.exe test/bitbuf.test.c || exit /b .build\bitbuf.test.exe || exit /b +%HOSTCC% -O2 -g -include test/test.h -o .build/crc32.test.exe test/crc32.test.c || exit /b +.build\crc32.test.exe || exit /b :: special case: test must be 32-bit %HOSTCC% -m32 -O2 -g -ladvapi32 -include test/test.h -o .build/hook.test.exe test/hook.test.c || exit /b .build\hook.test.exe || exit /b diff --git a/src/ac.c b/src/ac.c index c379326..605a2be 100644 --- a/src/ac.c +++ b/src/ac.c @@ -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 ; mov , 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 diff --git a/src/bind.c b/src/bind.c index 97864c8..37a34ea 100644 --- a/src/bind.c +++ b/src/bind.c @@ -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 , dword ptr [ * 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 + * Copyright © 2023 Michael Smith * * 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; } diff --git a/src/con_.c b/src/con_.c index eafc242..6ae1738 100644 --- a/src/con_.c +++ b/src/con_.c @@ -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 +#include // 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;; diff --git a/src/con_.h b/src/con_.h index dcba4d9..9e96741 100644 --- a/src/con_.h +++ b/src/con_.h @@ -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 [ + 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 + * Copyright © 2023 Michael Smith * Copyright © 2023 Willian Henrique * * 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 +#include // 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; } diff --git a/src/ent.c b/src/ent.c index 8dcb1f1..6329152 100644 --- a/src/ent.c +++ b/src/ent.c @@ -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); diff --git a/src/fov.c b/src/fov.c index 236182a..1886c1f 100644 --- a/src/fov.c +++ b/src/fov.c @@ -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 + * Copyright © 2023 Michael Smith * * 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 +#include // 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 + * Copyright © 2023 Michael Smith * * 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 -// entprops are built by a different tool, in a different header for simplicity -#include +#include // generated by build/mkgamedata.c +#include // 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 diff --git a/src/hook.c b/src/hook.c index ff7ac96..3d6c14d 100644 --- a/src/hook.c +++ b/src/hook.c @@ -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 [ + 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 + * Copyright © 2023 Michael Smith * * 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), diff --git a/src/mem.h b/src/mem.h index 97dfa91..367ed5b 100644 --- a/src/mem.h +++ b/src/mem.h @@ -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 + * Copyright © 2023 Michael Smith * * 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!? diff --git a/src/sst.c b/src/sst.c index a0f926d..afe7eb3 100644 --- a/src/sst.c +++ b/src/sst.c @@ -170,11 +170,10 @@ static const char *updatenotes = "\ * various internal cleanup\n\ "; -#include +#include // 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 +#include // generated by src/build/codegen.c // vi: sw=4 ts=4 noet tw=80 cc=80 -- cgit v1.2.3