aboutsummaryrefslogtreecommitdiff
path: root/api.c
diff options
context:
space:
mode:
Diffstat (limited to 'api.c')
-rw-r--r--api.c140
1 files changed, 115 insertions, 25 deletions
diff --git a/api.c b/api.c
index 0562bba..f2be734 100644
--- a/api.c
+++ b/api.c
@@ -21,14 +21,28 @@
#include "log.h"
#include "sst/x86.h"
-struct engserver *engserver = NULL;
-struct engclient *engclient = NULL;
-struct engineapi *engineapi = NULL;
-struct enginetools *enginetools = NULL;
-struct demoplayer *demoplayer = NULL;
-struct videomode **videomode = NULL;
-struct movieinfo *movieinfo = NULL;
-void (*cbuf_addtext)(char *) = NULL;
+struct engserver *engserver = 0;
+struct engclient *engclient = 0;
+struct engineapi *engineapi = 0;
+struct enginetools *enginetools = 0;
+struct cvar *cvar = 0;
+struct demoplayer *demoplayer = 0;
+struct videomode **videomode = 0;
+struct movieinfo *movieinfo = 0;
+struct audio_device **audio_device = 0;
+struct soundstate *snd = 0;
+void (*snd_recordbuffer)(void) = 0;
+void (*cbuf_addtext)(char *) = 0;
+
+// This macro is Copyright 2024 Michael Smith under the same license as above.
+#define NEXT_INSN(p, tgt) do { \
+ int _len = x86_len(p); \
+ if (_len == -1) { \
+ bail("unknown or invalid instruction looking for %s", tgt); \
+ return false; \
+ } \
+ (p) += _len; \
+} while (0)
// TODO: should this be split up?
@@ -36,15 +50,22 @@ bool api_init(void) {
void *engine_dll = os_dlhandle("engine");
createinterface_func engine_factory =
(createinterface_func)os_dlsym(engine_dll, "CreateInterface");
+
+ void *vstdlib_dll = os_dlhandle("vstdlib");
+ createinterface_func vstdlib_factory =
+ (createinterface_func)os_dlsym(vstdlib_dll, "CreateInterface");
+
if (!engine_factory) bail("couldn't get engine factory");
- engserver = engine_factory(VENGINE_SERVER_INTERFACE_VERSION, NULL);
+ engserver = engine_factory(VENGINE_SERVER_INTERFACE_VERSION, 0);
if (!engserver) bail("couldn't get IVEngineServer from engine");
- engclient = engine_factory(VENGINE_CLIENT_INTERFACE_VERSION, NULL);
+ engclient = engine_factory(VENGINE_CLIENT_INTERFACE_VERSION, 0);
if (!engclient) bail("couldn't get IVEngineClient from engine");
- engineapi = engine_factory(VENGINE_LAUNCHER_INTERFACE_VERSION, NULL);
+ engineapi = engine_factory(VENGINE_LAUNCHER_INTERFACE_VERSION, 0);
if (!engineapi) bail("couldn't get IEngineAPI from engine");
- enginetools = engine_factory(VENGINE_TOOL_INTERFACE_VERSION, NULL);
+ enginetools = engine_factory(VENGINE_TOOL_INTERFACE_VERSION, 0);
if (!engineapi) bail("couldn't get IEngineTools from engine");
+ cvar = vstdlib_factory(VENGINE_CVAR_INTERFACE_VERSION, 0);
+ if (!cvar) bail("couldn't get ICVar from vstdlib");
// find cbuf_addtext
const u8 *instr = (const u8 *)engserver->vt->server_command;
@@ -55,10 +76,7 @@ bool api_init(void) {
// 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;
+ NEXT_INSN(p, "CBuf_AddText");
}
if (!cbuf_addtext) bail("couldn't find cbuf_addtext");
@@ -85,10 +103,7 @@ bool api_init(void) {
videomode = *(struct videomode ***)(p + 2);
break;
}
- int l = x86_len(p);
- if (l == -1)
- bail("invalid instruction looking for videomode");
- p += l;
+ NEXT_INSN(p, "videomode");
}
debug("videomode = %p", (void *)videomode);
@@ -97,16 +112,13 @@ bool api_init(void) {
// step 1: find CL_IsRecordingMovie() in
// CEngineTools::StartMovieRecording
instr = (const u8 *)enginetools->vt->start_movie_recording;
- void *cl_isrecordingmovie = NULL;
+ void *cl_isrecordingmovie = 0;
for (const u8 *p = instr; p - instr < 64;) {
// this is the first call in the function
if (*p == X86_CALL) {
cl_isrecordingmovie = (void *)(p + 5 + *(i32 *)(p + 1));
}
- int l = x86_len(p);
- if (l == -1)
- bail("invalid instruction looking for CL_IsRecordingMovie");
- p += l;
+ NEXT_INSN(p, "CL_IsRecordingMovie");
}
if (!cl_isrecordingmovie)
bail("couldn't find cbuf_addtext CL_IsRecordingMovie");
@@ -120,6 +132,84 @@ bool api_init(void) {
debug("movieinfo = %p", (void *)movieinfo);
}
+ // find SND_RecordBuffer... this is something!
+ {
+ // step 1: Find S_Init() in snd_restart
+ struct concommand *snd_restart = cvar->vt->find_command(cvar, "snd_restart");
+ instr = (const u8 *)snd_restart->callback;
+ void *s_init = 0;
+ // S_Init is the 3rd call in Snd_Restart_f
+ int call_count = 0;
+ for (const u8 *p = instr; p - instr < 64;) {
+ if (*p == X86_CALL && ++call_count == 3)
+ s_init = (void *)(p + 5 + *(i32 *)(p + 1));
+ NEXT_INSN(p, "S_Init");
+ }
+ debug("S_Init = %p", (void *)s_init);
+ if (!s_init) bail("couldn't find S_Init");
+ // step 2: find g_AudioDevice in S_Init
+ // g_AudioDevice is the first variable assignment after the check for
+ // the -nosound command line arg. look for that.
+ instr = (const u8 *)s_init;
+ bool nosound_push = false;
+ for (const u8 *p = instr; p - instr < 180;) {
+ if (p[0] == X86_PUSHIW
+ && !strcmp(*(const char **)(p + 1), "-nosound"))
+ nosound_push = true;
+ if (nosound_push && p[0] == X86_MOVIIEAX) {
+ audio_device = *(struct audio_device ***)(p + 1);
+ break;
+ }
+ NEXT_INSN(p, "g_AudioDevice");
+ }
+ debug("g_AudioDevice = %p", (void *)audio_device);
+ if (!audio_device) bail("couldn't get ptr to g_AudioDevice");
+ }
+ return true;
+}
+
+bool api_find_snd_recordbuffer(void) {
+ // continuation, we must do this part later
+ // step 3: Find S_TransferStereo16 in CAudioDirectSound::TransferSamples
+ void *transferstereo16 = 0;
+ const u8 *instr = (*audio_device)->vt->transfer_samples;
+ debug("CAudioDirectSound::TransferSamples = %p", (void *)instr);
+ // S_TransferStereo16 is the 8th call in TransferSamples
+ int call_count = 0;
+ for (const u8 *p = instr; p - instr < 384 /* big func! */;) {
+ if (*p == X86_CALL && ++call_count == 8)
+ transferstereo16 = (void *)(p + 5 + *(i32 *)(p + 1));
+ NEXT_INSN(p, "S_TransferStereo16");
+ }
+ if (!transferstereo16)
+ bail("couldn't find transferstereo16");
+ debug("S_TransferStereo16 = %p", transferstereo16);
+ // step 4: find SND_RecordBuffer in S_TransferStereo16
+ // it should be the next call after an 'add ecx, ecx'
+ // We are also going to get Snd_WriteLinearBlastStereo16 to get some other
+ // variables we need.
+ instr = (const u8 *)transferstereo16;
+ bool found_double = false;
+ bool found_recbuf = false;
+ for (const u8 *p = instr; p - instr < 192;) {
+ if (p[0] == X86_ADDRMW && p[1] == X86_MODRM(3, 1, 1))
+ found_double = true;
+ else if (p[0] == X86_CALL && found_double && !found_recbuf) {
+ found_recbuf = true;
+ snd_recordbuffer = (void (*)(void))(p + 5 + *(i32 *)(p + 1));
+ }
+ // this will be the next call
+ else if (p[0] == X86_CALL && found_double && found_recbuf) {
+ // this looks outrageous! that is because it is.
+ snd = (struct soundstate *)(p + 5 + *(i32 *)(p + 1) + 3);
+ break;
+ }
+ NEXT_INSN(p, "SND_RecordBuffer");
+ }
+ if (!snd_recordbuffer)
+ bail("couldn't find snd_recordbuffer");
+ debug("SND_RecordBuffer = %p", (void *)snd_recordbuffer);
+ debug("Snd_WriteLinearBlastStereo16 + 3 = %p", (void *)snd);
return true;
}