From b53b780e1b14e174204457765b4a0a72b6d2924f Mon Sep 17 00:00:00 2001 From: Willian Henrique Date: Sat, 16 Jul 2022 18:08:23 +0100 Subject: Add stuff to track keypresses Committer's note: this is somewhat adapted from Bill's original code, written a while back, but he gets full credit for actually doing the hard part. --- gamedata/engine.kv | 8 +++- src/ac.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/sst.c | 2 +- 3 files changed, 114 insertions(+), 2 deletions(-) diff --git a/gamedata/engine.kv b/gamedata/engine.kv index 022ccbd..fdc03ff 100644 --- a/gamedata/engine.kv +++ b/gamedata/engine.kv @@ -34,10 +34,16 @@ vtidx_GetGameDirectory { vtidx_GetEngineBuildNumber { L4D2 { Client013 99 - //Client014 ??? // TODO(compat): find out what this (maybe 136?) + //Client014 ??? // TODO(compat): find out what this is (maybe 136?) } } +// IGameUIFuncs +vtidx_GetDesktopResolution 5 + +// IGame/CGame +vtidx_DispatchAllStoredGameMessages 16 + // VEngineServer vtidx_PEntityOfEntIndex { OrangeBox 19 } // probably OE too but??? vtidx_ServerCommand { OrangeBoxbased 36 } 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 + * Copyright © 2022 Willian Henrique * * 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 #include +#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(); -- cgit v1.2.3