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-02-19 14:28:17 -05:00
# include "component/updater.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 " ) ;
2022-11-21 13:35:15 -05:00
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-05-28 13:13:44 -04:00
2022-11-09 12:10:34 -05:00
void run_tls_callbacks ( const DWORD reason )
2022-05-28 13:13:44 -04:00
{
2022-11-09 12:10:34 -05:00
if ( ! g_call_tls_callbacks )
2022-05-28 13:13:44 -04:00
{
2022-11-09 12:10:34 -05:00
return ;
2022-05-28 13:13:44 -04:00
}
2022-11-09 12:10:34 -05:00
auto * callback = get_tls_callbacks ( ) ;
while ( callback & & * callback )
2022-05-28 13:13:44 -04:00
{
2022-11-09 12:10:34 -05:00
( * callback ) ( GetModuleHandleA ( nullptr ) , reason , nullptr ) ;
+ + callback ;
2022-05-28 13:13:44 -04:00
}
2022-11-09 12:10:34 -05:00
}
2022-05-28 13:13:44 -04:00
2022-11-09 12:10:34 -05:00
[[maybe_unused]] thread_local struct tls_runner
{
tls_runner ( )
2022-05-28 13:13:44 -04:00
{
2022-11-09 12:10:34 -05:00
run_tls_callbacks ( DLL_THREAD_ATTACH ) ;
2022-05-28 13:13:44 -04:00
}
2022-11-09 12:10:34 -05:00
~ tls_runner ( )
2022-05-28 13:13:44 -04:00
{
2022-11-09 12:10:34 -05:00
run_tls_callbacks ( DLL_THREAD_DETACH ) ;
2022-05-28 13:13:44 -04:00
}
2022-11-09 12:10:34 -05:00
} tls_runner ;
2022-05-28 13:13:44 -04:00
2022-11-09 12:10:34 -05:00
FARPROC load_process ( const std : : string & procname )
{
const auto proc = loader : : load_binary ( procname ) ;
2022-05-28 13:13:44 -04:00
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-05-28 13:13:44 -04:00
2022-11-09 12:10:34 -05:00
return FARPROC ( proc . get_ptr ( ) + proc . get_relative_entry_point ( ) ) ;
2022-05-28 13:13:44 -04:00
}
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-31 13:14:02 -05:00
{
const auto set_dpi = user32
? user32 . get_proc < BOOL ( WINAPI * ) ( DPI_AWARENESS_CONTEXT ) > (
" SetProcessDpiAwarenessContext " )
: nullptr ;
if ( set_dpi )
{
set_dpi ( DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ) ;
return ;
}
}
2023-01-30 12:38:41 -05:00
{
2023-01-31 13:14:02 -05:00
const utils : : nt : : library shcore { " shcore.dll " } ;
const auto set_dpi = shcore
? shcore . get_proc < HRESULT ( WINAPI * ) ( PROCESS_DPI_AWARENESS ) > (
" SetProcessDpiAwareness " )
: nullptr ;
if ( set_dpi )
{
set_dpi ( PROCESS_PER_MONITOR_DPI_AWARE ) ;
return ;
}
}
{
const auto set_dpi = user32
? user32 . get_proc < BOOL ( WINAPI * ) ( ) > (
" SetProcessDPIAware " )
: nullptr ;
if ( set_dpi )
{
set_dpi ( ) ;
}
2023-01-30 12:38:41 -05:00
}
}
2023-02-18 02:46:11 -05:00
void trigger_high_performance_gpu_switch ( )
{
// Make sure to link D3D11, as this might trigger high performance GPU
static volatile auto _ = & D3D11CreateDevice ;
const auto key = utils : : nt : : open_or_create_registry_key (
HKEY_CURRENT_USER , R " (Software \ Microsoft \ DirectX \ UserGpuPreferences) " ) ;
if ( ! key )
{
return ;
}
const auto self = utils : : nt : : library : : get_by_address ( & trigger_high_performance_gpu_switch ) ;
2023-02-18 04:42:48 -05:00
const auto path = self . get_path ( ) . make_preferred ( ) . wstring ( ) ;
2023-02-18 02:46:11 -05:00
2023-02-18 04:57:03 -05:00
if ( RegQueryValueExW ( key , path . data ( ) , nullptr , nullptr , nullptr , nullptr ) ! = ERROR_FILE_NOT_FOUND )
2023-02-18 04:42:48 -05:00
{
return ;
}
2023-02-18 04:57:03 -05:00
const std : : wstring data = L " GpuPreference=2; " ;
2023-02-18 02:46:11 -05:00
RegSetValueExW ( key , self . get_path ( ) . make_preferred ( ) . wstring ( ) . data ( ) , 0 , REG_SZ ,
reinterpret_cast < const BYTE * > ( data . data ( ) ) ,
static_cast < DWORD > ( ( data . size ( ) + 1u ) * 2 ) ) ;
}
2023-02-18 04:57:14 -05:00
void validate_non_network_share ( )
{
const auto self = utils : : nt : : library : : get_by_address ( & validate_non_network_share ) ;
const auto path = self . get_path ( ) . make_preferred ( ) ;
const auto wpath = path . wstring ( ) ;
if ( wpath . size ( ) > = 2 & & wpath [ 0 ] = = L ' \\ ' & & wpath [ 1 ] = = L ' \\ ' )
{
throw std : : runtime_error (
" You seem to be using a network share: \n \n " + path . string ( ) + " \n \n Network shares are not supported! " ) ;
}
}
2022-11-09 12:10:34 -05:00
int main ( )
2022-05-28 13:13:44 -04:00
{
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 ( ) ) ) ;
2022-05-28 13:13:44 -04:00
2023-01-30 12:38:41 -05:00
enable_dpi_awareness ( ) ;
2022-05-28 13:13:44 -04:00
{
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-05-28 13:13:44 -04:00
2022-11-09 12:10:34 -05:00
try
{
2023-02-18 04:57:14 -05:00
validate_non_network_share ( ) ;
2022-11-09 12:10:34 -05:00
remove_crash_file ( ) ;
2023-03-01 14:10:24 -05:00
updater : : update ( ) ;
2022-05-28 13:13:44 -04:00
2023-02-01 15:00:00 -05:00
const auto client_binary = " BlackOps3.exe " s ;
const auto server_binary = " BlackOps3_UnrankedDedicatedServer.exe " s ;
const auto has_client = utils : : io : : file_exists ( client_binary ) ;
const auto has_server = utils : : io : : file_exists ( server_binary ) ;
const auto is_server = utils : : flags : : has_flag ( " dedicated " ) | | ( ! has_client & & has_server ) ;
2023-02-18 02:46:11 -05:00
if ( ! is_server )
2023-01-30 12:47:51 -05:00
{
2023-02-18 02:46:11 -05:00
trigger_high_performance_gpu_switch ( ) ;
2023-02-19 14:28:17 -05:00
const auto launch = utils : : flags : : has_flag ( " launch " ) ;
2023-02-22 11:48:29 -05:00
if ( ! launch & & ! utils : : nt : : is_wine ( ) & & ! launcher : : run ( ) )
2023-02-18 02:46:11 -05:00
{
return 0 ;
}
2023-01-30 12:47:51 -05:00
}
2023-02-01 15:00:00 -05:00
if ( ! component_loader : : activate ( is_server ) )
2022-11-09 14:19:08 -05:00
{
return 1 ;
}
2023-02-01 15:00:00 -05:00
entry_point = load_process ( is_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 " ) ;
}
2022-05-28 13:13:44 -04:00
2023-02-03 13:19:47 -05:00
if ( is_server ! = game : : is_server ( ) )
2023-01-02 07:57:00 -05:00
{
throw std : : runtime_error ( " Bad binary loaded into memory " ) ;
}
2023-03-04 07:42:56 -05:00
if ( ! is_server & & ! game : : is_legacy_client ( ) )
{
if ( game : : is_client ( ) )
{
throw std : : runtime_error ( " You are running the latest Steam update. We're working on supporting it. For the time being, however, you have to revert to the old binary. " ) ;
}
throw std : : runtime_error ( " Bad binary loaded into memory " ) ;
}
2022-11-09 12:10:34 -05:00
patch_imports ( ) ;
2022-05-28 13:13:44 -04:00
2022-11-09 14:19:08 -05:00
if ( ! component_loader : : post_load ( ) )
2022-11-09 12:10:34 -05:00
{
return 1 ;
}
2022-05-28 13:13:44 -04:00
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-28 13:13:44 -04:00
}
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
}