From 5b8d37094a38aff1b2669056a4011a42450fd9c5 Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Sat, 2 Jul 2022 21:51:50 +0100 Subject: Add alias management plus input filtering skeleton Some of the alias stuff was kind of stolen from earlier figuring-out Bill did. More Bill code is also on the way. :^) --- src/ac.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/ac.h | 27 +++++++++++ src/alias.c | 112 +++++++++++++++++++++++++++++++++++++++++++ src/alias.h | 37 +++++++++++++++ src/demorec.c | 8 ++-- src/sst.c | 9 +++- 6 files changed, 337 insertions(+), 6 deletions(-) create mode 100644 src/ac.c create mode 100644 src/ac.h create mode 100644 src/alias.c create mode 100644 src/alias.h (limited to 'src') diff --git a/src/ac.c b/src/ac.c new file mode 100644 index 0000000..f5451de --- /dev/null +++ b/src/ac.c @@ -0,0 +1,150 @@ +/* + * Copyright © 2022 Michael Smith + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include "con_.h" +#include "hook.h" +#include "engineapi.h" +#include "errmsg.h" +#include "intdefs.h" +#include "mem.h" +#include "os.h" +#include "ppmagic.h" + +static bool lockdown = false; + +#ifdef _WIN32 + +static void *gamewin, *inhookwin, *inhookthr; +static ulong inhooktid; + +// UINT_PTR is a **stupid** typedef, but whatever. +static ssize __stdcall kproc(int code, UINT_PTR wp, ssize lp) { + KBDLLHOOKSTRUCT *data = (KBDLLHOOKSTRUCT *)lp; + if (lockdown && data->flags & LLKHF_INJECTED && + GetForegroundWindow() == gamewin) { + return 1; + } + return CallNextHookEx(0, code, wp, lp); +} + +static ssize __stdcall mproc(int code, UINT_PTR wp, ssize lp) { + MSLLHOOKSTRUCT *data = (MSLLHOOKSTRUCT *)lp; + if (lockdown && data->flags & LLMHF_INJECTED && + GetForegroundWindow() == gamewin) { + return 1; + } + return CallNextHookEx(0, code, wp, lp); +} + +// this is its own thread to meet the strict timing deadline, otherwise the +// hook gets silently removed. plus, we don't wanna incur latency anyway. +static ulong __stdcall inhookthrmain(void *unused) { + if (!SetWindowsHookExW(WH_KEYBOARD_LL, &kproc, 0, 0) || + !SetWindowsHookExW(WH_MOUSE_LL, &mproc, 0, 0)) { + // intentionally vague message + con_warn("sst: RTA mode is unavailable due to an error\n"); + return -1; + } + MSG m; int ret; + while ((ret = GetMessageW(&m, inhookwin, 0, 0)) > 0) DispatchMessage(&m); + if (ret == -1) { + // XXX: if this ever happens, it's a disaster! users might not notice + // their run just dying all of a sudden. with any luck it won't matter + // in practice but... this kind of sucks. + con_warn("** sst: ERROR in message loop, abandoning RTA mode! **"); + } + return ret; +} + +static bool win32_init(void) { + gamewin = FindWindowW(L"Valve001", 0); + if (!gamewin) { + errmsg_errorsys("failed to get game window handle"); + return false; + } + return true; +} + +static void inhook_start(void) { + inhookwin = CreateWindowW(L"sst-eventloop", L"sst-eventloop", + WS_DISABLED, 0, 0, 0, 0, HWND_MESSAGE, 0, 0, 0); + inhookthr = CreateThread(0, 0, &inhookthrmain, 0, 0, &inhooktid); +} + +// TODO(rta): run this check every tick (or at least X amount of time) +static void inhook_check(void) { + if (WaitForSingleObject(inhookthr, 0) == WAIT_OBJECT_0) { + ulong status; + GetExitCodeThread(inhookthr, &status); + if (status != 0) { + // TODO(rta): stop demos, and stuff. + lockdown = false; + } + } +} + +static void inhook_stop(void) { + PostThreadMessageW(inhooktid, WM_QUIT, 0, 0); + if (WaitForSingleObject(inhookthr, INFINITE) == WAIT_FAILED) { + errmsg_warnsys("couldn't wait for thread, status unknown"); + // XXX: now what!? + } +} + +#else + +// TODO(linux): do some stuff, I guess... + +#endif + +// TODO(rta): call these functions as part of the run lifecycle + +static void startlockdown(void) { + if (lockdown) return; +#ifdef _WIN32 + inhook_start(); +#endif + // FIXME: should really have some semaphore to make sure inhook thread got + // going okay... + lockdown = true; + // TODO(rta): start demos, etc +} + +static void endlockdown(void) { + if (!lockdown) return; +#ifdef _WIN32 + inhook_stop(); +#endif + lockdown = false; +} + +bool ac_init(void) { +#if defined(_WIN32) + if (!win32_init()) return false; +#elif defined(__linux__) + // TODO(linux): call init things +#endif + return true; +} + +void ac_end(void) { + endlockdown(); +} + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/ac.h b/src/ac.h new file mode 100644 index 0000000..638b01f --- /dev/null +++ b/src/ac.h @@ -0,0 +1,27 @@ +/* + * Copyright © 2022 Michael Smith + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef INC_AC_H +#define INC_AC_H + +#include + +bool ac_init(void); +void ac_end(void); + +#endif + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/alias.c b/src/alias.c new file mode 100644 index 0000000..7db5907 --- /dev/null +++ b/src/alias.c @@ -0,0 +1,112 @@ +/* + * Copyright © 2022 Michael Smith + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include "alias.h" +#include "con_.h" +#include "dbg.h" +#include "errmsg.h" +#include "extmalloc.h" +#include "mem.h" +#include "x86.h" + +struct alias **_alias_head; + +void alias_rm(const char *name) { + for (struct alias **p = _alias_head; *p; p = &(*p)->next) { + if (!strcmp((*p)->name, name)) { + struct alias *next = (*p)->next; + extfree((*p)->value); extfree(*p); + *p = next; + return; + } + } +} + +DEF_CCMD_HERE(sst_alias_remove, "Remove a command alias", 0) { + if (cmd->argc != 2) { + con_warn("usage: sst_alias_remove name"); + return; + } + if (strlen(cmd->argv[1]) > 31) { + con_warn("invalid alias name (too long)"); + return; + } + alias_rm(cmd->argv[1]); +} + +void alias_nuke(void) { + for (struct alias *p = alias_head; p;) { + struct alias *next = p->next; + extfree(p->value); extfree(p); + p = next; + } + alias_head = 0; +} + +DEF_CCMD_HERE(sst_alias_clear, "Remove all command aliases", 0) { + if (cmd->argc != 1) { + con_warn("usage: sst_alias_clear"); + return; + } + alias_nuke(); +} + +// XXX: same as in demorec, might want some abstraction for this +#define NEXT_INSN(p, tgt) do { \ + int _len = x86_len(p); \ + if (_len == -1) { \ + errmsg_errorx("unknown or invalid instruction looking for %s", tgt); \ + return false; \ + } \ + (p) += _len; \ +} while (0) + +static bool find_alias_head(con_cmdcb alias_cb) { +#ifdef _WIN32 + for (uchar *p = (uchar *)alias_cb; p - (uchar *)alias_cb < 64;) { + // alias command with no args calls ConMsg() then loads the head pointer + // that asm looks like: call ; mov , dword ptr [x] + // (we don't care about the exact registers) + if (p[0] == X86_MISCMW && (p[1] & 0xF0) == 0xD0 && + p[2] == X86_MOVRMW && (p[3] & 0xC7) == 0x05) { + _alias_head = mem_loadptr(p + 4); + return true; + } + NEXT_INSN(p, "load of alias list"); + } +#else +#warning TODO(linux): check whether linux is equivalent! +#endif + return false; +} + +bool alias_init(void) { + struct con_cmd *cmd_alias = con_findcmd("alias"); + if (!cmd_alias) { + errmsg_warnx("couldn't find \"alias\" command"); + return false; + } + if (!find_alias_head(con_getcmdcb(cmd_alias))) { + errmsg_warnx("couldn't find alias list"); + return false; + }; + return true; +} + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/alias.h b/src/alias.h new file mode 100644 index 0000000..3e417d9 --- /dev/null +++ b/src/alias.h @@ -0,0 +1,37 @@ +/* + * Copyright © 2022 Michael Smith + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef INC_ALIAS_H +#define INC_ALIAS_H + +#include + +struct alias { + struct alias *next; + char name[32]; // TIL this has a hard limit :^) + char *value; +}; +extern struct alias **_alias_head; +#define alias_head (*_alias_head) // act as a global + +void alias_rm(const char *name); +void alias_nuke(void); + +bool alias_init(void); + +#endif + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/demorec.c b/src/demorec.c index 156e7c6..a0c85a1 100644 --- a/src/demorec.c +++ b/src/demorec.c @@ -165,7 +165,7 @@ static void hook_stop_cb(const struct con_cmdargs *args) { // This finds the "demorecorder" global variable (the engine-wide CDemoRecorder // instance). -static inline bool find_demorecorder(struct con_cmd *cmd_stop) { +static inline bool find_demorecorder(void) { #ifdef _WIN32 // The "stop" command calls the virtual function demorecorder.IsRecording(), // so just look for the load of the "this" pointer into ECX @@ -224,7 +224,7 @@ bool demorec_init(void) { return false; } orig_stop_cb = con_getcmdcb(cmd_stop); - if (!find_demorecorder(cmd_stop)) { + if (!find_demorecorder()) { errmsg_errorx("couldn't find demo recorder instance"); return false; } @@ -245,8 +245,8 @@ bool demorec_init(void) { orig_StopRecording = (StopRecording_func)hook_vtable(vtable, vtidx_StopRecording, (void *)&hook_StopRecording); - orig_record_cb = cmd_record->cb; cmd_record->cb = &hook_record_cb; - orig_stop_cb = cmd_stop->cb; cmd_stop->cb = &hook_stop_cb; + cmd_record->cb = &hook_record_cb; + cmd_stop->cb = &hook_stop_cb; sst_autorecord->base.flags &= ~CON_HIDDEN; return true; diff --git a/src/sst.c b/src/sst.c index b7298ef..1321d03 100644 --- a/src/sst.c +++ b/src/sst.c @@ -21,6 +21,8 @@ #include #endif +#include "ac.h" +#include "alias.h" #include "autojump.h" #include "con_.h" #include "demorec.h" @@ -188,8 +190,8 @@ static const void *const *const plugin_obj; // figures out the dependencies at build time and generates all the init glue // but we want to actually release the plugin this decade so for now I'm just // plonking some bools here and worrying about it later. :^) -static bool has_autojump = false, has_demorec = false, has_fov = false, - has_nosleep = false, has_portalcolours = false; +static bool has_ac = false, has_autojump = false, has_demorec = false, + has_fov = false, has_nosleep = false, has_portalcolours = false; #ifdef _WIN32 static bool has_rinput = false; #endif @@ -204,6 +206,8 @@ static const char *updatenotes = "\ "; static void do_featureinit(void) { + has_ac = ac_init(); + bool has_alias = alias_init(); has_autojump = autojump_init(); has_demorec = demorec_init(); // not enabling demorec_custom yet - kind of incomplete and currently unused @@ -409,6 +413,7 @@ static void do_unload(void) { } #endif + if (has_ac) ac_end(); if (has_autojump) autojump_end(); if (has_demorec) demorec_end(); if (has_fov) fov_end(); // dep on ent -- cgit v1.2.3