summaryrefslogtreecommitdiff
path: root/launcher_main/main.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /launcher_main/main.cpp
downloadarchived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz
archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip
Diffstat (limited to 'launcher_main/main.cpp')
-rw-r--r--launcher_main/main.cpp720
1 files changed, 720 insertions, 0 deletions
diff --git a/launcher_main/main.cpp b/launcher_main/main.cpp
new file mode 100644
index 0000000..368e284
--- /dev/null
+++ b/launcher_main/main.cpp
@@ -0,0 +1,720 @@
+//========= Copyright 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>
+#include <stdio.h>
+#include <assert.h>
+#include "xbox\xbox_core.h"
+#include "xbox\xbox_launch.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 "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
+
+
+//-----------------------------------------------------------------------------
+// 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 );
+
+ 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;
+}
+
+#ifdef WIN32
+
+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 );
+
+#ifdef _DEBUG
+ int len =
+#endif
+ _snprintf( szBuffer, sizeof( szBuffer ), "PATH=%s\\bin\\;%s", pRootDir, 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\\launcher.dll", pRootDir );
+ 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 (POSIX)
+
+#if defined( LINUX )
+
+#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
+
+int main( int argc, char *argv[] )
+{
+ void *launcher = dlopen( "bin/launcher" DLL_EXT_STRING, RTLD_NOW );
+ if ( !launcher )
+ {
+ fprintf( stderr, "Failed to load the launcher\n" );
+ return 0;
+ }
+
+ LauncherMain_t main = (LauncherMain_t)dlsym( launcher, "LauncherMain" );
+ if ( !main )
+ {
+ fprintf( stderr, "Failed to load the launcher entry proc\n" );
+ return 0;
+ }
+
+#if defined(__clang__) && !defined(OSX)
+ // When building with clang we absolutely need the allocator to always
+ // give us 16-byte aligned memory because if any objects are tagged as
+ // being 16-byte aligned then clang will generate SSE instructions to move
+ // and initialize them, and an allocator that does not respect the
+ // contract will lead to crashes. On Linux we normally use the default
+ // allocator which does not give us this alignment guarantee.
+ // The google tcmalloc allocator gives us this guarantee.
+ // Test the current allocator to make sure it gives us the required alignment.
+ void* pointers[20];
+ for (int i = 0; i < ARRAYSIZE(pointers); ++i)
+ {
+ void* p = malloc( 16 );
+ pointers[ i ] = p;
+ if ( ( (size_t)p ) & 0xF )
+ {
+ printf( "%p is not 16-byte aligned. Aborting.\n", p );
+ printf( "Pass /define:CLANG to VPC to correct this.\n" );
+ return -10;
+ }
+ }
+ for (int i = 0; i < ARRAYSIZE(pointers); ++i)
+ {
+ if ( pointers[ i ] )
+ free( pointers[ i ] );
+ }
+
+ if ( __has_feature(address_sanitizer) )
+ {
+ printf( "Address sanitizer is enabled.\n" );
+ }
+ else
+ {
+ printf( "No address sanitizer!\n" );
+ }
+#endif
+
+ WaitForDebuggerConnect( argc, argv, 30 );
+
+ 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
+}
+
+//-----------------------------------------------------------------------------
+// Adheres to possible xbox exclude paths in for -dvddev mode.
+//-----------------------------------------------------------------------------
+bool IsBinExcluded( const char *pRemotePath, bool *pExcludeAll )
+{
+ *pExcludeAll = false;
+ bool bIsBinExcluded = false;
+
+ // find optional exclusion file
+ char szExcludeFile[MAX_PATH];
+ sprintf( szExcludeFile, "%s\\xbox_exclude_paths.txt", pRemotePath );
+ HANDLE hFile = CreateFile( szExcludeFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
+ if ( hFile != INVALID_HANDLE_VALUE )
+ {
+ int len = GetFileSize( hFile, NULL );
+ if ( len > 0 )
+ {
+ char *pBuffer = ( char *)malloc( len + 1 );
+ memset( pBuffer, 0, len+1 );
+ DWORD numBytesRead;
+ if ( ReadFile( hFile, pBuffer, len, &numBytesRead, NULL ) )
+ {
+ strlwr( pBuffer );
+ if ( strstr( pBuffer, "\"*\"" ) )
+ {
+ *pExcludeAll = true;
+ bIsBinExcluded = true;
+ }
+ else if ( strstr( pBuffer, "\"\\bin\"" ) )
+ {
+ // override file either specifies an exclusion of the root or the bin directory
+ bIsBinExcluded = true;
+ }
+ }
+ free( pBuffer );
+ }
+ CloseHandle( hFile );
+ }
+
+ return bIsBinExcluded;
+}
+
+//-----------------------------------------------------------------------------
+// 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 )
+ {
+ // launch is not ours
+ if ( launchID == LAUNCH_DATA_DEMO_ID )
+ {
+ // data is a demo blob, not ready to handle yet, so ignore
+ }
+
+ // 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 -dvd";
+#if defined( _MEMTEST )
+ pCmdLine = "default.xex -dvd +mat_picmip 2";
+#endif
+ }
+ }
+ else
+ {
+ // get embedded command line from payload
+ pCmdLine = pPayload;
+ }
+
+ int launchFlags = xboxLaunch.GetLaunchFlags();
+#if !defined( _CERT )
+ if ( launchFlags & LF_ISDEBUGGING )
+ {
+ while ( !DmIsDebuggerPresent() )
+ {
+ }
+
+ Sleep( 1000 );
+ Spew( "Resuming debug session.\n" );
+ }
+#endif
+
+ if ( launchID == VALVE_LAUNCH_ID )
+ {
+ // 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
+ xboxLaunch.SetLaunchData( pPayload, payloadSize, launchFlags );
+ }
+
+ // 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",
+
+ // last slot reserved, as dynamic, used to determine which application gets launched
+ "???"
+ };
+
+ // 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];
+ bool bDoChooser = false;
+ if ( !ParseCommandLineArg( pCmdLine, "-game", gameName, sizeof( gameName ) ) )
+ {
+ // usage of remote share requires a game (default to hl2)
+ // remote share mandates an absolute game path which is detected by the host
+ strcpy( gameName, "hl2" );
+ bDoChooser = true;
+ }
+ 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 );
+ }
+ }
+
+ char shareName[32];
+ if ( !ParseCommandLineArg( pCmdLine, "-share", shareName, sizeof( shareName ) ) )
+ {
+ // usage of remote share requires a share name for the game folder (default to game)
+ strcpy( shareName, "game" );
+ }
+
+ if ( ( xboxLaunch.GetLaunchFlags() & LF_EXITFROMGAME ) && !( xboxLaunch.GetLaunchFlags() & LF_GAMERESTART ) )
+ {
+ // exiting from a game back to chooser
+ bDoChooser = true;
+ }
+
+ // If we're restarting from an invite, we're funneling into TF2
+ if ( launchFlags & LF_INVITERESTART )
+ {
+ strcpy( gameName, "tf" );
+ bDoChooser = false;
+ }
+
+ // resolve which application gets launched
+ if ( bDoChooser )
+ {
+ // goto high level 1 of N game selector
+ pImplicitDLLs[ARRAYSIZE( pImplicitDLLs )-1] = "AppChooser_360.dll";
+ }
+ else
+ {
+ pImplicitDLLs[ARRAYSIZE( pImplicitDLLs )-1] = "launcher_360.dll";
+ }
+
+ // the base path is the where the game is predominantly anchored
+ char basePath[128];
+ // a remote path is for development mode only, on the host pc
+ char remotePath[128];
+
+#if !defined( _CERT )
+ if ( !ParseCommandLineArg( pCmdLine, "-dvd" ) )
+ {
+ // development mode only, using host pc
+ // auto host name detection can be overriden via command line
+ char hostName[32];
+ if ( !ParseCommandLineArg( pCmdLine, "-host", hostName, sizeof( hostName ) ) )
+ {
+ // auto detect, the 360 machine name must be <HostPC>_360
+ DWORD length = sizeof( hostName );
+ HRESULT hr = DmGetXboxName( hostName, &length );
+ if ( hr != XBDM_NOERR )
+ {
+ Spew( "FATAL: Could not get xbox name: %s\n", hostName );
+ return NULL;
+ }
+ char *p = strstr( hostName, "_360" );
+ if ( !p )
+ {
+ Spew( "FATAL: Xbox name must be <HostPC>_360\n" );
+ return NULL;
+ }
+ *p = '\0';
+ }
+
+ sprintf( remotePath, "net:\\smb\\%s\\%s", hostName, shareName );
+
+ // network remote shares seem to be buggy, but always manifest as the gamedir being unaccessible
+ // validate now, otherwise longer wait until process eventually fails
+ char szFullPath[MAX_PATH];
+ WIN32_FIND_DATA findData;
+ sprintf( szFullPath, "%s\\%s\\*.*", remotePath, gameName );
+ HANDLE hFindFile = FindFirstFile( szFullPath, &findData );
+ if ( hFindFile == INVALID_HANDLE_VALUE )
+ {
+ Spew( "*******************************************************************\n" );
+ Spew( "FATAL: Access to remote share '%s' on host PC lost. Forcing cold reboot.\n", szFullPath );
+ Spew( "FATAL: After reboot completes to dashboard, restart application.\n" );
+ Spew( "*******************************************************************\n" );
+ DmRebootEx( DMBOOT_COLD, NULL, NULL, NULL );
+ return NULL;
+ }
+ FindClose( hFindFile );
+ }
+#endif
+
+ char *searchPaths[2];
+ searchPaths[0] = basePath;
+ int numSearchPaths = 1;
+
+ bool bExcludeAll = false;
+ bool bAddRemotePath = false;
+
+ if ( ParseCommandLineArg( pCmdLine, "-dvd" ) )
+ {
+ // game runs from dvd only
+ strcpy( basePath, "d:" );
+ }
+ else if ( ParseCommandLineArg( pCmdLine, "-dvddev" ) )
+ {
+ // dvd development, game runs from dvd and can fall through to access remote path
+ // check user configuration for possible \bin exclusion from Xbox HDD
+ strcpy( basePath, "d:" );
+
+ if ( IsBinExcluded( remotePath, &bExcludeAll ) )
+ {
+ searchPaths[0] = remotePath;
+ numSearchPaths = 1;
+ }
+ else
+ {
+ searchPaths[0] = basePath;
+ searchPaths[1] = remotePath;
+ numSearchPaths = 2;
+ }
+
+ if ( bExcludeAll )
+ {
+ // override, user has excluded everything, game runs from remote path only
+ strcpy( basePath, remotePath );
+ }
+ else
+ {
+ // -dvddev appends a -remote <remotepath> for the filesystem to detect
+ bAddRemotePath = true;
+ }
+ }
+ else
+ {
+ // game runs from remote path only
+ strcpy( basePath, remotePath );
+ }
+
+ // load all the dlls specified
+ char dllPath[MAX_PATH];
+ for ( int i=0; i<ARRAYSIZE( pImplicitDLLs ); i++ )
+ {
+ hModule = NULL;
+ for ( int j = 0; j < numSearchPaths; j++ )
+ {
+ sprintf( dllPath, "%s\\bin\\%s", searchPaths[j], pImplicitDLLs[i] );
+ hModule = LoadLibrary( dllPath );
+ if ( hModule )
+ {
+ break;
+ }
+ }
+ if ( !hModule && bDllRequired[i] )
+ {
+ Spew( "FATAL: Failed to load dll: '%s'\n", dllPath );
+ return NULL;
+ }
+ }
+
+ char cleanCommandLine[512];
+ char tempCommandLine[512];
+ StripCommandLineArg( pCmdLine, tempCommandLine, "-basedir" );
+ StripCommandLineArg( tempCommandLine, cleanCommandLine, "-game" );
+
+ if ( bAddRemotePath )
+ {
+ // the remote path is only added for -dvddev mode
+ StripCommandLineArg( cleanCommandLine, tempCommandLine, "-remote" );
+ sprintf( cleanCommandLine, "%s -remote %s", tempCommandLine, remotePath );
+ }
+
+ // set the alternate command line
+ sprintf( pNewCommandLine, "%s -basedir %s -game %s\\%s", cleanCommandLine, basePath, basePath, gameName );
+
+ // 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[512];
+ LauncherMain_t newMain = GetLaunchEntryPoint( newCmdLine );
+ if ( newMain )
+ {
+ // 360 has no concept of instances, spoof one
+ newMain( (HINSTANCE)1, (HINSTANCE)0, (LPSTR)newCmdLine, 0 );
+ }
+}
+#endif