/* * Copyright © 2024 Matthew Wozniak * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include "api.h" #include "hook.h" #include "log.h" #include "render.h" #include "os.h" #include "opt.h" #include #define WIN32_LEAN_AND_MEAN #include struct { int width; int height; const char *game; int fps; int quality; int bitrate; int qvs; const char *out; const char **demo; int demo_count; bool combine; } args = {0}; void (*orig_cbuf_addtext)(char *); void hook_cbuf_addtext(char *str) { orig_cbuf_addtext(str); // this is the last thing that happens when the game is opened if (!strcmp(str, "exec valve.rc\n")) { // after sound has been init'd, we can finish this if (!api_find_snd_recordbuffer()) die("couldn't find SND_RecordBuffer"); if (!render_init()) die("couldn't init render"); demoplayer->vt->start_playback(demoplayer, args.demo[0], false); orig_cbuf_addtext("exec prerender"); } } char cmdline[128]; char WINAPI *hook_GetCommandLineA(void) { return cmdline; } void *(WINAPI *orig_LoadLibraryExA)(const char *, void *, int); void WINAPI *hook_LoadLibraryExA(const char *filename, void *hfile, int flags) { // if the dll is already loaded, don't run our code again if (os_dlhandle(filename)) return orig_LoadLibraryExA(filename, hfile, flags); void *ret = orig_LoadLibraryExA(filename, hfile, flags); if (!ret) return ret; // cut down to basename for display 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"); orig_cbuf_addtext = (void (*)(char *)) hook_inline((void *)cbuf_addtext, (void *)hook_cbuf_addtext); } return ret; } typedef int (*LauncherMain_t)(void *instance, void *prev_inst, char *cmdline, int cmd_show); void usage() { const char *usage = "usage:\n" " rt [-w ] [-h ] [-g ] [-r ] [-s ]" "[-1] [-q ] OR [-b ]\n" " path/to/video.mp4 path/to/demo1.dem..."; puts(usage); } int main(int argc, const char **argv) { SetDllDirectoryA("bin/"); char *strend; const char *arg; FOR_OPTS(argc, argv) { case 'w': arg = OPTARG(argc, argv); args.width = strtol(arg, &strend, 10); if (strend == arg) die("width must be a number"); break; case 'h': arg = OPTARG(argc, argv); args.height = strtol(arg, &strend, 10); if (strend == arg) die("height must be a number"); break; case 'r': arg = OPTARG(argc, argv); args.fps = strtol(arg, &strend, 10); if (strend == arg) die("fps must be a number"); break; case 'q': arg = OPTARG(argc, argv); args.quality = strtol(arg, &strend, 10); if (strend == arg || args.quality < 1 || args.quality > 100) die("quality must be a number 1-100"); break; case 's': arg = OPTARG(argc, argv); args.qvs = strtol(arg, &strend, 10); if (strend == arg || args.qvs < 1 || args.qvs > 100) die("qvs must be a number 1-100"); break; case 'b': arg = OPTARG(argc, argv); args.bitrate = strtol(arg, &strend, 10); if (strend == arg) die("bitrate must be a number"); break; case 'g': args.game = OPTARG(argc, argv); case '1': args.combine = true; } if (!args.width) args.width = 1280; if (!args.height) args.height = 720; if (!args.game) args.game = "hl2"; if (!args.fps) args.fps = 30; if (!args.quality) args.quality = 75; if (!args.qvs) args.qvs = 100; if (argc < 2) { usage(); exit(1); } args.out = argv[0]; args.demo = argv + 1; args.demo_count = argc - 2; sprintf(cmdline, "hl2.exe -game %s -w %d -h %d -window -console", args.game, args.width, args.height); info("cmdline = %s", cmdline); hook_init(); orig_LoadLibraryExA = (typeof(orig_LoadLibraryExA))hook_dllapi("kernel32", "LoadLibraryExA", (void *)hook_LoadLibraryExA); hook_dllapi("kernel32", "GetCommandLineA", (void *)hook_GetCommandLineA); void *launcher_dll = os_dlopen("launcher"); LauncherMain_t launcher_main = (LauncherMain_t)os_dlsym(launcher_dll, "LauncherMain"); if (!launcher_main) die("couldn't open launcher"); launcher_main(NULL, NULL, cmdline, 0); } // vi: sw=4 ts=4 noet tw=80 cc=80