int __stdcall thcrap_plugin_init() { int base_tasofro_removed = stack_remove_if_unneeded("base_tasofro"); if (base_tasofro_removed == 1) { return 1; } const char *game = json_object_get_string(runconfig_get(), "game"); game_id = game_id_from_string(game); if(base_tasofro_removed == -1) { if(game_id == TH145) { log_mboxf(NULL, MB_OK | MB_ICONINFORMATION, "Support for TH14.5 has been moved out of the sandbox.\n" "\n" "Please reconfigure your patch stack; otherwise, you might " "encounter errors or missing functionality after further " "updates.\n" ); } else { return 1; } } if (game_id >= TH135) { return th135_init(); } else { return nsml_init(); } return 0; }
BOOL thcrap_inject_into_new(const char *exe_fn, char *args, const char *run_cfg_fn) { int ret = 0; json_t *run_cfg = json_load_file_report(run_cfg_fn); if(!run_cfg) { return 1; }; json_object_set_new(run_cfg, "run_cfg_fn", json_string(run_cfg_fn)); runconfig_set(run_cfg); { STRLEN_DEC(exe_fn); VLA(char, exe_dir_local, exe_fn_len); VLA(char, exe_fn_local, exe_fn_len); STARTUPINFOA si = {0}; PROCESS_INFORMATION pi = {0}; char *exe_dir = NULL; strcpy(exe_fn_local, exe_fn); str_slash_normalize_win(exe_fn_local); strcpy(exe_dir_local, exe_fn); if(PathRemoveFileSpec(exe_dir_local)) { exe_dir = exe_dir_local; } /** * Sure, the alternative would be to set up the entire engine * with all plug-ins and modules to correctly run any additional * detours. While it would indeed be nice to allow those to control * initial startup, it really shouldn't be necessary for now - and * it really does run way too much unnecessary code for my taste. */ ret = W32_ERR_WRAP(inject_CreateProcessU( exe_fn_local, args, NULL, NULL, TRUE, 0, NULL, exe_dir, &si, &pi )); if(ret) { char *msg_str = ""; FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, ret, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&msg_str, 0, NULL ); log_mboxf(NULL, MB_OK | MB_ICONEXCLAMATION, "Failed to start %s: %s", exe_fn, msg_str ); LocalFree(msg_str); } VLA_FREE(exe_fn_local); VLA_FREE(exe_dir_local); } return ret; }
int WaitUntilEntryPoint(HANDLE hProcess, HANDLE hThread, const char *module) { // Try to get the entry point by various means, sorted by both efficiency // and probability of them working. void *entry_addr = NULL; /** * Method 1: Initial value of EAX * After creating a process in suspended state, EAX is guaranteed to contain * the correct address of the entry point, even when the executable has the * DYNAMICBASE flag activated in its header. * * (Works on Windows, but not on Wine) */ if(!(entry_addr = entry_from_context(hThread))) { HMODULE module_base; /** * Method 2: EnumProcessModules, then parse the PE header. * * (Works on Wine, but not on Windows immediately after the target process * was created in suspended state.) */ if(!(module_base = GetRemoteModuleHandle(hProcess, module))) { /** * Method 3: Guessing 0x400000 * This is the default value in many compilers and should thus work for * most non-ASLR Windows applications. */ module_base = (HMODULE)0x400000; } entry_addr = GetRemoteModuleEntryPoint(hProcess, module_base); } if(entry_addr) { return ThreadWaitUntil(hProcess, hThread, entry_addr); } else { log_mboxf(NULL, MB_OK | MB_ICONEXCLAMATION, "Couldn't determine the entry point of %s!\n" "\n" "Seems as if %s won't work with this game on your system.\n", PathFindFileNameA(module), PROJECT_NAME_SHORT() ); return 1; } }
static LRESULT CALLBACK loader_update_proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { static loader_update_state_t *state = nullptr; switch (uMsg) { case WM_CREATE: { CREATESTRUCTW *param = (CREATESTRUCTW*)lParam; state = (loader_update_state_t*)param->lpCreateParams; break; } case WM_COMMAND: switch LOWORD(wParam) { case HWND_BUTTON_RUN: if (HIWORD(wParam) == BN_CLICKED) { if (state->background_updates == false) { state->cancel_update = true; } EnterCriticalSection(&state->cs); state->game_started = true; LeaveCriticalSection(&state->cs); thcrap_inject_into_new(state->exe_fn, state->args, NULL, NULL); } break; case HWND_BUTTON_UPDATE: if (HIWORD(wParam) == BN_CLICKED) { SetEvent(state->event_require_update); } break; case HWND_CHECKBOX_KEEP_UPDATER: if (HIWORD(wParam) == BN_CLICKED) { BOOL enable_state; if (SendMessage(state->hwnd[HWND_CHECKBOX_KEEP_UPDATER], BM_GETCHECK, 0, 0) == BST_CHECKED) { state->background_updates = true; enable_state = TRUE; } else { state->background_updates = false; enable_state = FALSE; } EnableWindow(state->hwnd[HWND_STATIC_UPDATES_INTERVAL], enable_state); EnableWindow(state->hwnd[HWND_EDIT_UPDATES_INTERVAL], enable_state); EnableWindow(state->hwnd[HWND_UPDOWN], enable_state); } break; case HWND_EDIT_UPDATES_INTERVAL: if (HIWORD(wParam) == EN_CHANGE) { BOOL success; UINT n = GetDlgItemInt(state->hwnd[HWND_MAIN], HWND_EDIT_UPDATES_INTERVAL, &success, FALSE); if (success) { EnterCriticalSection(&state->cs); state->time_between_updates = n; LeaveCriticalSection(&state->cs); } } break; case HWND_CHECKBOX_UPDATE_AT_EXIT: if (HIWORD(wParam) == BN_CLICKED) { if (SendMessage(state->hwnd[HWND_CHECKBOX_UPDATE_AT_EXIT], BM_GETCHECK, 0, 0) == BST_CHECKED) { state->update_at_exit = true; } else { state->update_at_exit = false; } } break; case HWND_CHECKBOX_UPDATE_OTHERS: if (HIWORD(wParam) == BN_CLICKED) { if (SendMessage(state->hwnd[HWND_CHECKBOX_UPDATE_OTHERS], BM_GETCHECK, 0, 0) == BST_CHECKED) { state->update_others = true; } else { state->update_others = false; } } break; case HWND_BUTTON_DISABLE_UPDATES: if (HIWORD(wParam) == BN_CLICKED) { int len = GetCurrentDirectory(0, NULL); VLA(char, current_directory, len + 1); GetCurrentDirectory(len + 1, current_directory); if (log_mboxf(NULL, MB_YESNO, "Do you really want to completely disable updates?\n\n" "If you want to enable them again, you will need to run\n" "%s\\thcrap_enable_updates.bat", current_directory) == IDYES) { MoveFile("thcrap_update" DEBUG_OR_RELEASE ".dll", "thcrap_update_disabled" DEBUG_OR_RELEASE ".dll"); const char *bat_file = "@echo off\n" "if not exist \"%~dp0\"\\thcrap_update" DEBUG_OR_RELEASE ".dll (\n" "move \"%~dp0\"\\thcrap_update_disabled" DEBUG_OR_RELEASE ".dll \"%~dp0\"\\thcrap_update" DEBUG_OR_RELEASE ".dll\n" "echo Updates enabled\n" ") else (\n" "echo Updates are already enabled\n" ")\n" "pause\n" "(goto) 2>nul & del \"%~f0\"\n"; file_write("thcrap_enable_updates.bat", bat_file, strlen(bat_file)); log_mbox(NULL, MB_OK, "Updates are now disabled."); PostQuitMessage(0); } VLA_FREE(current_directory); } break; case HWND_BUTTON_EXPAND_LOGS: if (HIWORD(wParam) == BN_CLICKED) { if (state->settings_visible) { // Hide log window SetWindowPos(state->hwnd[HWND_MAIN], 0, 0, 0, 500, 165, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER); state->settings_visible = false; } else { // Show log window SetWindowPos(state->hwnd[HWND_MAIN], 0, 0, 0, 500, 435, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER); state->settings_visible = true; } } break; } break; case WM_CTLCOLORSTATIC: if ((HWND)lParam == state->hwnd[HWND_LABEL_STATUS] || (HWND)lParam == state->hwnd[HWND_CHECKBOX_UPDATE_AT_EXIT] || (HWND)lParam == state->hwnd[HWND_CHECKBOX_KEEP_UPDATER] || (HWND)lParam == state->hwnd[HWND_CHECKBOX_UPDATE_OTHERS] || (HWND)lParam == state->hwnd[HWND_STATIC_UPDATES_INTERVAL] || (HWND)lParam == state->hwnd[HWND_STATIC_UPDATE_AT_EXIT]) { HDC hdc = (HDC)wParam; SetTextColor(hdc, RGB(0, 0, 0)); SetBkMode(hdc, TRANSPARENT); return (LRESULT)GetSysColorBrush(COLOR_WINDOW); } break; case WM_DESTROY: state->cancel_update = true; PostQuitMessage(0); break; }