diff options
-rw-r--r-- | api.c | 39 | ||||
-rw-r--r-- | api.h | 23 | ||||
-rw-r--r-- | main.c | 7 | ||||
-rw-r--r-- | render.c | 21 |
4 files changed, 59 insertions, 31 deletions
@@ -33,6 +33,7 @@ struct audio_device **audio_device = 0; struct soundstate *snd = 0; void (*snd_recordbuffer)(void) = 0; void (*cbuf_addtext)(char *) = 0; +bool steampipe = false; // This macro is Copyright 2024 Michael Smith under the same license as above. #define NEXT_INSN(p, tgt) do { \ @@ -46,7 +47,8 @@ void (*cbuf_addtext)(char *) = 0; // TODO: should this be split up? -bool api_init(void) { +bool api_init(bool _steampipe) { + steampipe = _steampipe; void *engine_dll = os_dlhandle("engine"); createinterface_func engine_factory = (createinterface_func)os_dlsym(engine_dll, "CreateInterface"); @@ -81,13 +83,13 @@ bool api_init(void) { if (!cbuf_addtext) bail("couldn't find cbuf_addtext"); // find demoplayer - instr = (const u8 *)engclient->vt->is_playing_demo; + instr = steampipe ? (const u8 *)engclient->vt->steampipe_is_playing_demo : + (const u8 *)engclient->vt->is_playing_demo; // CEngineClient::IsPlayingDemo is a wrapper around a demoplayer call // The first thing it does should be load a ptr to 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); // find videomode // CEngineAPI::SetEngineWindow calls videomode->SetGameWindow after @@ -105,7 +107,6 @@ bool api_init(void) { } NEXT_INSN(p, "videomode"); } - debug("videomode = %p", (void *)videomode); // find cl_movieinfo { @@ -129,7 +130,6 @@ bool api_init(void) { if (instr[0] != X86_ALUMI8 || instr[1] != X86_MODRM(0, 7, 5)) bail("couldn't get movieinfo"); movieinfo = *(struct movieinfo **)(instr + 2); - debug("movieinfo = %p", (void *)movieinfo); } // find SND_RecordBuffer... this is something! @@ -138,15 +138,22 @@ bool api_init(void) { 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 + // S_Init is called after setting snd_firsttime to true. Look for the + // second call after the mov + bool snd_firsttime_set = false; int call_count = 0; - for (const u8 *p = instr; p - instr < 64;) { - if (*p == X86_CALL && ++call_count == 3) + for (const u8 *p = instr; p - instr < 80;) { + // mov byte ptr [snd_firsttime], 1 + if (p[0] == X86_MOVMI8 && p[1] == 0x05 && p[6] == 1) + snd_firsttime_set = true; + if (p[0] == X86_CALL && snd_firsttime_set && ++call_count == 2) { s_init = (void *)(p + 5 + *(i32 *)(p + 1)); + break; + } NEXT_INSN(p, "S_Init"); } - debug("S_Init = %p", (void *)s_init); - if (!s_init) bail("couldn't find 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. @@ -162,8 +169,8 @@ bool api_init(void) { } NEXT_INSN(p, "g_AudioDevice"); } - debug("g_AudioDevice = %p", (void *)audio_device); - if (!audio_device) bail("couldn't get ptr to g_AudioDevice"); + if (!audio_device) + bail("couldn't get ptr to g_AudioDevice"); } return true; } @@ -172,8 +179,9 @@ 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); + const u8 *instr = steampipe ? + (*audio_device)->vt->steampipe_transfer_samples : + (*audio_device)->vt->transfer_samples; // S_TransferStereo16 is the 8th call in TransferSamples int call_count = 0; for (const u8 *p = instr; p - instr < 384 /* big func! */;) { @@ -183,7 +191,6 @@ bool api_find_snd_recordbuffer(void) { } 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 @@ -208,8 +215,6 @@ bool api_find_snd_recordbuffer(void) { } 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; } @@ -39,6 +39,7 @@ struct engclient { struct { usize _pad[75]; void (*VIRTUAL is_playing_demo)(struct engclient *this); + void (*VIRTUAL steampipe_is_playing_demo)(struct engclient *this); } *vt; }; @@ -81,6 +82,7 @@ struct audio_device { struct { usize _pad[22]; void *transfer_samples; + void *steampipe_transfer_samples; } *vt; }; @@ -142,9 +144,14 @@ struct videomode { usize _pad[22]; void (*VIRTUAL write_movie_frame)(struct videomode *this, struct movieinfo *info); - usize _pad2[4]; + usize _pad2[3]; + void (*VIRTUAL steampipe_write_movie_frame)(struct videomode *this, + struct movieinfo *info); void (*VIRTUAL read_screen_pixels)(struct videomode *this, int x, int y, int w, int h, void *buf, enum image_format fmt); + usize _pad3; + void (*VIRTUAL steampipe_read_screen_pixels)(struct videomode *this, + int x, int y, int w, int h, void *buf, enum image_format fmt); } *vt; }; @@ -164,14 +171,13 @@ struct demoplayer { /* EPIC HACK: * Snd_WriteLinearBlastStereo16 is an inline asm function that starts with * - * mov ebx,snd_p - * mov edi,snd_out - * mov ecx,snd_linear_count - * mov esi,snd_vol + * mov ebx, snd_p + * mov edi, snd_out + * mov ecx, snd_linear_count + * mov esi, snd_vol * * Luckily for us, these are all the things that we need!!!! - * So, we just use the instructions as a struct so we don't have to copy - * anything. It just works. + * So, we just use the instructions as a struct. */ struct soundstate { u16 _mov_ebx; @@ -195,10 +201,11 @@ extern struct movieinfo *movieinfo; extern void (*cbuf_addtext)(char *); extern void (*snd_recordbuffer)(void); extern struct soundstate *snd; +extern bool steampipe; #endif // initializes required engine apis. returns false on error. -bool api_init(void); +bool api_init(bool steampipe); bool api_find_snd_recordbuffer(void); #endif @@ -70,9 +70,10 @@ void WINAPI *hook_LoadLibraryExA(const char *filename, void *hfile, int flags) { const char *basename = filename; for (const char *p = filename; *p; p++) if (*p == '\\') basename = p + 1; - - if (!strcmp(basename, "gameui.dll")) { - if (!api_init()) die("couldn't get apis"); + if (!strcmp(basename, "gameui.dll") || !strcmp(basename, "GameUI.dll")) { + if (!api_init(basename[0] == 'G')) + die("couldn't get apis"); + cbuf_addtext("echo THIS WORKS!!!!!!!!!!!!;"); orig_cbuf_addtext = (void (*)(char *)) hook_inline((void *)cbuf_addtext, (void *)hook_cbuf_addtext); } @@ -75,7 +75,13 @@ bool do_frame(struct videomode *this, struct movieinfo *info) { struct { u8 bgr[3]; } *data = 0; HR(imf_buffer->lpVtbl->Lock(imf_buffer, (u8 **)&data, NULL, NULL)); // THIS IS SLOW!! - this->vt->read_screen_pixels(this, 0, 0, args.width, args.height, raw, IMAGE_FORMAT_BGR888); + if (steampipe) + this->vt->steampipe_read_screen_pixels(this, 0, 0, args.width, + args.height, raw, IMAGE_FORMAT_BGR888); + else + this->vt->read_screen_pixels(this, 0, 0, args.width, args.height, raw, + IMAGE_FORMAT_BGR888); + for (int y = args.height - 1; y > 0; y--) { memcpy(data + ((args.height - y) * args.width), raw + y * args.width, szpx * args.width); @@ -140,6 +146,8 @@ void VIRTUAL hook_stop_playback(struct demoplayer *this) { return; } + info("finished!"); + if (!do_stop()) die("oopsie!"); @@ -150,8 +158,12 @@ bool render_init(void) { bool r = os_mprot((*videomode)->vt, 28 * sizeof(void *), PAGE_EXECUTE_READWRITE); if (!r) bail("couldn't mprotect videomode vtable"); - orig_write_movie_frame = (*videomode)->vt->write_movie_frame; - (*videomode)->vt->write_movie_frame = hook_write_movie_frame; + + typeof(orig_write_movie_frame) *wmf = steampipe ? + &(*videomode)->vt->steampipe_write_movie_frame : + &(*videomode)->vt->write_movie_frame; + orig_write_movie_frame = *wmf; + *wmf = hook_write_movie_frame; r = os_mprot(demoplayer->vt, 18 * sizeof(void *), PAGE_EXECUTE_READWRITE); if (!r) bail("couldn't mprotect demoplayer vtable"); @@ -170,6 +182,9 @@ bool render_init(void) { sprintf(framerate_cmd, "host_framerate %d;", args.fps); cbuf_addtext(framerate_cmd); + if (CoInitializeEx(NULL, COINIT_MULTITHREADED) == RPC_E_CHANGED_MODE) + warn("changed COM concurrency mode!"); + HR(MFStartup(MF_VERSION, 0)); // init sinkwriter |