From a2f7e37d8adf2047e1f3b0ea1227ac9d51514783 Mon Sep 17 00:00:00 2001 From: Matthew Wozniak Date: Fri, 1 Nov 2024 13:47:31 -0400 Subject: play demo using the demoplayer object --- api.c | 44 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) (limited to 'api.c') diff --git a/api.c b/api.c index af7bb5e..8001f8f 100644 --- a/api.c +++ b/api.c @@ -6,16 +6,50 @@ #undef NO_EXTERNS #include "os.h" #include "log.h" +#include "sst/x86.h" -struct engserver *engserver; +struct engserver *engserver = NULL; +struct engclient *engclient = NULL; +struct demoplayer *demoplayer = NULL; +void (*cbuf_addtext)(char *) = NULL; -void api_init() { - void *engine_dll = os_dlopen("engine"); +bool api_init(void) { + void *engine_dll = os_dlhandle("engine"); createinterface_func engine_factory = (createinterface_func)os_dlsym(engine_dll, "CreateInterface"); - if (!engine_factory) die("couldn't get engine factory"); + if (!engine_factory) bail("couldn't get engine factory"); engserver = engine_factory(INTERFACEVERSION_VENGINESERVER, NULL); - if (!engserver) die("couldn't get IVEngineServer from engine"); + if (!engserver) bail("couldn't get IVEngineServer from engine"); + engclient = engine_factory(VENGINE_CLIENT_INTERFACE_VERSION, NULL); + if (!engclient) bail("couldn't get IVEngineClient from engine"); + + // find cbuf_addtext + const u8 *instr = (const u8 *)engserver->vt->server_command; + // ServerCommand() calls a few small functions before Cbuf_AddText but they + // get inlined. look for 'push esi' and then a call. + for (const u8 *p = instr; p - instr < 64;) { + if (*p == X86_PUSHESI && *++p == X86_CALL) { + // jump is relative to after the instruction + cbuf_addtext = (void (*)(char *))(p + 5 + *(i32 *)(p + 1)); + } + int l = x86_len(p); + if (l == -1) + bail("invalid instruction looking for cbuf_addtext"); + p += l; + } + if (!cbuf_addtext) bail("couldn't find cbuf_addtext"); + + // find demoplayer + instr = (const u8 *)engclient->vt->is_playing_demo; + debug("is_playing_demo = %p", (void *)instr); + // CEngineClient::IsPlayingDemo is a wrapper around a demoplayer call + // The first thing it does should be load demoplayer into ECX + if (instr[0] != X86_MOVRMW || instr[1] != X86_MODRM(0, 1, 5)) + bail("couldn't get demoplayer"); + demoplayer = **(struct demoplayer ***)(instr + 2); + debug("demoplayer = %p", (void *)demoplayer); + + return true; } // vi: sw=4 ts=4 noet tw=80 cc=80 -- cgit v1.2.3-54-g00ecf