summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorWillian Henrique <wsimanbrazil@yahoo.com.br>2024-05-19 17:14:55 -0300
committerMichael Smith <mikesmiffy128@gmail.com>2024-05-21 21:09:44 +0100
commit2d0ac38fa0e023d29b32a867240df7fc38c5afaa (patch)
tree4a30cb789d2cb84be90c635bcbb1feb9b98975d4 /src
parent2796e6dabeed57d31069a6c56683cf3e66ab88b3 (diff)
Fix compatibility with latest L4D2 update
Diffstat (limited to 'src')
-rw-r--r--src/kvsys.c52
-rw-r--r--src/kvsys.h17
2 files changed, 58 insertions, 11 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);
}
}
diff --git a/src/kvsys.h b/src/kvsys.h
index 0c8217d..de97b01 100644
--- a/src/kvsys.h
+++ b/src/kvsys.h
@@ -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
@@ -32,7 +33,19 @@ struct KeyValues {
bool hasescapes;
bool evalcond;
//char unused;
- struct KeyValues *next, *child, *chain;
+ union {
+ struct {
+ struct KeyValues *next, *child, *chain;
+ } v1;
+ struct {
+ void *kvsys;
+ bool haskvsys; // wasting 3 bytes for no reason, brilliant
+ struct KeyValues *next, *child, *chain;
+ } v2;
+ };
+ // this was supposedly added here at some point but we don't use it:
+ // typedef bool (*GetSymbolProc_t)(const char *pKey);
+ // GetSymbolProc_t m_pExpressionGetSymbolProc;
};
/* Wraps the engine IKeyValuesSystem::GetStringForSymbol() call. */
@@ -49,7 +62,7 @@ struct KeyValues *kvsys_getsubkey(struct KeyValues *kv, int sym);
* IMPORTANT: currently does not automatically coerce types like the engine
* does. This can be added later if actually required.
*/
-const char *kvsys_getstrval(struct KeyValues *kv);
+const char *kvsys_getstrval(const struct KeyValues *kv);
/* Free a KV object and all its subkeys. */
void kvsys_free(struct KeyValues *kv);