mirror of
https://github.com/alliedmodders/hl2sdk.git
synced 2025-12-08 02:58:24 +00:00
673 lines
15 KiB
C++
673 lines
15 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose: A redirection tool that allows the DLLs to reside elsewhere.
|
|
//
|
|
//=====================================================================================//
|
|
|
|
#if defined( _WIN32 )
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include <direct.h>
|
|
#endif
|
|
|
|
#ifdef POSIX
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <dlfcn.h>
|
|
#include <limits.h>
|
|
#include <string.h>
|
|
#define MAX_PATH PATH_MAX
|
|
#endif
|
|
|
|
#include <cstdio>
|
|
#include <array>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "tier0/basetypes.h"
|
|
|
|
#ifdef WIN32
|
|
typedef int (*LauncherMain_t)( HINSTANCE hInstance, HINSTANCE hPrevInstance,
|
|
LPSTR lpCmdLine, int nCmdShow );
|
|
#elif POSIX
|
|
typedef int (*LauncherMain_t)( int argc, char **argv );
|
|
#else
|
|
#error
|
|
#endif
|
|
|
|
#ifdef WIN32
|
|
// hinting the nvidia driver to use the dedicated graphics card in an optimus configuration
|
|
// for more info, see: http://developer.download.nvidia.com/devzone/devcenter/gamegraphics/files/OptimusRenderingPolicies.pdf
|
|
extern "C" { _declspec( dllexport ) DWORD NvOptimusEnablement = 0x00000001; }
|
|
|
|
// same thing for AMD GPUs using v13.35 or newer drivers
|
|
extern "C" { __declspec( dllexport ) int AmdPowerXpressRequestHighPerformance = 1; }
|
|
|
|
#endif
|
|
|
|
|
|
#include <steam/steam_api.h>
|
|
|
|
#ifdef _WIN32
|
|
static HMODULE Launcher_LoadModule( const char *pszPath )
|
|
{
|
|
return LoadLibraryEx( pszPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH );
|
|
}
|
|
|
|
static void Launcher_CloseModule( HMODULE hHandle )
|
|
{
|
|
FreeLibrary( hHandle );
|
|
}
|
|
|
|
static auto Launcher_GetProcAddress( HMODULE hHandle, const char *pszName )
|
|
{
|
|
return GetProcAddress( hHandle, pszName );
|
|
}
|
|
#else
|
|
#include <dlfcn.h>
|
|
#include <SDL2/SDL.h>
|
|
|
|
static void* Launcher_LoadModule( const char *pszPath )
|
|
{
|
|
return dlopen( pszPath, RTLD_NOW );
|
|
}
|
|
|
|
static void Launcher_CloseModule( void *pHandle )
|
|
{
|
|
dlclose( pHandle );
|
|
}
|
|
|
|
static void *Launcher_GetProcAddress( void *pHandle, const char *pszName )
|
|
{
|
|
return dlsym( pHandle, pszName );
|
|
}
|
|
|
|
#define MessageBox( x, text, title, y) SDL_ShowSimpleMessageBox( SDL_MESSAGEBOX_ERROR, title, text, NULL )
|
|
#endif
|
|
|
|
static const AppId_t k_unSDK2013MPAppId = 243750;
|
|
|
|
#ifdef MOD_LAUNCHER
|
|
static const AppId_t k_unMyModAppid = MOD_APPID;
|
|
#else
|
|
static const AppId_t k_unMyModAppid = k_unSDK2013MPAppId;
|
|
#endif
|
|
|
|
static bool s_bInittedSteam = false;
|
|
|
|
#ifdef _WIN32
|
|
static HMODULE s_SteamModule;
|
|
#else
|
|
static void *s_SteamModule;
|
|
|
|
#define WRAP( fn, ret, ... ) \
|
|
ret __real_##fn(__VA_ARGS__); \
|
|
__attribute__ ((visibility ("hidden"))) ret __wrap_##fn(__VA_ARGS__)
|
|
|
|
#define CALL( fn ) __real_##fn
|
|
|
|
// Some stubs for pre-exec.
|
|
extern "C"
|
|
{
|
|
WRAP(fopen, FILE *, const char *path, const char *mode)
|
|
{
|
|
return CALL(fopen)( path, mode );
|
|
}
|
|
|
|
WRAP(fopen64, FILE *, const char *path, const char *mode)
|
|
{
|
|
return CALL(fopen64)( path, mode );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void UnloadSteam()
|
|
{
|
|
if ( s_SteamModule )
|
|
{
|
|
decltype( SteamAPI_Shutdown )* pfnSAPIShutdown = ( decltype( SteamAPI_Shutdown )* )Launcher_GetProcAddress( s_SteamModule, "SteamAPI_Shutdown" );
|
|
if ( pfnSAPIShutdown )
|
|
{
|
|
pfnSAPIShutdown();
|
|
s_bInittedSteam = false;
|
|
}
|
|
|
|
Launcher_CloseModule( s_SteamModule );
|
|
}
|
|
|
|
}
|
|
|
|
|
|
static bool LoadSteam( const char *pRootDir )
|
|
{
|
|
#if defined( _WIN64 )
|
|
#define STEAM_API_DLL_PATH "%s\\" PLATFORM_BIN_DIR "\\steam_api64.dll"
|
|
#elif defined( _WIN32 )
|
|
#define STEAM_API_DLL_PATH "%s\\" PLATFORM_BIN_DIR "\\steam_api.dll"
|
|
#elif defined( POSIX )
|
|
#define STEAM_API_DLL_PATH "%s/" PLATFORM_BIN_DIR "/libsteam_api.so"
|
|
#endif
|
|
|
|
char szBuffer[4096];
|
|
// Assemble the full path to our "steam_api.dll"
|
|
_snprintf( szBuffer, sizeof( szBuffer ), STEAM_API_DLL_PATH, pRootDir );
|
|
szBuffer[sizeof( szBuffer ) - 1] = '\0';
|
|
|
|
s_SteamModule = Launcher_LoadModule( szBuffer );
|
|
if ( !s_SteamModule )
|
|
{
|
|
MessageBox( 0, "Not able to load Steam API. Is " STEAM_API_DLL_PATH " missing?", "Launcher Error", MB_OK );
|
|
return false;
|
|
}
|
|
|
|
// Make a steam_appid.txt now, of just eg. Source SDK 2013 MP for this.
|
|
FILE *pFile = fopen( "steam_appid.txt", "w" );
|
|
if ( pFile )
|
|
{
|
|
fprintf( pFile, "%u\n", k_unMyModAppid );
|
|
fclose( pFile );
|
|
}
|
|
|
|
decltype(SteamAPI_Init) *pfnSAPIInit = (decltype( SteamAPI_Init ) *) GetProcAddress( s_SteamModule, "SteamAPI_Init" );
|
|
if ( !pfnSAPIInit )
|
|
{
|
|
MessageBox( 0, "SteamAPI_Init was not available!", "Launcher Error", MB_OK );
|
|
UnloadSteam();
|
|
return false;
|
|
}
|
|
|
|
if ( !pfnSAPIInit() )
|
|
{
|
|
MessageBox( 0, "SteamAPI_Init failed!", "Launcher Error", MB_OK );
|
|
UnloadSteam();
|
|
return false;
|
|
}
|
|
|
|
s_bInittedSteam = true;
|
|
return true;
|
|
}
|
|
|
|
static bool GetGameInstallDir( const char *pRootDir, char *pszBuf, int nBufSize )
|
|
{
|
|
if ( !LoadSteam( pRootDir ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
decltype( SteamAPI_GetHSteamUser )* pfnSteamAPI_GetHSteamUser = ( decltype( SteamAPI_GetHSteamUser )* )GetProcAddress( s_SteamModule, "SteamAPI_GetHSteamUser" );
|
|
if ( !pfnSteamAPI_GetHSteamUser )
|
|
{
|
|
MessageBox( 0, "SteamAPI_GetHSteamUser not present!", "Launcher Error", MB_OK );
|
|
return false;
|
|
}
|
|
|
|
decltype( SteamInternal_FindOrCreateUserInterface )* pfnSteamInternal_FindOrCreateUserInterface = ( decltype( SteamInternal_FindOrCreateUserInterface )* )GetProcAddress( s_SteamModule, "SteamInternal_FindOrCreateUserInterface" );
|
|
if ( !pfnSteamInternal_FindOrCreateUserInterface )
|
|
{
|
|
MessageBox( 0, "SteamInternal_FindOrCreateUserInterface not present!", "Launcher Error", MB_OK );
|
|
return false;
|
|
}
|
|
|
|
ISteamApps* pSteamApps = ( ISteamApps* )( pfnSteamInternal_FindOrCreateUserInterface( pfnSteamAPI_GetHSteamUser(), STEAMAPPS_INTERFACE_VERSION ) );
|
|
if ( !pSteamApps )
|
|
{
|
|
MessageBox( 0, "ISteamApps not present!", "Launcher Error", MB_OK );
|
|
return false;
|
|
}
|
|
|
|
uint32_t unLength = 0;
|
|
if ( pSteamApps->BIsAppInstalled( k_unSDK2013MPAppId ) )
|
|
{
|
|
unLength = pSteamApps->GetAppInstallDir( k_unSDK2013MPAppId, pszBuf, nBufSize );
|
|
}
|
|
|
|
UnloadSteam();
|
|
|
|
if ( unLength == 0 )
|
|
{
|
|
MessageBox( 0, "Source SDK 2013 Multiplayer (243750) must be installed to launch this mod.", "Launcher Error", MB_OK );
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Return the directory where this .exe is running from
|
|
// Output : char
|
|
//-----------------------------------------------------------------------------
|
|
#if !defined( _X360 )
|
|
|
|
static char *GetBaseDir( const char *pszBuffer )
|
|
{
|
|
static char basedir[ MAX_PATH ];
|
|
char szBuffer[ MAX_PATH ];
|
|
size_t j;
|
|
char *pBuffer = NULL;
|
|
|
|
strcpy( szBuffer, pszBuffer );
|
|
|
|
#ifdef _WIN32
|
|
pBuffer = strrchr( szBuffer,'\\' );
|
|
#else
|
|
pBuffer = strrchr( szBuffer,'/' );
|
|
#endif
|
|
if ( pBuffer )
|
|
{
|
|
*(pBuffer+1) = '\0';
|
|
}
|
|
|
|
strcpy( basedir, szBuffer );
|
|
|
|
j = strlen( basedir );
|
|
if (j > 0)
|
|
{
|
|
if ( ( basedir[ j-1 ] == '\\' ) ||
|
|
( basedir[ j-1 ] == '/' ) )
|
|
{
|
|
basedir[ j-1 ] = 0;
|
|
}
|
|
}
|
|
|
|
return basedir;
|
|
}
|
|
|
|
#ifdef WIN32
|
|
|
|
#include <process.h>
|
|
|
|
std::wstring GetExePath()
|
|
{
|
|
std::vector<WCHAR> exePath;
|
|
exePath.resize( MAX_PATH + 1 );
|
|
|
|
DWORD len = ::GetModuleFileNameW( NULL, exePath.data(), MAX_PATH );
|
|
if ( !len || len == MAX_PATH )
|
|
return L"";
|
|
exePath.resize( len + 1 );
|
|
|
|
return exePath.data();
|
|
}
|
|
|
|
std::wstring GetExeName()
|
|
{
|
|
std::wstring fullPath = GetExePath();
|
|
auto n = fullPath.find_last_of( L'\\' );
|
|
|
|
return ( n != std::wstring::npos )
|
|
? fullPath.substr( n + 1 )
|
|
: fullPath;
|
|
}
|
|
|
|
std::wstring GetDefaultGameName()
|
|
{
|
|
std::wstring sName = GetExeName();
|
|
auto n = sName.find_last_of( L'.' );
|
|
|
|
return ( n != std::wstring::npos )
|
|
? sName.substr( 0, n )
|
|
: sName;
|
|
}
|
|
|
|
static void RelaunchAs64Bit( std::vector<std::wstring> &args )
|
|
{
|
|
#ifndef _WIN64
|
|
if ( args.size() == 0 )
|
|
{
|
|
// oh no
|
|
return;
|
|
}
|
|
|
|
auto iter = args[0].find( L".exe" );
|
|
if ( iter == std::string::npos )
|
|
return;
|
|
args[0].replace( iter, wcslen( L".exe" ), L"_win64.exe" );
|
|
|
|
std::wstring sExecutable = args[0];
|
|
|
|
std::vector<wchar_t*> pArgs;
|
|
for ( std::wstring& arg : args )
|
|
{
|
|
arg = L"\"" + arg + L"\"";
|
|
pArgs.push_back( (wchar_t *)arg.c_str() );
|
|
}
|
|
|
|
pArgs.push_back( NULL );
|
|
|
|
_wexecvp( sExecutable.c_str(), &pArgs[0]);
|
|
exit( 1 );
|
|
#endif
|
|
}
|
|
|
|
static BOOL FileExists( wchar_t *pwcsPath )
|
|
{
|
|
DWORD dwAttrib = GetFileAttributesW( pwcsPath );
|
|
|
|
return ( dwAttrib != INVALID_FILE_ATTRIBUTES && !( dwAttrib & FILE_ATTRIBUTE_DIRECTORY ) );
|
|
}
|
|
|
|
bool Is64BitWindows()
|
|
{
|
|
#if defined(_WIN64)
|
|
return true;
|
|
#elif defined(_WIN32)
|
|
BOOL f64 = FALSE;
|
|
return IsWow64Process( GetCurrentProcess(), &f64 ) && f64;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
static void HandleRelaunching()
|
|
{
|
|
#ifndef _WIN64
|
|
// Can't relaunch if on 32-bit only windows.
|
|
if ( !Is64BitWindows() )
|
|
return;
|
|
|
|
int nArgs = 0;
|
|
LPWSTR* pBaseArgs = CommandLineToArgvW( GetCommandLineW(), &nArgs );
|
|
|
|
std::vector<std::wstring> pArgs;
|
|
for ( int i = 0; i < nArgs; i++ )
|
|
{
|
|
pArgs.push_back( pBaseArgs[i] );
|
|
}
|
|
|
|
std::wstring sDefaultGameName = GetDefaultGameName();
|
|
|
|
const wchar_t *pGameArg = NULL;
|
|
bool bNextIsGameArg = false;
|
|
for ( std::wstring &arg : pArgs )
|
|
{
|
|
if ( bNextIsGameArg )
|
|
{
|
|
pGameArg = arg.c_str();
|
|
bNextIsGameArg = false;
|
|
}
|
|
else if ( arg == L"-game" )
|
|
{
|
|
bNextIsGameArg = true;
|
|
}
|
|
else if ( arg == L"-force32bit" )
|
|
{
|
|
return;
|
|
}
|
|
else if ( arg == L"-force64bit" )
|
|
{
|
|
RelaunchAs64Bit( pArgs );
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( !pGameArg )
|
|
{
|
|
pGameArg = sDefaultGameName.c_str();
|
|
}
|
|
|
|
std::wstring sServerDLL = pGameArg;
|
|
if ( sServerDLL.empty() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( sServerDLL[sServerDLL.size() - 1] != L'\\' )
|
|
{
|
|
sServerDLL += L"\\";
|
|
}
|
|
|
|
sServerDLL += L"bin\\x64\\server.dll";
|
|
|
|
if ( FileExists( (wchar_t *)sServerDLL.c_str() ) )
|
|
{
|
|
RelaunchAs64Bit( pArgs );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
|
|
{
|
|
HandleRelaunching();
|
|
|
|
// Must add 'bin' to the path....
|
|
char* pPath = getenv("PATH");
|
|
|
|
char szBuffer[4096];
|
|
|
|
// Use the .EXE name to determine the root directory
|
|
char moduleName[MAX_PATH];
|
|
if ( !GetModuleFileName( hInstance, moduleName, MAX_PATH ) )
|
|
{
|
|
MessageBox( 0, "Failed calling GetModuleFileName", "Launcher Error", MB_OK );
|
|
return 0;
|
|
}
|
|
|
|
// Get the root directory the .exe is in
|
|
char* pRootDir = GetBaseDir( moduleName );
|
|
const char *pBinaryGameDir = pRootDir;
|
|
char szGameInstallDir[4096];
|
|
if ( !GetGameInstallDir( pRootDir, szGameInstallDir, 4096 ) )
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
pBinaryGameDir = szGameInstallDir;
|
|
|
|
SetEnvironmentVariableA( "SDK_EXEC_DIR", szGameInstallDir );
|
|
|
|
#define LAUNCHER_DLL_PATH "%s\\" PLATFORM_BIN_DIR "\\launcher.dll"
|
|
#define LAUNCHER_PATH "%s\\" PLATFORM_BIN_DIR
|
|
|
|
_snprintf( szBuffer, sizeof( szBuffer ), "PATH=" LAUNCHER_PATH ";%s", pBinaryGameDir, pPath );
|
|
szBuffer[sizeof( szBuffer ) - 1] = '\0';
|
|
_putenv( szBuffer );
|
|
|
|
// Assemble the full path to our "launcher.dll"
|
|
_snprintf( szBuffer, sizeof( szBuffer ), LAUNCHER_DLL_PATH, pBinaryGameDir );
|
|
szBuffer[sizeof( szBuffer ) - 1] = '\0';
|
|
|
|
// STEAM OK ... filesystem not mounted yet
|
|
HINSTANCE launcher = LoadLibraryEx( szBuffer, NULL, LOAD_WITH_ALTERED_SEARCH_PATH );
|
|
|
|
if ( !launcher )
|
|
{
|
|
char *pszError;
|
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&pszError, 0, NULL);
|
|
|
|
char szBuf[1024];
|
|
_snprintf(szBuf, sizeof( szBuf ), "Failed to load the launcher DLL:\n\n%s", pszError);
|
|
szBuf[sizeof( szBuf ) - 1] = '\0';
|
|
MessageBox( 0, szBuf, "Launcher Error", MB_OK );
|
|
|
|
LocalFree(pszError);
|
|
return 0;
|
|
}
|
|
|
|
LauncherMain_t main = (LauncherMain_t)GetProcAddress( launcher, "LauncherMain" );
|
|
return main( hInstance, hPrevInstance, lpCmdLine, nCmdShow );
|
|
}
|
|
|
|
#elif defined (POSIX)
|
|
|
|
#if defined( LINUX )
|
|
|
|
bool GetExePath( char *pszPath, size_t size )
|
|
{
|
|
return readlink("/proc/self/exe", pszPath, size ) > 0;
|
|
}
|
|
|
|
#include <fcntl.h>
|
|
|
|
static bool IsDebuggerPresent( int time )
|
|
{
|
|
// Need to get around the __wrap_open() stuff. Just find the open symbol
|
|
// directly and use it...
|
|
typedef int (open_func_t)( const char *pathname, int flags, mode_t mode );
|
|
open_func_t *open_func = (open_func_t *)dlsym( RTLD_NEXT, "open" );
|
|
|
|
if ( open_func )
|
|
{
|
|
for ( int i = 0; i < time; i++ )
|
|
{
|
|
int tracerpid = -1;
|
|
|
|
int fd = (*open_func)( "/proc/self/status", O_RDONLY, S_IRUSR );
|
|
if (fd >= 0)
|
|
{
|
|
char buf[ 4096 ];
|
|
static const char tracerpid_str[] = "TracerPid:";
|
|
|
|
const int len = read( fd, buf, sizeof(buf) - 1 );
|
|
if ( len > 0 )
|
|
{
|
|
buf[ len ] = 0;
|
|
|
|
const char *str = strstr( buf, tracerpid_str );
|
|
tracerpid = str ? atoi( str + sizeof( tracerpid_str ) ) : -1;
|
|
}
|
|
|
|
close( fd );
|
|
}
|
|
|
|
if ( tracerpid > 0 )
|
|
return true;
|
|
|
|
sleep( 1 );
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void WaitForDebuggerConnect( int argc, char *argv[], int time )
|
|
{
|
|
for ( int i = 1; i < argc; i++ )
|
|
{
|
|
if ( strstr( argv[i], "-wait_for_debugger" ) )
|
|
{
|
|
printf( "\nArg -wait_for_debugger found.\nWaiting %dsec for debugger...\n", time );
|
|
printf( " pid = %d\n", getpid() );
|
|
|
|
if ( IsDebuggerPresent( time ) )
|
|
printf("Debugger connected...\n\n");
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
static void WaitForDebuggerConnect( int argc, char *argv[], int time )
|
|
{
|
|
}
|
|
|
|
#endif // !LINUX
|
|
|
|
|
|
static const char *GetExecutableModName( char *pszExePath )
|
|
{
|
|
static char s_szFinalFilename[ MAX_PATH + 1 ] = "hl2";
|
|
|
|
char szExePath[ MAX_PATH + 1 ];
|
|
strncpy( szExePath, pszExePath, sizeof( szExePath ) );
|
|
szExePath[ MAX_PATH ] = 0;
|
|
|
|
if ( !*szExePath )
|
|
return s_szFinalFilename;
|
|
|
|
const char *pszLastSeparator = strrchr( szExePath, '/' );
|
|
if ( !pszLastSeparator )
|
|
return s_szFinalFilename;
|
|
|
|
strncpy( s_szFinalFilename, pszLastSeparator + 1, sizeof( s_szFinalFilename ) );
|
|
s_szFinalFilename[ MAX_PATH ] = 0;
|
|
char *pszLastUnderscore = NULL;
|
|
char* pszLastDot = NULL;
|
|
for ( char *ch = s_szFinalFilename; *ch != 0; ch++ )
|
|
{
|
|
if ( *ch == '_' )
|
|
{
|
|
pszLastUnderscore = ch;
|
|
}
|
|
|
|
if ( *ch == '.' )
|
|
{
|
|
pszLastDot = ch;
|
|
}
|
|
}
|
|
|
|
if ( pszLastUnderscore )
|
|
*pszLastUnderscore = 0;
|
|
|
|
if ( pszLastDot )
|
|
*pszLastDot = 0;
|
|
|
|
return s_szFinalFilename;
|
|
}
|
|
|
|
int main( int argc, char *argv[] )
|
|
{
|
|
char moduleName[MAX_PATH];
|
|
if ( !GetExePath( moduleName, sizeof( moduleName ) ) )
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
char* pRootDir = GetBaseDir( moduleName );
|
|
|
|
const char *pBinaryGameDir = pRootDir;
|
|
|
|
char szGameInstallDir[4096];
|
|
if ( !GetGameInstallDir( pRootDir, szGameInstallDir, 4096 ) )
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
char szExecutable[8192];
|
|
snprintf(szExecutable, sizeof(szExecutable), "%s/hl2.sh", szGameInstallDir );
|
|
|
|
std::vector<char *> new_argv;
|
|
|
|
new_argv.push_back( szExecutable );
|
|
|
|
bool bHasGame = false;
|
|
for ( int i = 1; i < argc; i++ )
|
|
{
|
|
if ( !strcmp( argv[i], "-game" ) )
|
|
{
|
|
bHasGame = true;
|
|
}
|
|
|
|
new_argv.push_back(argv[i]);
|
|
}
|
|
|
|
char szGamePath[8192];
|
|
if ( !bHasGame )
|
|
{
|
|
new_argv.push_back("-game");
|
|
|
|
const char *pModName = GetExecutableModName( moduleName );
|
|
snprintf( szGamePath, sizeof( szGamePath ), "%s/%s", pRootDir, pModName );
|
|
|
|
printf( "[Source Mod Launcher] Launching default game: %s\n", pModName );
|
|
|
|
new_argv.push_back(szGamePath);
|
|
}
|
|
|
|
new_argv.push_back(NULL);
|
|
|
|
execvp( szExecutable, new_argv.data() );
|
|
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
#error
|
|
#endif // WIN32 || POSIX
|
|
|
|
#endif
|