diff options
author | Michael Smith <mikesmiffy128@gmail.com> | 2023-05-05 00:04:43 +0100 |
---|---|---|
committer | Michael Smith <mikesmiffy128@gmail.com> | 2023-05-05 00:06:17 +0100 |
commit | 3ff6d47277fc0e39053584d8889240cab446a72a (patch) | |
tree | 03c26795061fc472d2224efefa283af6b42fe3e0 /src/demorec.c | |
parent | ea752466846c129a0910e47d34d725af1aea5d84 (diff) |
Implement APIs to control demo recording
This is a surprise tool that will help us later!
Diffstat (limited to 'src/demorec.c')
-rw-r--r-- | src/demorec.c | 61 |
1 files changed, 60 insertions, 1 deletions
diff --git a/src/demorec.c b/src/demorec.c index 9c836b4..bbcc317 100644 --- a/src/demorec.c +++ b/src/demorec.c @@ -20,6 +20,7 @@ #include "con_.h" #include "engineapi.h" #include "errmsg.h" +#include "event.h" #include "feature.h" #include "gamedata.h" #include "gameinfo.h" @@ -41,7 +42,9 @@ DEF_CVAR(sst_autorecord, "Continuously record demos even after reconnecting", 1, void *demorecorder; static int *demonum; static bool *recording; +const char *demorec_basename; static bool wantstop = false; +bool demorec_forceauto = false; #define SIGNONSTATE_NEW 3 #define SIGNONSTATE_SPAWN 5 @@ -78,16 +81,22 @@ static void VCALLCONV hook_StopRecording(void *this) { orig_StopRecording(this); // If the user didn't specifically request the stop, tell the engine to // start recording again as soon as it can. - if (wasrecording && !wantstop && con_getvari(sst_autorecord)) { + if (wasrecording && !wantstop && (demorec_forceauto || + con_getvari(sst_autorecord))) { *recording = true; *demonum = lastnum; } } +DECL_VFUNC_DYN(void, StartRecording) + static struct con_cmd *cmd_record, *cmd_stop; static con_cmdcb orig_record_cb, orig_stop_cb; +DEF_PREDICATE(AllowDemoControl, void) + static void hook_record_cb(const struct con_cmdargs *args) { + if (!CHECK_AllowDemoControl()) return; bool was = *recording; if (!was && args->argc == 2 || args->argc == 3) { // safety check: make sure a directory exists, otherwise recording @@ -149,6 +158,7 @@ static void hook_record_cb(const struct con_cmdargs *args) { } static void hook_stop_cb(const struct con_cmdargs *args) { + if (!CHECK_AllowDemoControl()) return; wantstop = true; orig_stop_cb(args); wantstop = false; @@ -198,6 +208,51 @@ static inline bool find_recmembers(void *stoprecording) { return false; } +// This finds "m_szDemoBaseName" using the pointer to the original +// "StartRecording" demorecorder function. +static inline bool find_demoname(void *startrecording) { +#ifdef _WIN32 + for (uchar *p = (uchar *)startrecording; p - (uchar *)startrecording < 32;) { + // the function immediately calls Q_strncpy and copies into a buffer + // offset from `this` - look for a LEA instruction some time *before* + // the first call takes place + if (p[0] == X86_CALL) return false; + if (p[0] == X86_LEA && (p[1] & 0xC0) == 0x80) { + demorec_basename = mem_offset(demorecorder, mem_load32(p + 2)); + return true; + } + NEXT_INSN(p, "demo basename variable"); + } +#else +#warning TODO(linux): implement linux equivalent (???) +#endif + return false; +} + +bool demorec_start(const char *name) { + bool was = *recording; + if (was) return false; + // easiest way to do this, though dumb, is to just call the record command + // callback that we already have a hold of. note: this args object is very + // incomplete, but is enough to make the command work + struct con_cmdargs args = {.argc = 2, .argv = {0, name, 0}}; + orig_record_cb(&args); + if (!was && *recording) *demonum = 0; // same logic as in the hook + return *recording; +} + +int demorec_stop(void) { + // note: our set-to-0-and-back hack actually has the nice side effect of + // making this correct when recording and stopping in the menu lol + int ret = *demonum; + orig_StopRecording(demorecorder); + return ret; +} + +bool demorec_recording(void) { + return *recording; +} + INIT { cmd_record = con_findcmd("record"); if (!cmd_record) { // can *this* even happen? I hope not! @@ -226,6 +281,10 @@ INIT { errmsg_errorx("couldn't find recording state variables"); return false; } + if (!find_demoname(vtable[vtidx_StartRecording])) { + errmsg_errorx("couldn't find demo basename variable"); + return false; + } orig_SetSignonState = (SetSignonState_func)hook_vtable(vtable, vtidx_SetSignonState, (void *)&hook_SetSignonState); |