30#ifdef DTTR_MODS_ENABLED
48 size_t copy_len = sdslen(
src);
49 if (copy_len >= dst_size) {
50 copy_len = dst_size - 1u;
52 memcpy(
dst,
src, copy_len);
64 if (!request || !report
71 memset(report, 0,
sizeof(*report));
75 CONTEXT context = request->
context;
76 EXCEPTION_POINTERS exception_pointers = {
77 .ExceptionRecord = &exception_record,
78 .ContextRecord = &context,
80 const DWORD current_thread_id = GetCurrentThreadId();
85 GetCurrentProcessId(),
98 HANDLE thread = GetCurrentThread();
99 bool close_thread =
false;
100 if (thread_id != current_thread_id) {
102 THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION,
106 close_thread = thread !=
NULL;
107 if (!thread && report->
win32_error == ERROR_SUCCESS) {
166 .game_module = game_module,
171 .sidecar_module = sidecar_module,
204 char exe_path[MAX_PATH];
207 HANDLE file = CreateFileA(
217 if (file == INVALID_HANDLE_VALUE) {
222 DWORD file_size = GetFileSize(file,
NULL);
223 if (file_size == INVALID_FILE_SIZE && GetLastError() != NO_ERROR) {
224 DTTR_LOG_ERROR(
"Failed to get exe size for hashing: %s", exe_path);
228 buf = malloc(file_size);
229 if (file_size != 0 && !buf) {
230 DTTR_LOG_ERROR(
"Failed to allocate %lu bytes for exe hashing", file_size);
234 DWORD bytes_read = 0;
235 if (!ReadFile(file, buf, file_size, &bytes_read,
NULL)) {
241 file = INVALID_HANDLE_VALUE;
243 XXH64_hash_t hash = XXH3_64bits(buf, bytes_read);
251 if (file != INVALID_HANDLE_VALUE) {
266 const size_t len = sdslen(loader_dir);
267 if (len >= 8 && _stricmp(loader_dir + len - 8,
"modules\\") == 0) {
268 sdsrange(loader_dir, 0, (ssize_t)len - 9);
276 const char *config_env = getenv(
"DTTR_CONFIG_PATH");
283 const bool is_fullscreen = (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) != 0;
284 if (!SDL_SetWindowFullscreen(window, !is_fullscreen)) {
285 DTTR_LOG_WARN(
"SDL_SetWindowFullscreen failed: %s", SDL_GetError());
289#ifdef DTTR_MODS_ENABLED
295#define after_sdl_event(event, consumed) \
303 "Required PCDOGS operation failed: %s (%s)",
313#define REQUIRE_PCDOGS_CALL(expr_) require_pcdogs_call(#expr_, (expr_))
317#ifdef DTTR_MODS_ENABLED
340 switch (
event->type) {
348 case SDL_EVENT_GAMEPAD_ADDED:
349 case SDL_EVENT_GAMEPAD_REMOVED:
354 case SDL_EVENT_AUDIO_DEVICE_ADDED:
355 case SDL_EVENT_AUDIO_DEVICE_REMOVED:
360 case SDL_EVENT_KEY_DOWN:
361 if (
event->key.scancode == SDL_SCANCODE_F11) {
369 case SDL_EVENT_WINDOW_RESIZED:
370 case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
386 while (SDL_PollEvent(&
event)) {
396#ifdef DTTR_MODS_ENABLED
408#ifdef DTTR_MODS_ENABLED
431 int32_t config_ret = 0;
454 int32_t rendering_enabled = 0;
461 if (rendering_enabled) {
462#ifdef DTTR_MODS_ENABLED
464 uint8_t frame_status = 0;
480 uint8_t frame_status = 0;
490#ifdef DTTR_MODS_ENABLED
504 if (!prefix || !names) {
505 DTTR_LOG_WARN(
"Startup movie metadata unavailable; skipping intro movies");
509 for (
int i = 0; i < 4; i++) {
514 sds path = sdsnew(prefix);
557 "\x83\xEC\x40\x53\x8B\x5C\x24",
564 dttr_hook_win_main_site = site;
567 uint8_t jmp[5] = {0xE9};
568 memcpy(jmp + 1, &rel, 4);
576 HINSTANCE hPrevInstance,
581 bool should_exit_process =
false;
582 FILE *log_file =
NULL;
584 sds config_path =
NULL;
593 OutputDebugStringA(
"DTTR_SIDECAR_ENTRYPOINT");
600 "Failed to load configuration file at %s",
610 log_file = fopen(log_path,
"a+");
612 DTTR_FATAL(
"Could not open log file at %s", log_path);
616 DTTR_LOG_INFO(
"Loaded configuration file at %s", config_path);
621 DTTR_LOG_INFO(
"Log level set to %s", log_level_string(level));
640 DTTR_LOG_ERROR(
"Failed to install required sidecar hooks - aborting");
642 goto cleanup_sidecar_runtime;
645#ifdef DTTR_MODS_ENABLED
656 .overlay_visible =
false,
657 .game_input_enabled =
true,
669 goto cleanup_sidecar_runtime;
674 goto cleanup_sidecar_runtime;
680 goto cleanup_sidecar_runtime;
685 goto cleanup_sidecar_runtime;
688 void *audio_driver =
NULL;
693 goto cleanup_sidecar_runtime;
696 if (audio_driver ==
NULL) {
702 goto cleanup_sidecar_runtime;
705#ifdef DTTR_MODS_ENABLED
717 goto cleanup_sidecar_runtime;
723 int32_t should_quit = 0;
731 if (should_quit != 0) {
742cleanup_sidecar_runtime:
747 should_exit_process =
true;
751 sdsfree(config_path);
758 if (should_exit_process) {
759 ExitProcess((UINT)exit_code);
767 if (reason_for_call == DLL_PROCESS_ATTACH) {
bool dttr_audio_init(const DTTR_Mods_Context *ctx)
void dttr_audio_handle_device_event(const SDL_Event *event)
void dttr_audio_cleanup(const DTTR_Mods_Context *)
static void cleanup(DTTR_BackendState *state)
DTTR_Graphics_COM_Direct3DDevice7 DWORD block DTTR_Graphics_COM_Direct3DDevice7 DWORD block DTTR_Graphics_COM_Direct3DDevice7 void void void void DWORD f BOOL
DTTR_Graphics_COM_Direct3DDevice7 DWORD block DTTR_Graphics_COM_Direct3DDevice7 DWORD block DTTR_Graphics_COM_Direct3DDevice7 void void void void DWORD f FALSE
DTTR_Graphics_COM_Direct3DDevice7 DWORD block DTTR_Graphics_COM_Direct3DDevice7 DWORD block DTTR_Graphics_COM_Direct3DDevice7 void * dst
void void DWORD HANDLE event
DTTR_Graphics_COM_DirectDrawSurface7 DWORD flags void NULL
void dttr_pcdogs_crash_symbols_clear()
void dttr_pcdogs_crash_symbols_register(const DTTR_Core_Context *runtime)
#define DTTR_CONFIG_FILENAME
bool DTTR_Config_Load(const char *filename)
Loads config values from a strict JSON file into the global config object.
sds DTTR_CrashDump_Write(HANDLE process, DWORD pid, DWORD tid, EXCEPTION_POINTERS *exception_info)
void DTTR_CrashDump_Init(const char *dump_dir)
sds DTTR_CrashDump_FormatStackTrace(HANDLE process, HANDLE thread, const CONTEXT *context)
Formats a stack trace from a thread context. Caller frees the returned sds.
#define DTTR_UNWRAP_WINAPI_EXISTS(result)
#define DTTR_FATAL(error_message,...)
int32_t _stdcall DTTR_Hook_WinMainCallback(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int32_t nCmdShow)
Callback target for the patched game Window_RunWinMain routine.
void DTTR_Log(int level, const char *file, int line, const char *fmt,...)
bool DTTR_Log_IsEnabled(int level)
void DTTR_Log_SetLevel(int level)
#define DTTR_LOG_WARN(...)
void DTTR_Log_Unchecked(int level, const char *file, int line, const char *fmt,...)
#define DTTR_LOG_INFO(...)
int DTTR_Log_AddFP(FILE *fp, int level)
#define DTTR_LOG_ERROR(...)
union SDL_Event SDL_Event
struct SDL_Window SDL_Window
bool DTTR_Path_CopySds(char *out, size_t out_size, sds value)
sds DTTR_Path_ResolveRelativeTo(const char *base_dir, const char *path)
sds DTTR_Path_ModuleDir(void *module)
bool DTTR_Path_AppendSegment(sds *path, const char *segment, char separator)
DTTR_PCDOGS_API const struct dttr_pcdogs_function_accessor_PKG_InitializeResourceGameEngine *const DTTR_PCDOGS_F_PKG_InitializeResourceGameEngine
Accessor object for PKG_InitializeResourceGameEngine.
DTTR_PCDOGS_API const struct dttr_pcdogs_function_accessor_PKG_InitializeSystem *const DTTR_PCDOGS_F_PKG_InitializeSystem
Accessor object for PKG_InitializeSystem.
DTTR_PCDOGS_API const struct dttr_pcdogs_function_accessor_Graphics_RenderFrame *const DTTR_PCDOGS_F_Graphics_RenderFrame
Accessor object for Graphics_RenderFrame.
DTTR_PCDOGS_API const struct DTTR_PCDOGS_D_Video_PlayMovieIntro_PathPrefix_type *const DTTR_PCDOGS_D_Video_PlayMovieIntro_PathPrefix
DTTR_PCDOGS_API const struct dttr_pcdogs_function_accessor_Config_LoadAlternateFromINI *const DTTR_PCDOGS_F_Config_LoadAlternateFromINI
Accessor object for Config_LoadAlternateFromINI.
DTTR_PCDOGS_API const struct DTTR_PCDOGS_D_Video_PlayMovieIntro_FileNames_type *const DTTR_PCDOGS_D_Video_PlayMovieIntro_FileNames
DTTR_PCDOGS_API const struct dttr_pcdogs_function_accessor_PKG_FindAndOpenFile *const DTTR_PCDOGS_F_PKG_FindAndOpenFile
Accessor object for PKG_FindAndOpenFile.
DTTR_PCDOGS_API const struct DTTR_PCDOGS_D_Window_ProcessGameProc_Initialized_type *const DTTR_PCDOGS_D_Window_ProcessGameProc_Initialized
DTTR_PCDOGS_API bool DTTR_PCDOGS_ResolveAll(const DTTR_Core_Context *ctx)
DTTR_PCDOGS_API const struct dttr_pcdogs_function_accessor_Input_ResetState *const DTTR_PCDOGS_F_Input_ResetState
Accessor object for Input_ResetState.
DTTR_PCDOGS_API const struct DTTR_PCDOGS_D_Window_RunWinMain_SecondaryWindowHandle_type *const DTTR_PCDOGS_D_Window_RunWinMain_SecondaryWindowHandle
DTTR_PCDOGS_API const struct dttr_pcdogs_function_accessor_D3D_InitializeGraphicsSubsystem *const DTTR_PCDOGS_F_D3D_InitializeGraphicsSubsystem
Accessor object for D3D_InitializeGraphicsSubsystem.
DTTR_PCDOGS_API const struct DTTR_PCDOGS_D_Window_RunWinMain_RenderingEnabled_type *const DTTR_PCDOGS_D_Window_RunWinMain_RenderingEnabled
DTTR_PCDOGS_API const struct dttr_pcdogs_function_accessor_Display_SetMode *const DTTR_PCDOGS_F_Display_SetMode
Accessor object for Display_SetMode.
DTTR_PCDOGS_API const struct DTTR_PCDOGS_D_Audio_InitializeSystem_DigitalDriver_type *const DTTR_PCDOGS_D_Audio_InitializeSystem_DigitalDriver
DTTR_PCDOGS_API const struct DTTR_PCDOGS_D_Window_MainHandle_type *const DTTR_PCDOGS_D_Window_MainHandle
DTTR_PCDOGS_API const struct DTTR_PCDOGS_D_Input_ProcessWindowMessages_ShouldQuit_type *const DTTR_PCDOGS_D_Input_ProcessWindowMessages_ShouldQuit
bool DTTR_ResultOK(DTTR_Result result)
DTTR_Core_Hook * DTTR_Core_HookAttachPointer(uintptr_t addr, void *new_value, void **out_original)
bool DTTR_Core_HookIsActive(DTTR_Core_Hook *hook)
DTTR_Core_Hook * DTTR_Core_HookAttachFunction(uintptr_t addr, int prologue_size, void *handler, void **out_original)
DTTR_Core_Hook * DTTR_Core_HookPatchBytes(uintptr_t addr, const uint8_t *bytes, size_t size)
void DTTR_Core_HookCleanupAll()
Detach all remaining hooks and free the sigscan cache.
uintptr_t DTTR_Core_HookCachedSigscan(HMODULE mod, const char *sig, const char *mask)
void DTTR_Core_HookDetach(DTTR_Core_Hook *hook)
uintptr_t DTTR_Core_HookSigscan(HMODULE mod, const char *sig, const char *mask)
bool DTTR_Core_HookDetachChecked(DTTR_Core_Hook *hook)
#define DTTR_SDK_ABI_VERSION
const DTTR_Mods_Context * dttr_sidecar_context()
static HMODULE pc_dogs_module
static DTTR_Mods_Context sidecar_ctx
static bool initialize_pcdogs_runtime(const DTTR_Core_Context *ctx, HWND hwnd)
HINSTANCE dttr_sidecar_module
static sds get_config_path()
const DTTR_Core_Context * dttr_sidecar_runtime_context()
BOOL DllMain(HMODULE module, const DWORD reason_for_call, LPVOID reserved)
static const DTTR_Core_API RUNTIME_API
static void install_win_main_hook()
static void compute_exe_hash()
static bool dttr_sidecar_store_sds(char *dst, size_t dst_size, sds src)
#define after_sdl_event(event, consumed)
char dttr_exe_hash[DTTR_EXE_HASH_LENGTH+1]
char dttr_loader_dir[MAX_PATH]
#define REQUIRE_PCDOGS_CALL(expr_)
static bool dttr_sidecar_write_exception_report(const DTTR_Mods_ExceptionReportRequest *request, DTTR_Mods_ExceptionReport *report)
static dttr_startup_movies_result attempt_play_startup_movies()
static bool start_pcdogs_runtime(const DTTR_Core_Context *ctx, HWND hwnd)
static bool install_required_sidecar_hooks(const DTTR_Mods_Context *ctx)
dttr_startup_movies_result
@ DTTR_STARTUP_MOVIES_FAILED
@ DTTR_STARTUP_MOVIES_QUIT
@ DTTR_STARTUP_MOVIES_CONTINUE
static void set_default_exe_hash()
static sds get_loader_dir()
static void init_sidecar_context(HMODULE game_module, HMODULE sidecar_module)
static void toggle_fullscreen()
int32_t _stdcall DTTR_Hook_WinMainCallback(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int32_t nCmdShow)
Callback target for the patched game Window_RunWinMain routine.
static const DTTR_Mods_API MOD_API
void dttr_sidecar_handle_sdl_event(const SDL_Event *event)
static bool tick_main_loop()
static void cleanup_runtime(const DTTR_Mods_Context *ctx)
void dttr_sidecar_poll_sdl_events()
static bool require_pcdogs_call(const char *name, DTTR_Result result)
void dttr_game_hooks_cleanup(const DTTR_Mods_Context *)
bool dttr_game_hooks_init(const DTTR_Mods_Context *ctx)
void dttr_game_data_cleanup()
void dttr_game_data_init()
void dttr_graphics_hooks_cleanup(const DTTR_Mods_Context *ctx)
bool dttr_graphics_hooks_init(const DTTR_Mods_Context *ctx)
SDL_Window * dttr_graphics_get_window()
SDL_GPUDevice * dttr_graphics_get_device()
HWND dttr_graphics_init()
void dttr_graphics_handle_window_resize(int width, int height)
void dttr_graphics_begin_frame()
void dttr_graphics_cleanup()
void dttr_graphics_end_frame()
static void dttr_graphics_mod_device_restored(DTTR_BackendState *)
static void dttr_graphics_mod_device_created(DTTR_BackendState *)
DTTR_BackendState dttr_backend
static void dttr_graphics_mod_window_created(DTTR_BackendState *)
void dttr_imgui_cleanup()
bool dttr_imgui_process_event(const SDL_Event *event)
void dttr_imgui_init(SDL_Window *window, SDL_GPUDevice *device, DTTR_BackendType backend)
void dttr_mods_input_mode_changed(const DTTR_Mods_InputContext *ctx)
void dttr_mods_overlay_visible_changed(bool visible)
void dttr_mods_late_init()
bool dttr_mods_handle_event(const SDL_Event *event)
void dttr_mods_game_frame_blocked()
bool dttr_mods_should_advance_game_frame()
bool dttr_mods_before_event(const SDL_Event *event)
void dttr_mods_game_frame_advanced()
void dttr_mods_after_event(const SDL_Event *event, bool consumed)
void dttr_movies_hooks_cleanup(const DTTR_Mods_Context *ctx)
bool dttr_movies_hooks_init(const DTTR_Mods_Context *ctx)
void dttr_movies_start(const char *path)
dttr_movie_result dttr_movies_stop()
void dttr_movies_cleanup()
bool dttr_movies_is_playing()
bool dttr_movies_handle_event(const SDL_Event *event)
static const char * dttr_sidecar_result_detail(DTTR_Result result)
EXCEPTION_RECORD exception_record
char stack_trace[DTTR_MODS_EXCEPTION_REPORT_STACK_TRACE_CAPACITY]