summaryrefslogtreecommitdiffhomepage
path: root/src/kvsys.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/kvsys.c')
-rw-r--r--src/kvsys.c52
1 files changed, 43 insertions, 9 deletions
diff --git a/src/kvsys.c b/src/kvsys.c
index 25344da..1d09b0f 100644
--- a/src/kvsys.c
+++ b/src/kvsys.c
@@ -1,5 +1,6 @@
/*
* Copyright © 2023 Michael Smith <mikesmiffy128@gmail.com>
+ * Copyright © 2024 Willian Henrique <wsimanbrazil@yahoo.com.br>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -25,29 +26,35 @@
#include "mem.h"
#include "os.h"
#include "vcall.h"
+#include "x86.h"
FEATURE()
IMPORT void *KeyValuesSystem(void); // vstlib symbol
static void *kvs;
-DECL_VFUNC(int, GetSymbolForString, 3, const char *, bool)
-DECL_VFUNC(const char *, GetStringForSymbol, 4, int)
+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)
const char *kvsys_symtostr(int sym) { return GetStringForSymbol(kvs, sym); }
int kvsys_strtosym(const char *s) { return GetSymbolForString(kvs, s, true); }
struct KeyValues *kvsys_getsubkey(struct KeyValues *kv, int sym) {
- for (kv = kv->child; kv; kv = kv->next) if (kv->keyname == sym) return kv;
+ for (kv = iskvv2 ? kv->v2.child : kv->v1.child; kv;
+ kv = iskvv2 ? kv->v2.next : kv->v1.next) {
+ if (kv->keyname == sym) return kv;
+ }
return 0;
}
// this is trivial for now, but may need expansion later; see header comment
-const char *kvsys_getstrval(struct KeyValues *kv) { return kv->strval; }
+const char *kvsys_getstrval(const struct KeyValues *kv) { return kv->strval; }
void kvsys_free(struct KeyValues *kv) {
while (kv) {
- kvsys_free(kv->child);
- struct KeyValues *next = kv->next;
+ kvsys_free(iskvv2 ? kv->v2.child : kv->v1.child);
+ struct KeyValues *next = iskvv2 ? kv->v2.next : kv->v1.next;
// NOTE! could (should?) call the free function in IKeyValuesSystem but
// we instead assume pooling is compiled out in favour of the IMemAlloc
// stuff, and thus call the latter directly for less overhead
@@ -68,12 +75,38 @@ static const char *VCALLCONV hook_GetStringForSymbol(void *this, int s) {
return ret;
}
+// XXX: 5th instance of this in the codebase, should REALLY tidy up soon
+#ifdef _WIN32
+#define NVDTOR 1
+#else
+#define NVDTOR 2
+#endif
+
+static void detectabichange(void **kvsvt) {
+ // When no virtual destructor is present, the 6th function in the KVS vtable
+ // is AddKeyValuesToMemoryLeakList, which is a nop in release builds.
+ // L4D2 2.2.3.8 (14th May 2024) adds a virtual destructor which pushes
+ // everything down, so the 6th function is not a nop any more. This
+ // coincides with changes to the KeyValues struct layout (see above).
+ uchar *insns = kvsvt[5];
+ // should be RETI16 on Windows (thiscall, callee cleanup) and RET on Linux
+ // (cdecl, caller cleanup) but let's just check both to be thorough
+ if (insns[0] != X86_RETI16 && insns[0] != X86_RET) {
+ iskvv2 = true;
+ vtidx_GetSymbolForString += NVDTOR;
+ vtidx_GetStringForSymbol += NVDTOR;
+ }
+}
+
INIT {
kvs = KeyValuesSystem();
- // NOTE: this is technically redundant for early versions but I CBA writing
- // a version check; it's easier to just do this unilaterally.
+ // NOTE: this hook is technically redundant for early versions but I CBA
+ // writing a version check; it's easier to just do this unilaterally. The
+ // 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 (!os_mprot(kvsvt + vtidx_GetStringForSymbol, sizeof(void *),
PAGE_READWRITE)) {
errmsg_warnx("couldn't make KeyValuesSystem vtable writable");
@@ -90,7 +123,8 @@ INIT {
END {
if (orig_GetStringForSymbol) {
- unhook_vtable(*(void ***)kvs, 4, (void *)orig_GetStringForSymbol);
+ unhook_vtable(*(void ***)kvs, vtidx_GetStringForSymbol,
+ (void *)orig_GetStringForSymbol);
}
}