t7x/src/client/main.cpp

263 lines
7.1 KiB
C++
Raw Normal View History

2022-05-21 06:04:08 -04:00
#include <std_include.hpp>
#include "loader/component_loader.hpp"
2022-11-09 12:10:34 -05:00
#include "loader/loader.hpp"
2022-05-21 06:04:08 -04:00
#include <utils/finally.hpp>
#include <utils/hook.hpp>
#include <utils/nt.hpp>
2022-06-26 04:07:46 -04:00
#include <utils/io.hpp>
2023-01-02 07:57:00 -05:00
#include <utils/flags.hpp>
2022-06-26 04:07:46 -04:00
#include <steam/steam.hpp>
2022-05-21 06:04:08 -04:00
2023-01-02 07:57:00 -05:00
#include "game/game.hpp"
2023-01-30 12:47:51 -05:00
#include "launcher/launcher.hpp"
2023-01-02 07:57:00 -05:00
2022-05-21 06:04:08 -04:00
namespace
{
2022-11-09 12:10:34 -05:00
volatile bool g_call_tls_callbacks = false;
2022-06-26 04:07:46 -04:00
std::pair<void**, void*> g_original_import{};
2022-09-17 06:19:59 -04:00
DECLSPEC_NORETURN void WINAPI exit_hook(const uint32_t code)
2022-05-23 11:57:45 -04:00
{
component_loader::pre_destroy();
2022-09-17 06:19:59 -04:00
ExitProcess(code);
2022-05-23 11:57:45 -04:00
}
2022-05-21 06:04:08 -04:00
2022-06-26 04:07:46 -04:00
std::pair<void**, void*> patch_steam_import(const std::string& func, void* function)
2022-05-21 06:04:08 -04:00
{
2022-06-26 04:07:46 -04:00
static const utils::nt::library game{};
2022-05-23 11:57:45 -04:00
2022-06-26 04:07:46 -04:00
const auto game_entry = game.get_iat_entry("steam_api64.dll", func);
if (!game_entry)
2022-05-21 06:04:08 -04:00
{
2023-01-01 15:46:36 -05:00
//throw std::runtime_error("Import '" + func + "' not found!");
return {nullptr, nullptr};
2022-06-26 04:07:46 -04:00
}
2022-05-21 06:04:08 -04:00
2022-06-26 04:07:46 -04:00
const auto original_import = game_entry;
utils::hook::set(game_entry, function);
return {game_entry, original_import};
}
bool restart_app_if_necessary_stub()
{
const std::string steam_path = steam::SteamAPI_GetSteamInstallPath();
if (steam_path.empty() || !::utils::io::file_exists(steam_path + "/steam.exe"))
{
MessageBoxA(nullptr, "Steam must be installed for the game to run. Please install Steam!", "Error",
MB_ICONERROR);
ShellExecuteA(nullptr, "open", "https://store.steampowered.com/about/", nullptr, nullptr, SW_SHOWNORMAL);
TerminateProcess(GetCurrentProcess(), 1);
}
utils::hook::set(g_original_import.first, g_original_import.second);
patch_steam_import("SteamAPI_Shutdown", steam::SteamAPI_Shutdown);
component_loader::post_unpack();
return steam::SteamAPI_RestartAppIfNecessary();
}
2023-01-02 07:57:00 -05:00
BOOL set_process_dpi_aware_stub()
{
component_loader::post_unpack();
return SetProcessDPIAware();
}
2022-06-26 04:07:46 -04:00
void patch_imports()
{
patch_steam_import("SteamAPI_RegisterCallback", steam::SteamAPI_RegisterCallback);
patch_steam_import("SteamAPI_RegisterCallResult", steam::SteamAPI_RegisterCallResult);
patch_steam_import("SteamGameServer_Shutdown", steam::SteamGameServer_Shutdown);
patch_steam_import("SteamGameServer_RunCallbacks", steam::SteamGameServer_RunCallbacks);
patch_steam_import("SteamGameServer_GetHSteamPipe", steam::SteamGameServer_GetHSteamPipe);
patch_steam_import("SteamGameServer_GetHSteamUser", steam::SteamGameServer_GetHSteamUser);
patch_steam_import("SteamInternal_GameServer_Init", steam::SteamInternal_GameServer_Init);
patch_steam_import("SteamAPI_UnregisterCallResult", steam::SteamAPI_UnregisterCallResult);
patch_steam_import("SteamAPI_UnregisterCallback", steam::SteamAPI_UnregisterCallback);
patch_steam_import("SteamAPI_RunCallbacks", steam::SteamAPI_RunCallbacks);
patch_steam_import("SteamInternal_CreateInterface", steam::SteamInternal_CreateInterface);
patch_steam_import("SteamAPI_GetHSteamUser", steam::SteamAPI_GetHSteamUser);
patch_steam_import("SteamAPI_GetHSteamPipe", steam::SteamAPI_GetHSteamPipe);
patch_steam_import("SteamAPI_Init", steam::SteamAPI_Init);
//patch_steam_import("SteamAPI_Shutdown", steam::SteamAPI_Shutdown);
g_original_import = patch_steam_import("SteamAPI_RestartAppIfNecessary", restart_app_if_necessary_stub);
2023-01-02 07:57:00 -05:00
const utils::nt::library game{};
utils::hook::set(game.get_iat_entry("kernel32.dll", "ExitProcess"), exit_hook);
utils::hook::set(game.get_iat_entry("user32.dll", "SetProcessDPIAware"), set_process_dpi_aware_stub);
2022-05-23 11:57:45 -04:00
}
2022-05-21 06:04:08 -04:00
2022-09-18 02:48:12 -04:00
void remove_crash_file()
{
const utils::nt::library game{};
const auto game_file = game.get_path();
auto game_path = std::filesystem::path(game_file);
game_path.replace_extension(".start");
utils::io::remove_file(game_path);
2022-09-18 02:48:12 -04:00
}
2022-11-09 12:10:34 -05:00
PIMAGE_TLS_CALLBACK* get_tls_callbacks()
2022-05-23 11:57:45 -04:00
{
2022-11-09 12:10:34 -05:00
const utils::nt::library game{};
const auto& entry = game.get_optional_header()->DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS];
if (!entry.VirtualAddress || !entry.Size)
2022-05-23 11:57:45 -04:00
{
2022-11-09 12:10:34 -05:00
return nullptr;
2022-05-23 11:57:45 -04:00
}
2022-11-09 12:10:34 -05:00
const auto* tls_dir = reinterpret_cast<IMAGE_TLS_DIRECTORY*>(game.get_ptr() + entry.VirtualAddress);
return reinterpret_cast<PIMAGE_TLS_CALLBACK*>(tls_dir->AddressOfCallBacks);
2022-05-23 11:57:45 -04:00
}
2022-11-09 12:10:34 -05:00
void run_tls_callbacks(const DWORD reason)
{
2022-11-09 12:10:34 -05:00
if (!g_call_tls_callbacks)
{
2022-11-09 12:10:34 -05:00
return;
}
2022-11-09 12:10:34 -05:00
auto* callback = get_tls_callbacks();
while (callback && *callback)
{
2022-11-09 12:10:34 -05:00
(*callback)(GetModuleHandleA(nullptr), reason, nullptr);
++callback;
}
2022-11-09 12:10:34 -05:00
}
2022-11-09 12:10:34 -05:00
[[maybe_unused]] thread_local struct tls_runner
{
tls_runner()
{
2022-11-09 12:10:34 -05:00
run_tls_callbacks(DLL_THREAD_ATTACH);
}
2022-11-09 12:10:34 -05:00
~tls_runner()
{
2022-11-09 12:10:34 -05:00
run_tls_callbacks(DLL_THREAD_DETACH);
}
2022-11-09 12:10:34 -05:00
} tls_runner;
2022-11-09 12:10:34 -05:00
FARPROC load_process(const std::string& procname)
{
const auto proc = loader::load_binary(procname);
2022-11-09 12:10:34 -05:00
auto* const peb = reinterpret_cast<PPEB>(__readgsqword(0x60));
peb->Reserved3[1] = proc.get_ptr();
static_assert(offsetof(PEB, Reserved3[1]) == 0x10);
2022-11-09 12:10:34 -05:00
return FARPROC(proc.get_ptr() + proc.get_relative_entry_point());
}
2022-11-11 10:21:24 -05:00
bool handle_process_runner()
{
const auto* const command = "-proc ";
const char* parent_proc = strstr(GetCommandLineA(), command);
if (!parent_proc)
{
return false;
}
const auto pid = DWORD(atoi(parent_proc + strlen(command)));
const utils::nt::handle<> process_handle = OpenProcess(SYNCHRONIZE, FALSE, pid);
if (process_handle)
{
WaitForSingleObject(process_handle, INFINITE);
}
return true;
}
2023-01-30 12:38:41 -05:00
void enable_dpi_awareness()
{
2023-01-30 12:47:51 -05:00
const utils::nt::library user32{"user32.dll"};
2023-01-30 12:38:41 -05:00
const auto set_dpi = user32
2023-01-30 12:47:51 -05:00
? user32.get_proc<BOOL(WINAPI*)(DPI_AWARENESS_CONTEXT)>(
"SetProcessDpiAwarenessContext")
: nullptr;
2023-01-30 12:38:41 -05:00
if (set_dpi)
{
set_dpi(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
}
}
2022-11-09 12:10:34 -05:00
int main()
{
2022-11-11 10:21:24 -05:00
if (handle_process_runner())
{
return 0;
}
2022-11-09 12:10:34 -05:00
FARPROC entry_point{};
srand(uint32_t(time(nullptr)) ^ ~(GetTickCount() * GetCurrentProcessId()));
2023-01-30 12:38:41 -05:00
enable_dpi_awareness();
{
2022-11-09 12:10:34 -05:00
auto premature_shutdown = true;
2022-11-11 10:21:24 -05:00
const auto _ = utils::finally([&premature_shutdown]
2022-11-09 12:10:34 -05:00
{
if (premature_shutdown)
{
component_loader::pre_destroy();
}
});
2022-11-09 12:10:34 -05:00
try
{
remove_crash_file();
2023-01-30 12:47:51 -05:00
if (!launcher::run())
{
return 0;
}
2023-01-01 15:46:36 -05:00
const auto client_binary = "BlackOps3.exe"s;
const auto server_binary = "BlackOps3_UnrankedDedicatedServer.exe"s;
2023-01-02 07:57:00 -05:00
const auto has_server = !utils::io::file_exists(client_binary) || utils::flags::has_flag("dedicated");
2023-01-01 15:46:36 -05:00
if (!component_loader::activate(has_server))
{
return 1;
}
2023-01-01 15:46:36 -05:00
entry_point = load_process(has_server ? server_binary : client_binary);
2022-11-09 12:10:34 -05:00
if (!entry_point)
{
throw std::runtime_error("Unable to load binary into memory");
}
2023-01-02 07:57:00 -05:00
if (has_server != game::is_server())
{
throw std::runtime_error("Bad binary loaded into memory");
}
2022-11-09 12:10:34 -05:00
patch_imports();
if (!component_loader::post_load())
2022-11-09 12:10:34 -05:00
{
return 1;
}
2022-11-09 12:10:34 -05:00
premature_shutdown = false;
}
catch (std::exception& e)
{
2022-11-09 14:19:28 -05:00
MessageBoxA(nullptr, e.what(), "ERROR", MB_ICONERROR | MB_SETFOREGROUND | MB_TOPMOST);
2022-11-09 12:10:34 -05:00
return 1;
}
}
2022-05-21 06:04:08 -04:00
2022-11-09 12:10:34 -05:00
g_call_tls_callbacks = true;
return static_cast<int>(entry_point());
2022-05-23 11:57:45 -04:00
}
2022-05-21 06:04:08 -04:00
}
2022-11-09 12:10:34 -05:00
int __stdcall WinMain(HINSTANCE, HINSTANCE, PSTR, int)
{
return main();
2022-05-21 06:04:08 -04:00
}