From 39725b79404acb6a020b3653b59df3e7bf598e00 Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Sun, 26 Dec 2021 23:58:23 +0000 Subject: Block the plugin nag dialog in newer L4D2 builds --- src/sst.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/src/sst.c b/src/sst.c index 6c60cf3..d13ba5d 100644 --- a/src/sst.c +++ b/src/sst.c @@ -15,6 +15,7 @@ */ #include +#include #include "con_.h" #include "demorec.h" @@ -66,6 +67,20 @@ ifacefactory factory_client = 0, factory_server = 0, factory_engine = 0; // plonking ~~some bools~~ one bool here and worrying about it later. :^) static bool has_demorec = false; +// 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. :) +static void **kvsvt; +typedef const char *(*VCALLCONV GetStringForSymbol_func)(void *this, int s); +static GetStringForSymbol_func orig_GetStringForSymbol = 0; +static const char *VCALLCONV GetStringForSymbol_hook(void *this, int s) { + const char *ret = orig_GetStringForSymbol(this, s); + if (!strcmp(ret, "OnClientPluginWarning")) ret = "sstBlockedThisEvent"; + return ret; +} + static bool do_load(ifacefactory enginef, ifacefactory serverf) { factory_engine = enginef; factory_server = serverf; #ifndef __linux__ @@ -111,7 +126,37 @@ nc: gamedata_init(); has_demorec = demorec_init(); fixes_apply(); - con_colourmsg(RGBA(64, 255, 64, 255), + // NOTE: this is technically redundant for early versions but I CBA writing + // a version check; it's easier to just do this unilaterally. + if (GAMETYPE_MATCHES(L4D2)) { +#ifdef _WIN32 + // XXX: not sure if vstdlib should be done dynamically like this or just + // another stub like tier0? + void *vstdlib = GetModuleHandleW(L"vstdlib.dll"); + if (!vstdlib) { + con_warn("sst: warning: couldn't get vstdlib, won't be able to " + "prevent nag message\n"); + goto e; + } + void *(*KeyValuesSystem)(void) = (void *(*)(void))os_dlsym(vstdlib, + "KeyValuesSystem"); + if (KeyValuesSystem) { + void *kvs = KeyValuesSystem(); + kvsvt = *(void ***)kvs; + if (!os_mprot(kvsvt + 4, sizeof(void *), PAGE_READWRITE)) { + con_warn("sst: warning: couldn't unprotect KeyValuesSystem " + "vtable; won't be able to prevent nag message\n"); + goto e; + } + orig_GetStringForSymbol = (GetStringForSymbol_func)hook_vtable( + kvsvt, 4, (void *)GetStringForSymbol_hook); + } +#else +#warning TODO(linux) suitably abstract this stuff to Linux! +#endif + } + +e: con_colourmsg(RGBA(64, 255, 64, 255), NAME " v" VERSION " successfully loaded"); con_colourmsg(RGBA(255, 255, 255, 255), " for game "); con_colourmsg(RGBA(0, 255, 255, 255), "%s\n", gameinfo_title); @@ -127,6 +172,10 @@ static void do_unload(void) { if (clientlib) dlclose(clientlib); #endif con_disconnect(); + + if (orig_GetStringForSymbol) { + unhook_vtable(kvsvt, 4, (void *)orig_GetStringForSymbol); + } } // since this is static/global, it only becomes false again when the plugin SO -- cgit v1.2.3