summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorMichael Smith <mikesmiffy128@gmail.com>2022-07-02 21:51:50 +0100
committerMichael Smith <mikesmiffy128@gmail.com>2022-07-23 18:51:28 +0100
commit5b8d37094a38aff1b2669056a4011a42450fd9c5 (patch)
tree0e67c30227d4368834df94be42fef645186267c6 /src
parente3fd5ad05a7d933d401de431ce2d27a99d3b9995 (diff)
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. :^)
Diffstat (limited to 'src')
-rw-r--r--src/ac.c150
-rw-r--r--src/ac.h27
-rw-r--r--src/alias.c112
-rw-r--r--src/alias.h37
-rw-r--r--src/demorec.c8
-rw-r--r--src/sst.c9
6 files changed, 337 insertions, 6 deletions
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 <mikesmiffy128@gmail.com>
+ *
+ * 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 <stdbool.h>
+#include <stdlib.h>
+
+#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 <mikesmiffy128@gmail.com>
+ *
+ * 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 <stdbool.h>
+
+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 <mikesmiffy128@gmail.com>
+ *
+ * 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 <stdbool.h>
+#include <string.h>
+
+#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 <reg>; mov <reg>, 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 <mikesmiffy128@gmail.com>
+ *
+ * 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 <stdbool.h>
+
+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 <shlwapi.h>
#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