diff options
Diffstat (limited to 'api.c')
-rw-r--r-- | api.c | 140 |
1 files changed, 115 insertions, 25 deletions
@@ -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; } |