You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
985 lines
28 KiB
985 lines
28 KiB
//========= Copyright � 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
|