summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ac.c106
-rw-r--r--src/sst.c2
2 files changed, 107 insertions, 1 deletions
diff --git a/src/ac.c b/src/ac.c
index f5451de..2a1a8a4 100644
--- a/src/ac.c
+++ b/src/ac.c
@@ -1,5 +1,6 @@
/*
* Copyright © 2022 Michael Smith <mikesmiffy128@gmail.com>
+ * Copyright © 2022 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
@@ -17,6 +18,7 @@
#include <stdbool.h>
#include <stdlib.h>
+#include "bind.h"
#include "con_.h"
#include "hook.h"
#include "engineapi.h"
@@ -25,6 +27,9 @@
#include "mem.h"
#include "os.h"
#include "ppmagic.h"
+#include "vcall.h"
+#include "x86.h"
+#include "x86util.h"
static bool lockdown = false;
@@ -134,17 +139,118 @@ static void endlockdown(void) {
lockdown = false;
}
+enum /* from InputEventType_t - terser names used here */ {
+ BTNDOWN = 0, // data contains button code
+ BTNUP, // "
+ BTNDOUBLECLICK, // "
+ ANALOGUEVALCHG, // data contains analogue code, data2 contains value
+ SYSQUIT = 100,
+ SYSCTRLHOTPLUG, // data contains controller ID
+ SYSCTRLCOLDPLUG, // "
+ // ranges for other things
+ FIRSTVGUIEV = 1000,
+ FIRSTAPPEV = 2000
+};
+
+struct inputevent {
+ int type; // above enum
+ int tick;
+ int data, data2, data3;
+};
+
+DECL_VFUNC_DYN(void, GetDesktopResolution, int *, int *)
+DECL_VFUNC_DYN(void, DispatchAllStoredGameMessages)
+
+typedef void (*VCALLCONV DispatchInputEvent_func)(void *, struct inputevent *);
+static DispatchInputEvent_func orig_DispatchInputEvent;
+static void VCALLCONV hook_DispatchInputEvent(void *this,
+ struct inputevent *ev) {
+ // TODO(rta): do something here! (here's a quick reference/example)
+ //switch (ev->type) {
+ // CASES(BTNDOWN, BTNUP, BTNDOUBLECLICK):
+ // const char *desc[] = {"DOWN", "UP", "DOUBLE"};
+ // const char *binding = bind_get(ev->data);
+ // if (!binding) binding = "[unbound]";
+ // con_msg("key %d %s => %s\n", ev->data, desc[ev->type - BTNDOWN],
+ // binding);
+ //}
+ orig_DispatchInputEvent(this, ev);
+}
+
+static bool find_DispatchInputEvent(void) {
+#ifdef _WIN32
+ // Crazy pointer-chasing path to get to DispachInputEvent (to log keypresses
+ // and their associated binds):
+ // IGameUIFuncs interface
+ // -> CGameUIFuncs::GetDesktopResolution vfunc
+ // -> IGame/CGame (first mov into ECX)
+ // -> CGame::DispatchAllStoredGameMessages vfunc
+ // -> DispatchInputEvent (first call instruction)
+ void *gameuifuncs = factory_engine("VENGINE_GAMEUIFUNCS_VERSION005", 0);
+ if (!gameuifuncs) {
+ errmsg_errorx("couldn't get engine game UI interface");
+ return false;
+ }
+ void *cgame;
+ GetDesktopResolution_func GetDesktopResolution =
+ VFUNC(gameuifuncs, GetDesktopResolution);
+ for (uchar *p = (uchar *)GetDesktopResolution;
+ p - (uchar *)GetDesktopResolution < 16;) {
+ if (p[0] == X86_MOVRMW && p[1] == X86_MODRM(0, 1, 5)) {
+ void **indirect = mem_loadptr(p + 2);
+ cgame = *indirect;
+ goto ok;
+ }
+ NEXT_INSN(p, "CGame instance pointer");
+ }
+ errmsg_errorx("couldn't find pointer to CGame instance");
+ return false;
+ok: DispatchAllStoredGameMessages_func DispatchAllStoredGameMessages =
+ VFUNC(cgame, DispatchAllStoredGameMessages);
+ for (uchar *p = (uchar *)DispatchAllStoredGameMessages;
+ p - (uchar *)DispatchAllStoredGameMessages < 128;) {
+ if (p[0] == X86_CALL) {
+ orig_DispatchInputEvent = (DispatchInputEvent_func)(p + 5 +
+ mem_loadoffset(p + 1));
+ // Note: we could go further and dig HandleEngineKey from Key_Event,
+ // but it seems like Key_Event isn't directly called from
+ // DispatchInputEvent in L4D2 (or has a much different structure,
+ // requiring another function call to lead to HandleEngineKey).
+ return true;
+ }
+ NEXT_INSN(p, "DispatchInputEvent function");
+ }
+ errmsg_errorx("couldn't find DispatchInputEvent function");
+#else
+#warning TODO(linux): more find-y stuff
+#endif
+ return false;
+}
+
bool ac_init(void) {
+ if (!has_vtidx_GetDesktopResolution ||
+ !has_vtidx_DispatchAllStoredGameMessages) {
+ errmsg_errorx("missing gamedata entries for this engine");
+ return false;
+ }
#if defined(_WIN32)
if (!win32_init()) return false;
#elif defined(__linux__)
// TODO(linux): call init things
#endif
+ if (!find_DispatchInputEvent()) return false;
+ orig_DispatchInputEvent = (DispatchInputEvent_func)hook_inline(
+ (void *)orig_DispatchInputEvent, (void *)&hook_DispatchInputEvent);
+ if (!orig_DispatchInputEvent) {
+ errmsg_errorsys("couldn't hook DispatchInputEvent function");
+ return false;
+ }
return true;
}
void ac_end(void) {
endlockdown();
+ unhook_inline((void *)orig_DispatchInputEvent);
}
// vi: sw=4 ts=4 noet tw=80 cc=80
diff --git a/src/sst.c b/src/sst.c
index 93908dc..7246e89 100644
--- a/src/sst.c
+++ b/src/sst.c
@@ -208,7 +208,7 @@ static const char *updatenotes = "\
static void do_featureinit(void) {
bool has_bind = bind_init();
- has_ac = ac_init();
+ if (has_bind) has_ac = ac_init();
bool has_alias = alias_init();
has_autojump = autojump_init();
has_demorec = demorec_init();