csgo-2018-source/launcher_main/main.cpp
2021-07-24 21:11:47 -07:00

986 lines
28 KiB
C++
Raw Permalink Blame History

//========= Copyright <20> 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: A redirection tool that allows the DLLs to reside elsewhere.
//
//=====================================================================================//
#if defined( _WIN32 ) && !defined( _X360 )
#include <windows.h>
#include <stdio.h>
#include <assert.h>
#include <direct.h>
#endif
#if defined( _X360 )
#define _XBOX
#include <xtl.h>
#include <xbdm.h>
#undef _XBOX
#include <stdio.h>
#include <assert.h>
#include "xbox\xbox_core.h"
#include "xbox\xbox_launch.h"
#elif defined( SN_TARGET_PS3 )
#include "../public/ps3_pathinfo.h"
#include <stddef.h>
#include <cell/fios/fios_common.h>
#include <cell/fios/fios_memory.h>
#include <cell/fios/fios_configuration.h>
#include <cell/fios/fios_time.h>
#include <sys/tty.h>
#include <sys/ppu_thread.h>
#include "tier0/vprof_sn.h"
#include "errorrenderloop.h"
//#if defined( VPROF_SN_LEVEL )
#include "sn/libsntuner.h"
#include "libsn.h"
//#endif
#endif
#ifdef POSIX
#include <stdio.h>
#include <stdlib.h>
#ifndef SN_TARGET_PS3
#include <dlfcn.h>
#endif // SN_TARGET_PS3
#include <limits.h>
#include <string.h>
#define MAX_PATH PATH_MAX
#endif
#include "tier0/platform.h"
#include "tier0/basetypes.h"
#if defined( VPCGAME )
#define _VPCGAME_STRING_HACK2(x) #x
#define _VPCGAME_STRING_HACK1(x) _VPCGAME_STRING_HACK2(x)
#define VPCGAME_STRING _VPCGAME_STRING_HACK1(VPCGAME)
#endif
#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
//-----------------------------------------------------------------------------
// Purpose: Return the directory where this .exe is running from
// Output : char
//-----------------------------------------------------------------------------
#if !defined( _X360 )
#ifdef WIN32
static char *GetBaseDir( const char *pszBuffer )
{
static char basedir[ MAX_PATH ];
char szBuffer[ MAX_PATH ];
size_t j;
char *pBuffer = NULL;
strcpy( szBuffer, pszBuffer );
pBuffer = strrchr( szBuffer,'\\' );
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;
}
int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
// Must add 'bin' to the path....
char* pPath = getenv("PATH");
// Use the .EXE name to determine the root directory
char moduleName[ MAX_PATH ];
char szBuffer[4096];
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* pBinPath =
#ifdef _WIN64
"\\x64"
#else
""
#endif
;
#ifdef _DEBUG
int len =
#endif
_snprintf( szBuffer, sizeof( szBuffer ), "PATH=%s\\bin%s\\;%s", pRootDir, pBinPath, pPath );
szBuffer[sizeof( szBuffer ) - 1] = '\0';
assert( len < sizeof( szBuffer ) );
_putenv( szBuffer );
// Assemble the full path to our "launcher.dll"
_snprintf( szBuffer, sizeof( szBuffer ), "%s\\bin%s\\launcher.dll", pRootDir, pBinPath );
szBuffer[sizeof( szBuffer ) - 1] = '\0';
// STEAM OK ... filesystem not mounted yet
#if defined(_X360)
HINSTANCE launcher = LoadLibrary( szBuffer );
#else
HINSTANCE launcher = LoadLibraryEx( szBuffer, NULL, LOAD_WITH_ALTERED_SEARCH_PATH );
#endif
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( SN_TARGET_PS3 )
#if defined( __GCC__ )
#define COMPILER_GCC
#elif defined( __SNC__ )
#define COMPILER_SNC
#endif
#include "../public/tls_ps3.h"
#include "sys/process.h"
// We need to avoid printf before we
// configure our custom memory allocator
#define printf(...) ((void)0)
#include "../common/ps3/ps3_helpers.h"
#ifdef APPCHANGELISTVERSION
// write the changelist number into the executable so that the GUID changes between builds.
// previously, setting the version number via the SYS_MODULE_INFO was good enough to do
// this, but not after sdk 350.
volatile unsigned int clnumber = APPCHANGELISTVERSION;
__attribute__ ((noinline)) void DummyFuncForUpdatingGUIDs( char *pOut )
{
sprintf( pOut, "%x", clnumber );
}
#else // absent appchangelistversion, invent one
volatile unsigned int clnumber = 0;
#define DUMMY_VER_STRING __DATE__ " " __TIME__
volatile char dummyVersionDateString[] = DUMMY_VER_STRING "\n";
__attribute__ ((noinline)) void DummyFuncForUpdatingGUIDs( char *pOut )
{
sprintf( pOut, DUMMY_VER_STRING );
}
#undef DUMMY_VER_STRING
#endif
// 1 Mb stack is maximum allowed for the main thread
// and we will make good use of it
SYS_PROCESS_PARAM( 1000, 1 * 1024 * 1024 )
/////////////////////////////////////////////////////////////////////////////////////////////////////
// All thread-local storage must reside in the ELF and be exported for PRXes to use it
/////////////////////////////////////////////////////////////////////////////////////////////////////
__thread TLSGlobals gTLSGlobals =
{
// TLS values/flags
/*nThreadLocalStateIndex*/ 0,
/*TLSValues*/ { NULL },
/*TLSFlags*/ { false },
/*bWaitObjectsCreated*/ false,
/*WaitObjectsSemaphore*/ 0,
/*pCurThread*/ NULL,
/*nThreadID*/ 0,
// Engine TLS data (zip/console/splitslot)
/*uiEngineZipLastErrorZ*/ 0,
/*bEngineConsoleIsInSpew*/ false,
/*pEngineSplitSlot*/ NULL,
// Malloc debugging TLS data
/*pMallocDbgInfoStack*/ NULL,
/*nMallocDbgInfoStackDepth*/ 0,
// Filesystem read filename buffer
/*pFileSystemReadFilename*/ NULL,
// Material system render context
/*pMaterialSystemRenderContext*/ NULL,
// Physics virtual mesh frame locks
/*pPhysicsVirtualMeshFrameLocks*/ NULL,
/*bNormalQuitRequested*/ false
};
TLSGlobals *GetTLSGlobals_ELF() { return &gTLSGlobals; }
extern CPs3ContentPathInfo g_Ps3GameDataPathInfo;
template< typename BaseStruct >
struct PS3MainParameters : public BaseStruct
{
PS3MainParameters() { memset( this, 0, sizeof( BaseStruct ) ); BaseStruct::cbSize = sizeof( BaseStruct ); }
};
struct PS3_Launch_t
{
explicit PS3_Launch_t( char const *szPrxName, PS3_PrxLoadParametersBase_t *pParams ) :
m_szPrxName( szPrxName ), m_pPrxParams( pParams )
{
m_iResult = PS3_PrxLoad( m_szPrxName, m_pPrxParams );
if ( m_iResult < CELL_OK )
{
printf( "ERROR: %s PRX load failed: 0x%08x\n", m_szPrxName, m_iResult );
}
else
{
printf( "Loaded: %s (0x%08x)\n", m_szPrxName, m_iResult );
}
}
char const *m_szPrxName;
PS3_PrxLoadParametersBase_t *m_pPrxParams;
int m_iResult;
};
static const char *LauncherMainSPRXPath( const char *modulename, char *buf, int buflen = CELL_GAME_PATH_MAX ) // formats a path to the module. returns a pointer to the buf param for convenience.
{
snprintf( buf, buflen, "%s/%s" DLL_EXT_STRING, g_Ps3GameDataPathInfo.PrxPath(), modulename );
return buf;
}
#if defined( SN_TARGET_PS3 )
void TunerMarkerPush( const char * pName )
{
//#if defined( VPROF_SN_LEVEL )
snPushMarker( pName );
//#endif
}
void TunerMarkerPop()
{
//#if defined( VPROF_SN_LEVEL )
snPopMarker();
//#endif
}
// this is debug-only counter; never use it for anything other than debugging!
uint64_t g_nDebugSwapBufferCount = 0;
void TunerSwapBufferMarker()
{
// this dummy function is only required as a patch-through for Tuner that attaches to the game after the game has been started, as a convenience funciton/
// it must be called at every frame boundary (presumably at/after psglSwap )
g_nDebugSwapBufferCount++;
}
#endif
PS3_PrxModuleEntry_t *g_pPrxModulesList = NULL;
PS3_PrxModuleEntry_t ** PS3_PrxGetModulesList() { return &g_pPrxModulesList; }
void TestThreadProc( uint64_t id )
{
printf( "Hello from PPU thread %lld\n", id );
sys_ppu_thread_exit( id );
}
void TestThreads( int nLevel = 0)
{
printf("testing threads\n");
const int numThreads = 20;
sys_ppu_thread_t id[numThreads];
for( int i = 0;i < numThreads; ++i )
{
if( nLevel > 0 )
TestThreads( nLevel - 1 );
if( CELL_OK != sys_ppu_thread_create( &id[i], TestThreadProc, i, 1001, 64*1024, SYS_PPU_THREAD_CREATE_JOINABLE, "SimpleThread" ) )
{
printf("ERROR: cannot create thread\n");
return;
}
}
for( int i = 0;i < numThreads; ++i )
{
uint64_t res;
sys_ppu_thread_join( id[i], &res );
if( res != i )
{
printf("ERROR: invalid thread return value\n");
return;
}
}
}
int MainImpl( int argc, char *argv[] )
{
// #ifdef _CERT // possibly enable it for ship to disable command line cheating?
#if 0
// Disable command line support for shipping unless -certcmdline specified
if ( ( argc > 1 ) && !strcmp( argv[1], "-certcmdline" ) )
{
argc = 1;
}
#endif
// this is the very first timing message, before tier0 is even initialized and we can use any
// logging or timing facilities; this is the baseline to measure loading times
cell::fios::abstime_t fiosLaunchTime = cell::fios::FIOSGetCurrentTime();
#ifndef _CERT
{
double flTime = cell::fios::FIOSAbstimeToMicroseconds( fiosLaunchTime ) * 1e-6;
char buffer[4096];
int nMessageSize = snprintf(buffer, sizeof(buffer), "--------------------------------------\n 0.0000 / %8.4f : launcher_main(", flTime );
for( int i = 0;i < argc && nMessageSize < sizeof(buffer)-4; ++i )
{
// add delimiters
if( i > 0 )
buffer[nMessageSize++] = ',';
nMessageSize += snprintf( buffer + nMessageSize, sizeof(buffer) - nMessageSize, " \"%s\"", argv[i] );
}
nMessageSize += snprintf(buffer + nMessageSize, sizeof(buffer) - nMessageSize, " )\n" );
unsigned wrote;
sys_tty_write( SYS_TTYP6, buffer, nMessageSize, &wrote );
}
#endif
// this is a dummy operation to force the compiler to not elide the
// changelist GUID. It's really necessary, because otherwise the compiler
// will notice the function isn't called, and elide it, and the GUID string,
// altogether. Because the compiler "can't" know what argc will be, it has
// to compile in the function call here. This is the only way to guarantee
// that different builds will have different GUIDs, because we don't often
// change launcher_main between versions, and the PRXes don't get individual
// GUIDs in the dump.
// Don't pass in more than one million commandline
// parameters or this will corrupt the one millionth.
if ( argc > 100000000 )
{
DummyFuncForUpdatingGUIDs( argv[100000000] );
}
char path[CELL_GAME_PATH_MAX];
bool bDevHddCfgOnly = false;
bool bRunLauncherMain = true;
bool bSupportPathLegacyArgs = true;
bool bEnableMlaa = true;
#ifdef HDD_BOOT
unsigned int uiInitFlags = CPs3ContentPathInfo::INIT_PRX_ON_HDD | CPs3ContentPathInfo::INIT_IMAGE_ON_HDD;
#else
unsigned int uiInitFlags = CPs3ContentPathInfo::INIT_RETAIL_MODE; // assume that if no arguments are specified we are going to run in retail mode
#endif
// uncomment the following in order to allow starting game in /app_home from XMB (shortcutting creating disk image)
// uiInitFlags = CPs3ContentPathInfo::INIT_PRX_APP_HOME | CPs3ContentPathInfo::INIT_IMAGE_APP_HOME;
for ( int k = 0; k < argc; ++ k )
{
if( !strcmp( "-noMlaa", argv[k] ) )
{
bEnableMlaa = false;
}
else if ( !strcmp( "-errorrenderloop", argv[k] ) )
{
return -1;
}
else if ( !strcmp( "-devhddcfgonly", argv[k] ) )
{
bDevHddCfgOnly = true;
}
else if ( !strcmp( "-nolaunchermain", argv[k] ) )
{
bRunLauncherMain = false;
}
else if ( !strcmp( "-syscacheclear", argv[k] ) )
{
uiInitFlags |= CPs3ContentPathInfo::INIT_SYS_CACHE_CLEAR;
}
else if ( !strncmp( "-path_retail", argv[k], 12 ) )
{
bSupportPathLegacyArgs = false;
if ( argv[k][12] )
uiInitFlags &=~CPs3ContentPathInfo::INIT_RETAIL_MODE;
}
else if ( !strncmp( "-path_prx_", argv[k], 10 ) )
{
char chPrxPathMode = argv[k][10];
switch ( chPrxPathMode )
{
case 'h': uiInitFlags |= CPs3ContentPathInfo::INIT_PRX_ON_HDD; break;
case 'b': uiInitFlags |= CPs3ContentPathInfo::INIT_PRX_ON_BDVD; break;
case 'a': uiInitFlags |= CPs3ContentPathInfo::INIT_PRX_APP_HOME; break;
}
}
else if ( !strncmp( "-path_img_", argv[k], 10 ) )
{
char chPrxPathMode = argv[k][10];
switch ( chPrxPathMode )
{
case 'h': uiInitFlags |= CPs3ContentPathInfo::INIT_IMAGE_ON_HDD; break;
case 'b': uiInitFlags |= CPs3ContentPathInfo::INIT_IMAGE_ON_BDVD; break;
case 'a': uiInitFlags |= CPs3ContentPathInfo::INIT_IMAGE_APP_HOME; break;
}
}
// LEGACY PARAMETERS because people are used to running with them (need to clean up some time later)
else if ( bSupportPathLegacyArgs && !strcmp( "-dev", argv[k] ) )
{
uiInitFlags &=~CPs3ContentPathInfo::INIT_RETAIL_MODE;
uiInitFlags |= CPs3ContentPathInfo::INIT_IMAGE_APP_HOME;
uiInitFlags |= CPs3ContentPathInfo::INIT_PRX_APP_HOME;
}
else if ( bSupportPathLegacyArgs && !strcmp( "-ps3hd", argv[k] ) )
{
uiInitFlags &=~CPs3ContentPathInfo::INIT_RETAIL_MODE;
uiInitFlags |= CPs3ContentPathInfo::INIT_IMAGE_ON_HDD;
uiInitFlags |= CPs3ContentPathInfo::INIT_PRX_APP_HOME;
}
else if ( bSupportPathLegacyArgs && !strcmp( "-nops3hd", argv[k] ) )
{
uiInitFlags &=~CPs3ContentPathInfo::INIT_RETAIL_MODE;
uiInitFlags |= CPs3ContentPathInfo::INIT_IMAGE_APP_HOME;
uiInitFlags |= CPs3ContentPathInfo::INIT_PRX_APP_HOME;
}
else if ( bSupportPathLegacyArgs && !strcmp( "-dev_bdvd", argv[k] ) )
{
uiInitFlags &=~CPs3ContentPathInfo::INIT_RETAIL_MODE;
uiInitFlags |= CPs3ContentPathInfo::INIT_IMAGE_ON_BDVD;
uiInitFlags |= CPs3ContentPathInfo::INIT_PRX_APP_HOME;
}
// END LEGACY PARAMETERS
}
// uncomment the following to hard code for Eurogamer (as if running -dev)
#ifdef _PS3
// uiInitFlags &=~CPs3ContentPathInfo::INIT_RETAIL_MODE;
#endif
int iPathInfoInitResult = g_Ps3GameDataPathInfo.Init( uiInitFlags );
if ( iPathInfoInitResult < 0 )
return iPathInfoInitResult;
if ( bDevHddCfgOnly )
return 0;
PS3MainParameters< PS3_LoadTier0_Parameters_t > tier0;
tier0.pfnGetTlsGlobals = GetTLSGlobals_ELF;
tier0.pPS3PathInfo = &g_Ps3GameDataPathInfo;
tier0.fiosLaunchTime = fiosLaunchTime;
tier0.nCLNumber = clnumber;
tier0.pfnPushMarker = TunerMarkerPush;
tier0.pfnPopMarker = TunerMarkerPop;
tier0.pfnSwapBufferMarker = TunerSwapBufferMarker;
tier0.ppPrxModulesList = PS3_PrxGetModulesList();
tier0.m_pGcmSharedData = &g_gcmSharedData;
#ifndef _CERT
tier0.snRawSPULockHandler = snRawSPULockHandler;
tier0.snRawSPUUnlockHandler = snRawSPUUnlockHandler;
tier0.snRawSPUNotifyCreation = snRawSPUNotifyCreation;
tier0.snRawSPUNotifyDestruction = snRawSPUNotifyDestruction;
tier0.snRawSPUNotifyElfLoad = snRawSPUNotifyElfLoad;
tier0.snRawSPUNotifyElfLoadNoWait = snRawSPUNotifyElfLoadNoWait;
tier0.snRawSPUNotifyElfLoadAbs = snRawSPUNotifyElfLoadAbs;
tier0.snRawSPUNotifyElfLoadAbsNoWait = snRawSPUNotifyElfLoadAbsNoWait;
tier0.snRawSPUNotifySPUStopped = snRawSPUNotifySPUStopped;
tier0.snRawSPUNotifySPUStarted = snRawSPUNotifySPUStarted;
#endif
(void)bEnableMlaa; // we'll use it if we need to init GCM and start rendering right away
/*
g_gcmSharedData.m_nIoMemorySize = bEnableMlaa ? 5 * 1024 * 1024 : 1 * 1024 * 1024;
sys_addr_t pIoAddress = NULL;
int nError = sys_memory_allocate( g_gcmSharedData.m_nIoMemorySize, SYS_MEMORY_PAGE_SIZE_1M, &pIoAddress );
if( CELL_OK != nError || !pIoAddress )
{
// cannot allocate IO memory
return -2;
}
int32 result = cellGcmInit( m_nCmdSize, m_nIoSize, m_pIoAddress );
if ( result < CELL_OK )
return result;
g_gcmSharedData.m_pIoMemory = ( void* )pIoAddress;
*/
PS3_Launch_t tier0Launch( LauncherMainSPRXPath( "tier0", path ), &tier0 );
if( tier0Launch.m_iResult < CELL_OK )
{
return -1;
}
int iAppRetCode = 0;
PS3MainParameters< PS3_PrxLoadParametersBase_t > vstdlib;
PS3_Launch_t vstdlibLaunch( LauncherMainSPRXPath( "vstdlib", path ), &vstdlib );
if( vstdlibLaunch.m_iResult >= CELL_OK )
{
#ifndef NO_STEAM
PS3MainParameters< PS3_PrxLoadParametersBase_t > steamapi;
PS3_Launch_t steamapiLaunch( LauncherMainSPRXPath( "steam_api", path ), &steamapi );
if ( steamapiLaunch.m_iResult >= CELL_OK )
{
#endif
PS3MainParameters< PS3_LoadLauncher_Parameters_t > launcher;
PS3_Launch_t launcherLaunch( LauncherMainSPRXPath( "launcher", path ), &launcher );
if ( launcher.pfnLauncherMain )
{
printf( "Launching...\n" );
iAppRetCode = bRunLauncherMain ? (*launcher.pfnLauncherMain)( argc, argv ) : 0;
printf( "Shutting down...\n" );
launcher.pfnLauncherShutdown();
}
else
{
printf( "ERROR: failed to obtain LauncherMain entry point!\n" );
}
PS3_PrxUnload( launcher.sysPrxId );
#ifndef NO_STEAM
PS3_PrxUnload( steamapi.sysPrxId );
}
#endif
PS3_PrxUnload( vstdlib.sysPrxId );
}
// Before tier0 unloads make sure that there are no modules remaining loaded
#if !defined( _CERT )
for ( PS3_PrxModuleEntry_t *pEntry = *PS3_PrxGetModulesList(); pEntry; pEntry = pEntry->pNextModule )
{
if ( strstr( pEntry->chName, "/tier0" DLL_EXT_STRING ) )
continue;
unsigned int dummy;
char const *szWarnMsg = "EXITING WITH PRX MODULE: ";
sys_tty_write( SYS_TTYP6, szWarnMsg, strlen( szWarnMsg ), &dummy );
sys_tty_write( SYS_TTYP6, pEntry->chName, strlen( pEntry->chName ), &dummy );
szWarnMsg = "\n";
sys_tty_write( SYS_TTYP6, szWarnMsg, strlen( szWarnMsg ), &dummy );
}
#endif
tier0.pfnTier0Shutdown();
PS3_PrxUnload( tier0.sysPrxId );
return iAppRetCode;
}
int main( int argc, char *argv[] )
{
// Init LibSn
#ifndef _CERT
snInit();
#endif
int nReturn = MainImpl( argc, argv );
#ifndef _PS3
// if( !gTLSGlobals.bNormalQuitRequested )
// {
// printf("no normal quit requested, starting error render loop\n");
// ErrorRenderLoop loop;
// loop.Run();
// printf("Error render loop finished\n");
// }
#endif
#if !defined( _CERT )
if ( 1 )
{
unsigned int dummy;
char const *szWarnMsg =
(*PS3_PrxGetModulesList())
? "------- WARNING: RETURNING FROM MAIN WITH PRX MODULES RUNNING --------\n"
: "--------------------------------BYE-----------------------------------\n";
sys_tty_write( SYS_TTYP6, szWarnMsg, strlen( szWarnMsg ), &dummy );
}
#endif
return nReturn;
}
#elif defined (POSIX)
int main( int argc, char *argv[] )
{
#ifdef PLATFORM_64BITS
#ifdef OSX
const char *pLauncherPath = "bin/osx64/launcher" DLL_EXT_STRING;
#else
const char *pLauncherPath = "bin/linux64/launcher" DLL_EXT_STRING;
#endif
#else
const char *pLauncherPath = "bin/launcher" DLL_EXT_STRING;
#endif
void *launcher = dlopen( pLauncherPath, RTLD_NOW );
if ( !launcher )
{
printf( "Failed to load the launcher (%s)\n", dlerror() );
while(1);
return 0;
}
LauncherMain_t main = (LauncherMain_t)dlsym( launcher, "LauncherMain" );
if ( !main )
{
printf( "Failed to load the launcher entry proc\n" );
while(1);
return 0;
}
return main( argc, argv );
}
#else
#error
#endif // WIN32 || POSIX
#else // X360
//-----------------------------------------------------------------------------
// 360 Quick and dirty command line parsing. Returns true if key found,
// false otherwise. Caller can optionally get next argument.
//-----------------------------------------------------------------------------
bool ParseCommandLineArg( const char *pCmdLine, const char* pKey, char* pValueBuff = NULL, int valueBuffSize = 0 )
{
int keyLen = (int)strlen( pKey );
const char* pArg = pCmdLine;
for ( ;; )
{
// scan for match
pArg = strstr( (char*)pArg, pKey );
if ( !pArg )
{
return false;
}
// found, but could be a substring
if ( pArg[keyLen] == '\0' || pArg[keyLen] == ' ' )
{
// exact match
break;
}
pArg += keyLen;
}
if ( pValueBuff )
{
// caller wants next token
// skip past key and whitespace
pArg += keyLen;
while ( *pArg == ' ' )
{
pArg++;
}
int i;
for ( i=0; i<valueBuffSize; i++ )
{
pValueBuff[i] = *pArg;
if ( *pArg == '\0' || *pArg == ' ' )
break;
pArg++;
}
pValueBuff[i] = '\0';
}
return true;
}
//-----------------------------------------------------------------------------
// 360 Quick and dirty command line arg stripping.
//-----------------------------------------------------------------------------
void StripCommandLineArg( const char *pCmdLine, char *pNewCmdLine, const char *pStripArg )
{
// cannot operate in place
assert( pCmdLine != pNewCmdLine );
int numTotal = strlen( pCmdLine ) + 1;
const char* pArg = strstr( pCmdLine, pStripArg );
if ( !pArg )
{
strcpy( pNewCmdLine, pCmdLine );
return;
}
int numDiscard = strlen( pStripArg );
while ( pArg[numDiscard] && ( pArg[numDiscard] != '-' && pArg[numDiscard] != '+' ) )
{
// eat whitespace up to the next argument
numDiscard++;
}
memcpy( pNewCmdLine, pCmdLine, pArg - pCmdLine );
memcpy( pNewCmdLine + ( pArg - pCmdLine ), (void*)&pArg[numDiscard], numTotal - ( pArg + numDiscard - pCmdLine ) );
// ensure we don't leave any trailing whitespace, occurs if last arg is stripped
int len = strlen( pNewCmdLine );
while ( len > 0 && pNewCmdLine[len-1] == ' ' )
{
len--;
}
pNewCmdLine[len] = '\0';
}
//-----------------------------------------------------------------------------
// 360 Conditional spew
//-----------------------------------------------------------------------------
void Spew( const char *pFormat, ... )
{
#if defined( _DEBUG )
char msg[2048];
va_list argptr;
va_start( argptr, pFormat );
vsprintf( msg, pFormat, argptr );
va_end( argptr );
OutputDebugString( msg );
#endif
}
//-----------------------------------------------------------------------------
// Get the new entry point and command line
//-----------------------------------------------------------------------------
LauncherMain_t GetLaunchEntryPoint( char *pNewCommandLine )
{
HMODULE hModule;
char *pCmdLine;
// determine source of our invocation, internal or external
// a valid launch payload will have an embedded command line
// command line could be from internal restart in dev or retail mode
CXboxLaunch xboxLaunch;
int payloadSize;
unsigned int launchID;
char *pPayload;
bool bInternalRestart = xboxLaunch.GetLaunchData( &launchID, (void**)&pPayload, &payloadSize );
if ( !bInternalRestart || !payloadSize || launchID != VALVE_LAUNCH_ID )
{
// could be first time, get command line from system
pCmdLine = GetCommandLine();
if ( !stricmp( pCmdLine, "\"default.xex\"" ) )
{
// matches retail xex and no arguments, mut be first time retail launch
pCmdLine = "default.xex";
#if defined( _MEMTEST )
pCmdLine = "default.xex +mat_picmip 2";
#endif
}
}
else
{
// get embedded command line from payload
pCmdLine = pPayload;
}
int launchFlags = 0;
if ( launchID == VALVE_LAUNCH_ID )
{
launchFlags = xboxLaunch.GetLaunchFlags();
}
#if !defined( _CERT )
if ( launchFlags & LF_ISDEBUGGING )
{
while ( !DmIsDebuggerPresent() )
{
}
Sleep( 1000 );
Spew( "Resuming debug session.\n" );
}
#endif
// unforunately, the xbox erases its internal store upon first fetch
// must re-establish it so the payload that contains other data (past command line) can be accessed by the game
// the launch data will be owned by tier0 and supplied to game
if ( launchID == VALVE_LAUNCH_ID )
{
xboxLaunch.SetLaunchData( pPayload, payloadSize, launchFlags );
}
#if defined( _DEMO )
else if ( pPayload && payloadSize )
{
// not our data
// restore the launch data as expected
xboxLaunch.SetLaunchData( pPayload, payloadSize, LF_UNKNOWNDATA );
}
#endif
#if defined( _DEMO )
// the demo version cannot trust launch environment
// Kiosk or Magazines launch in unpredictable ways with unknown paths
// MUST slam the command line!!!
#if !defined( _CERT )
// take the command line as specified by the debugger
if ( !DmIsDebuggerPresent() )
{
pCmdLine = "default.xex";
}
#else
pCmdLine = "default.xex";
#endif
#endif
// The 360 has no paths and therefore the xex must reside in the same location as the dlls.
// Only the xex must reside locally, on the box, but the dlls can be mounted from the remote share.
// Resolve all known implicitly loaded dlls to be explicitly loaded now to allow their remote location.
const char *pImplicitDLLs[] =
{
"tier0_360.dll",
"vstdlib_360.dll",
"vxbdm_360.dll",
"launcher_360.dll",
};
// Corresponds to pImplicitDLLs. A dll load failure is only an error if that dll is tagged as required.
const bool bDllRequired[] =
{
true, // tier0
true, // vstdlib
false, // vxbdm
true, // ???
};
char gameName[32];
if ( !ParseCommandLineArg( pCmdLine, "-game", gameName, sizeof( gameName ) ) )
{
#if defined( VPCGAME_STRING )
strcpy( gameName, VPCGAME_STRING );
#endif
}
else
{
// sanitize a possible absolute game path back to expected game name
char *pSlash = strrchr( gameName, '\\' );
if ( pSlash )
{
memcpy( gameName, pSlash+1, strlen( pSlash+1 )+1 );
}
}
// resolve which application gets launched
// default is to application
pImplicitDLLs[ARRAYSIZE( pImplicitDLLs )-1] = "launcher_360.dll";
// the base path is the where the game is predominantly anchored
// game runs from dvd only
// this can only be the d: by definition on the xbox
const char *pBasePath = "d:";
// load all the dlls specified
char dllPath[MAX_PATH];
for ( int i=0; i<ARRAYSIZE( pImplicitDLLs ); i++ )
{
hModule = NULL;
sprintf( dllPath, "%s\\bin\\%s", pBasePath, pImplicitDLLs[i] );
hModule = LoadLibrary( dllPath );
if ( !hModule && bDllRequired[i] )
{
Spew( "FATAL: Failed to load dll: '%s'\n", dllPath );
return NULL;
}
}
char cleanCommandLine[1024];
char tempCommandLine[1024];
StripCommandLineArg( pCmdLine, tempCommandLine, "-basedir" );
StripCommandLineArg( tempCommandLine, cleanCommandLine, "-game" );
// HACK: For ratings build, unlock everything. Remove this for later testing
const char *pAdditionalArgs = "";
#if defined( RATINGSBUILD )
pAdditionalArgs = "-dev -unlockchapters mp_mark_all_maps_complete";
#endif
// set the alternate command line
sprintf( pNewCommandLine, "%s -basedir %s -game %s\\%s %s", cleanCommandLine, pBasePath, pBasePath, gameName, pAdditionalArgs );
// the 'main' export is guaranteed to be at ordinal 1
// the library is already loaded, this just causes a lookup that will resolve against the shortname
const char *pLaunchDllName = pImplicitDLLs[ARRAYSIZE( pImplicitDLLs )-1];
hModule = LoadLibrary( pLaunchDllName );
LauncherMain_t main = (LauncherMain_t)GetProcAddress( hModule, (LPSTR)1 );
if ( !main )
{
Spew( "FATAL: 'LauncherMain' entry point not found in %s\n", pLaunchDllName );
return NULL;
}
return main;
}
//-----------------------------------------------------------------------------
// 360 Application Entry Point.
//-----------------------------------------------------------------------------
VOID __cdecl main()
{
char newCmdLine[1024];
LauncherMain_t newMain = GetLaunchEntryPoint( newCmdLine );
if ( newMain )
{
// 360 has no concept of instances, spoof one
newMain( (HINSTANCE)1, (HINSTANCE)0, (LPSTR)newCmdLine, 0 );
}
}
#endif