Team Fortress 2 Source Code as on 22/4/2020
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.

720 lines
19 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: A redirection tool that allows the DLLs to reside elsewhere.
  4. //
  5. //=====================================================================================//
  6. #if defined( _WIN32 ) && !defined( _X360 )
  7. #include <windows.h>
  8. #include <stdio.h>
  9. #include <assert.h>
  10. #include <direct.h>
  11. #endif
  12. #if defined( _X360 )
  13. #define _XBOX
  14. #include <xtl.h>
  15. #include <xbdm.h>
  16. #include <stdio.h>
  17. #include <assert.h>
  18. #include "xbox\xbox_core.h"
  19. #include "xbox\xbox_launch.h"
  20. #endif
  21. #ifdef POSIX
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <dlfcn.h>
  25. #include <limits.h>
  26. #include <string.h>
  27. #define MAX_PATH PATH_MAX
  28. #endif
  29. #include "tier0/basetypes.h"
  30. #ifdef WIN32
  31. typedef int (*LauncherMain_t)( HINSTANCE hInstance, HINSTANCE hPrevInstance,
  32. LPSTR lpCmdLine, int nCmdShow );
  33. #elif POSIX
  34. typedef int (*LauncherMain_t)( int argc, char **argv );
  35. #else
  36. #error
  37. #endif
  38. #ifdef WIN32
  39. // hinting the nvidia driver to use the dedicated graphics card in an optimus configuration
  40. // for more info, see: http://developer.download.nvidia.com/devzone/devcenter/gamegraphics/files/OptimusRenderingPolicies.pdf
  41. extern "C" { _declspec( dllexport ) DWORD NvOptimusEnablement = 0x00000001; }
  42. // same thing for AMD GPUs using v13.35 or newer drivers
  43. extern "C" { __declspec( dllexport ) int AmdPowerXpressRequestHighPerformance = 1; }
  44. #endif
  45. //-----------------------------------------------------------------------------
  46. // Purpose: Return the directory where this .exe is running from
  47. // Output : char
  48. //-----------------------------------------------------------------------------
  49. #if !defined( _X360 )
  50. static char *GetBaseDir( const char *pszBuffer )
  51. {
  52. static char basedir[ MAX_PATH ];
  53. char szBuffer[ MAX_PATH ];
  54. size_t j;
  55. char *pBuffer = NULL;
  56. strcpy( szBuffer, pszBuffer );
  57. pBuffer = strrchr( szBuffer,'\\' );
  58. if ( pBuffer )
  59. {
  60. *(pBuffer+1) = '\0';
  61. }
  62. strcpy( basedir, szBuffer );
  63. j = strlen( basedir );
  64. if (j > 0)
  65. {
  66. if ( ( basedir[ j-1 ] == '\\' ) ||
  67. ( basedir[ j-1 ] == '/' ) )
  68. {
  69. basedir[ j-1 ] = 0;
  70. }
  71. }
  72. return basedir;
  73. }
  74. #ifdef WIN32
  75. int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
  76. {
  77. // Must add 'bin' to the path....
  78. char* pPath = getenv("PATH");
  79. // Use the .EXE name to determine the root directory
  80. char moduleName[ MAX_PATH ];
  81. char szBuffer[4096];
  82. if ( !GetModuleFileName( hInstance, moduleName, MAX_PATH ) )
  83. {
  84. MessageBox( 0, "Failed calling GetModuleFileName", "Launcher Error", MB_OK );
  85. return 0;
  86. }
  87. // Get the root directory the .exe is in
  88. char* pRootDir = GetBaseDir( moduleName );
  89. #ifdef _DEBUG
  90. int len =
  91. #endif
  92. _snprintf( szBuffer, sizeof( szBuffer ), "PATH=%s\\bin\\;%s", pRootDir, pPath );
  93. szBuffer[sizeof( szBuffer ) - 1] = '\0';
  94. assert( len < sizeof( szBuffer ) );
  95. _putenv( szBuffer );
  96. // Assemble the full path to our "launcher.dll"
  97. _snprintf( szBuffer, sizeof( szBuffer ), "%s\\bin\\launcher.dll", pRootDir );
  98. szBuffer[sizeof( szBuffer ) - 1] = '\0';
  99. // STEAM OK ... filesystem not mounted yet
  100. #if defined(_X360)
  101. HINSTANCE launcher = LoadLibrary( szBuffer );
  102. #else
  103. HINSTANCE launcher = LoadLibraryEx( szBuffer, NULL, LOAD_WITH_ALTERED_SEARCH_PATH );
  104. #endif
  105. if ( !launcher )
  106. {
  107. char *pszError;
  108. FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&pszError, 0, NULL);
  109. char szBuf[1024];
  110. _snprintf(szBuf, sizeof( szBuf ), "Failed to load the launcher DLL:\n\n%s", pszError);
  111. szBuf[sizeof( szBuf ) - 1] = '\0';
  112. MessageBox( 0, szBuf, "Launcher Error", MB_OK );
  113. LocalFree(pszError);
  114. return 0;
  115. }
  116. LauncherMain_t main = (LauncherMain_t)GetProcAddress( launcher, "LauncherMain" );
  117. return main( hInstance, hPrevInstance, lpCmdLine, nCmdShow );
  118. }
  119. #elif defined (POSIX)
  120. #if defined( LINUX )
  121. #include <fcntl.h>
  122. static bool IsDebuggerPresent( int time )
  123. {
  124. // Need to get around the __wrap_open() stuff. Just find the open symbol
  125. // directly and use it...
  126. typedef int (open_func_t)( const char *pathname, int flags, mode_t mode );
  127. open_func_t *open_func = (open_func_t *)dlsym( RTLD_NEXT, "open" );
  128. if ( open_func )
  129. {
  130. for ( int i = 0; i < time; i++ )
  131. {
  132. int tracerpid = -1;
  133. int fd = (*open_func)( "/proc/self/status", O_RDONLY, S_IRUSR );
  134. if (fd >= 0)
  135. {
  136. char buf[ 4096 ];
  137. static const char tracerpid_str[] = "TracerPid:";
  138. const int len = read( fd, buf, sizeof(buf) - 1 );
  139. if ( len > 0 )
  140. {
  141. buf[ len ] = 0;
  142. const char *str = strstr( buf, tracerpid_str );
  143. tracerpid = str ? atoi( str + sizeof( tracerpid_str ) ) : -1;
  144. }
  145. close( fd );
  146. }
  147. if ( tracerpid > 0 )
  148. return true;
  149. sleep( 1 );
  150. }
  151. }
  152. return false;
  153. }
  154. static void WaitForDebuggerConnect( int argc, char *argv[], int time )
  155. {
  156. for ( int i = 1; i < argc; i++ )
  157. {
  158. if ( strstr( argv[i], "-wait_for_debugger" ) )
  159. {
  160. printf( "\nArg -wait_for_debugger found.\nWaiting %dsec for debugger...\n", time );
  161. printf( " pid = %d\n", getpid() );
  162. if ( IsDebuggerPresent( time ) )
  163. printf("Debugger connected...\n\n");
  164. break;
  165. }
  166. }
  167. }
  168. #else
  169. static void WaitForDebuggerConnect( int argc, char *argv[], int time )
  170. {
  171. }
  172. #endif // !LINUX
  173. int main( int argc, char *argv[] )
  174. {
  175. void *launcher = dlopen( "bin/launcher" DLL_EXT_STRING, RTLD_NOW );
  176. if ( !launcher )
  177. {
  178. fprintf( stderr, "Failed to load the launcher\n" );
  179. return 0;
  180. }
  181. LauncherMain_t main = (LauncherMain_t)dlsym( launcher, "LauncherMain" );
  182. if ( !main )
  183. {
  184. fprintf( stderr, "Failed to load the launcher entry proc\n" );
  185. return 0;
  186. }
  187. #if defined(__clang__) && !defined(OSX)
  188. // When building with clang we absolutely need the allocator to always
  189. // give us 16-byte aligned memory because if any objects are tagged as
  190. // being 16-byte aligned then clang will generate SSE instructions to move
  191. // and initialize them, and an allocator that does not respect the
  192. // contract will lead to crashes. On Linux we normally use the default
  193. // allocator which does not give us this alignment guarantee.
  194. // The google tcmalloc allocator gives us this guarantee.
  195. // Test the current allocator to make sure it gives us the required alignment.
  196. void* pointers[20];
  197. for (int i = 0; i < ARRAYSIZE(pointers); ++i)
  198. {
  199. void* p = malloc( 16 );
  200. pointers[ i ] = p;
  201. if ( ( (size_t)p ) & 0xF )
  202. {
  203. printf( "%p is not 16-byte aligned. Aborting.\n", p );
  204. printf( "Pass /define:CLANG to VPC to correct this.\n" );
  205. return -10;
  206. }
  207. }
  208. for (int i = 0; i < ARRAYSIZE(pointers); ++i)
  209. {
  210. if ( pointers[ i ] )
  211. free( pointers[ i ] );
  212. }
  213. if ( __has_feature(address_sanitizer) )
  214. {
  215. printf( "Address sanitizer is enabled.\n" );
  216. }
  217. else
  218. {
  219. printf( "No address sanitizer!\n" );
  220. }
  221. #endif
  222. WaitForDebuggerConnect( argc, argv, 30 );
  223. return main( argc, argv );
  224. }
  225. #else
  226. #error
  227. #endif // WIN32 || POSIX
  228. #else // X360
  229. //-----------------------------------------------------------------------------
  230. // 360 Quick and dirty command line parsing. Returns true if key found,
  231. // false otherwise. Caller can optionally get next argument.
  232. //-----------------------------------------------------------------------------
  233. bool ParseCommandLineArg( const char *pCmdLine, const char* pKey, char* pValueBuff = NULL, int valueBuffSize = 0 )
  234. {
  235. int keyLen = (int)strlen( pKey );
  236. const char* pArg = pCmdLine;
  237. for ( ;; )
  238. {
  239. // scan for match
  240. pArg = strstr( (char*)pArg, pKey );
  241. if ( !pArg )
  242. {
  243. return false;
  244. }
  245. // found, but could be a substring
  246. if ( pArg[keyLen] == '\0' || pArg[keyLen] == ' ' )
  247. {
  248. // exact match
  249. break;
  250. }
  251. pArg += keyLen;
  252. }
  253. if ( pValueBuff )
  254. {
  255. // caller wants next token
  256. // skip past key and whitespace
  257. pArg += keyLen;
  258. while ( *pArg == ' ' )
  259. {
  260. pArg++;
  261. }
  262. int i;
  263. for ( i=0; i<valueBuffSize; i++ )
  264. {
  265. pValueBuff[i] = *pArg;
  266. if ( *pArg == '\0' || *pArg == ' ' )
  267. break;
  268. pArg++;
  269. }
  270. pValueBuff[i] = '\0';
  271. }
  272. return true;
  273. }
  274. //-----------------------------------------------------------------------------
  275. // 360 Quick and dirty command line arg stripping.
  276. //-----------------------------------------------------------------------------
  277. void StripCommandLineArg( const char *pCmdLine, char *pNewCmdLine, const char *pStripArg )
  278. {
  279. // cannot operate in place
  280. assert( pCmdLine != pNewCmdLine );
  281. int numTotal = strlen( pCmdLine ) + 1;
  282. const char* pArg = strstr( pCmdLine, pStripArg );
  283. if ( !pArg )
  284. {
  285. strcpy( pNewCmdLine, pCmdLine );
  286. return;
  287. }
  288. int numDiscard = strlen( pStripArg );
  289. while ( pArg[numDiscard] && ( pArg[numDiscard] != '-' && pArg[numDiscard] != '+' ) )
  290. {
  291. // eat whitespace up to the next argument
  292. numDiscard++;
  293. }
  294. memcpy( pNewCmdLine, pCmdLine, pArg - pCmdLine );
  295. memcpy( pNewCmdLine + ( pArg - pCmdLine ), (void*)&pArg[numDiscard], numTotal - ( pArg + numDiscard - pCmdLine ) );
  296. // ensure we don't leave any trailing whitespace, occurs if last arg is stripped
  297. int len = strlen( pNewCmdLine );
  298. while ( len > 0 && pNewCmdLine[len-1] == ' ' )
  299. {
  300. len--;
  301. }
  302. pNewCmdLine[len] = '\0';
  303. }
  304. //-----------------------------------------------------------------------------
  305. // 360 Conditional spew
  306. //-----------------------------------------------------------------------------
  307. void Spew( const char *pFormat, ... )
  308. {
  309. #if defined( _DEBUG )
  310. char msg[2048];
  311. va_list argptr;
  312. va_start( argptr, pFormat );
  313. vsprintf( msg, pFormat, argptr );
  314. va_end( argptr );
  315. OutputDebugString( msg );
  316. #endif
  317. }
  318. //-----------------------------------------------------------------------------
  319. // Adheres to possible xbox exclude paths in for -dvddev mode.
  320. //-----------------------------------------------------------------------------
  321. bool IsBinExcluded( const char *pRemotePath, bool *pExcludeAll )
  322. {
  323. *pExcludeAll = false;
  324. bool bIsBinExcluded = false;
  325. // find optional exclusion file
  326. char szExcludeFile[MAX_PATH];
  327. sprintf( szExcludeFile, "%s\\xbox_exclude_paths.txt", pRemotePath );
  328. HANDLE hFile = CreateFile( szExcludeFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
  329. if ( hFile != INVALID_HANDLE_VALUE )
  330. {
  331. int len = GetFileSize( hFile, NULL );
  332. if ( len > 0 )
  333. {
  334. char *pBuffer = ( char *)malloc( len + 1 );
  335. memset( pBuffer, 0, len+1 );
  336. DWORD numBytesRead;
  337. if ( ReadFile( hFile, pBuffer, len, &numBytesRead, NULL ) )
  338. {
  339. strlwr( pBuffer );
  340. if ( strstr( pBuffer, "\"*\"" ) )
  341. {
  342. *pExcludeAll = true;
  343. bIsBinExcluded = true;
  344. }
  345. else if ( strstr( pBuffer, "\"\\bin\"" ) )
  346. {
  347. // override file either specifies an exclusion of the root or the bin directory
  348. bIsBinExcluded = true;
  349. }
  350. }
  351. free( pBuffer );
  352. }
  353. CloseHandle( hFile );
  354. }
  355. return bIsBinExcluded;
  356. }
  357. //-----------------------------------------------------------------------------
  358. // Get the new entry point and command line
  359. //-----------------------------------------------------------------------------
  360. LauncherMain_t GetLaunchEntryPoint( char *pNewCommandLine )
  361. {
  362. HMODULE hModule;
  363. char *pCmdLine;
  364. // determine source of our invocation, internal or external
  365. // a valid launch payload will have an embedded command line
  366. // command line could be from internal restart in dev or retail mode
  367. CXboxLaunch xboxLaunch;
  368. int payloadSize;
  369. unsigned int launchID;
  370. char *pPayload;
  371. bool bInternalRestart = xboxLaunch.GetLaunchData( &launchID, (void**)&pPayload, &payloadSize );
  372. if ( !bInternalRestart || !payloadSize || launchID != VALVE_LAUNCH_ID )
  373. {
  374. // launch is not ours
  375. if ( launchID == LAUNCH_DATA_DEMO_ID )
  376. {
  377. // data is a demo blob, not ready to handle yet, so ignore
  378. }
  379. // could be first time, get command line from system
  380. pCmdLine = GetCommandLine();
  381. if ( !stricmp( pCmdLine, "\"default.xex\"" ) )
  382. {
  383. // matches retail xex and no arguments, mut be first time retail launch
  384. pCmdLine = "default.xex -dvd";
  385. #if defined( _MEMTEST )
  386. pCmdLine = "default.xex -dvd +mat_picmip 2";
  387. #endif
  388. }
  389. }
  390. else
  391. {
  392. // get embedded command line from payload
  393. pCmdLine = pPayload;
  394. }
  395. int launchFlags = xboxLaunch.GetLaunchFlags();
  396. #if !defined( _CERT )
  397. if ( launchFlags & LF_ISDEBUGGING )
  398. {
  399. while ( !DmIsDebuggerPresent() )
  400. {
  401. }
  402. Sleep( 1000 );
  403. Spew( "Resuming debug session.\n" );
  404. }
  405. #endif
  406. if ( launchID == VALVE_LAUNCH_ID )
  407. {
  408. // unforunately, the xbox erases its internal store upon first fetch
  409. // must re-establish it so the payload that contains other data (past command line) can be accessed by the game
  410. // the launch data will be owned by tier0 and supplied to game
  411. xboxLaunch.SetLaunchData( pPayload, payloadSize, launchFlags );
  412. }
  413. // The 360 has no paths and therefore the xex must reside in the same location as the dlls.
  414. // Only the xex must reside locally, on the box, but the dlls can be mounted from the remote share.
  415. // Resolve all known implicitly loaded dlls to be explicitly loaded now to allow their remote location.
  416. const char *pImplicitDLLs[] =
  417. {
  418. "tier0_360.dll",
  419. "vstdlib_360.dll",
  420. "vxbdm_360.dll",
  421. // last slot reserved, as dynamic, used to determine which application gets launched
  422. "???"
  423. };
  424. // Corresponds to pImplicitDLLs. A dll load failure is only an error if that dll is tagged as required.
  425. const bool bDllRequired[] =
  426. {
  427. true, // tier0
  428. true, // vstdlib
  429. false, // vxbdm
  430. true, // ???
  431. };
  432. char gameName[32];
  433. bool bDoChooser = false;
  434. if ( !ParseCommandLineArg( pCmdLine, "-game", gameName, sizeof( gameName ) ) )
  435. {
  436. // usage of remote share requires a game (default to hl2)
  437. // remote share mandates an absolute game path which is detected by the host
  438. strcpy( gameName, "hl2" );
  439. bDoChooser = true;
  440. }
  441. else
  442. {
  443. // sanitize a possible absolute game path back to expected game name
  444. char *pSlash = strrchr( gameName, '\\' );
  445. if ( pSlash )
  446. {
  447. memcpy( gameName, pSlash+1, strlen( pSlash+1 )+1 );
  448. }
  449. }
  450. char shareName[32];
  451. if ( !ParseCommandLineArg( pCmdLine, "-share", shareName, sizeof( shareName ) ) )
  452. {
  453. // usage of remote share requires a share name for the game folder (default to game)
  454. strcpy( shareName, "game" );
  455. }
  456. if ( ( xboxLaunch.GetLaunchFlags() & LF_EXITFROMGAME ) && !( xboxLaunch.GetLaunchFlags() & LF_GAMERESTART ) )
  457. {
  458. // exiting from a game back to chooser
  459. bDoChooser = true;
  460. }
  461. // If we're restarting from an invite, we're funneling into TF2
  462. if ( launchFlags & LF_INVITERESTART )
  463. {
  464. strcpy( gameName, "tf" );
  465. bDoChooser = false;
  466. }
  467. // resolve which application gets launched
  468. if ( bDoChooser )
  469. {
  470. // goto high level 1 of N game selector
  471. pImplicitDLLs[ARRAYSIZE( pImplicitDLLs )-1] = "AppChooser_360.dll";
  472. }
  473. else
  474. {
  475. pImplicitDLLs[ARRAYSIZE( pImplicitDLLs )-1] = "launcher_360.dll";
  476. }
  477. // the base path is the where the game is predominantly anchored
  478. char basePath[128];
  479. // a remote path is for development mode only, on the host pc
  480. char remotePath[128];
  481. #if !defined( _CERT )
  482. if ( !ParseCommandLineArg( pCmdLine, "-dvd" ) )
  483. {
  484. // development mode only, using host pc
  485. // auto host name detection can be overriden via command line
  486. char hostName[32];
  487. if ( !ParseCommandLineArg( pCmdLine, "-host", hostName, sizeof( hostName ) ) )
  488. {
  489. // auto detect, the 360 machine name must be <HostPC>_360
  490. DWORD length = sizeof( hostName );
  491. HRESULT hr = DmGetXboxName( hostName, &length );
  492. if ( hr != XBDM_NOERR )
  493. {
  494. Spew( "FATAL: Could not get xbox name: %s\n", hostName );
  495. return NULL;
  496. }
  497. char *p = strstr( hostName, "_360" );
  498. if ( !p )
  499. {
  500. Spew( "FATAL: Xbox name must be <HostPC>_360\n" );
  501. return NULL;
  502. }
  503. *p = '\0';
  504. }
  505. sprintf( remotePath, "net:\\smb\\%s\\%s", hostName, shareName );
  506. // network remote shares seem to be buggy, but always manifest as the gamedir being unaccessible
  507. // validate now, otherwise longer wait until process eventually fails
  508. char szFullPath[MAX_PATH];
  509. WIN32_FIND_DATA findData;
  510. sprintf( szFullPath, "%s\\%s\\*.*", remotePath, gameName );
  511. HANDLE hFindFile = FindFirstFile( szFullPath, &findData );
  512. if ( hFindFile == INVALID_HANDLE_VALUE )
  513. {
  514. Spew( "*******************************************************************\n" );
  515. Spew( "FATAL: Access to remote share '%s' on host PC lost. Forcing cold reboot.\n", szFullPath );
  516. Spew( "FATAL: After reboot completes to dashboard, restart application.\n" );
  517. Spew( "*******************************************************************\n" );
  518. DmRebootEx( DMBOOT_COLD, NULL, NULL, NULL );
  519. return NULL;
  520. }
  521. FindClose( hFindFile );
  522. }
  523. #endif
  524. char *searchPaths[2];
  525. searchPaths[0] = basePath;
  526. int numSearchPaths = 1;
  527. bool bExcludeAll = false;
  528. bool bAddRemotePath = false;
  529. if ( ParseCommandLineArg( pCmdLine, "-dvd" ) )
  530. {
  531. // game runs from dvd only
  532. strcpy( basePath, "d:" );
  533. }
  534. else if ( ParseCommandLineArg( pCmdLine, "-dvddev" ) )
  535. {
  536. // dvd development, game runs from dvd and can fall through to access remote path
  537. // check user configuration for possible \bin exclusion from Xbox HDD
  538. strcpy( basePath, "d:" );
  539. if ( IsBinExcluded( remotePath, &bExcludeAll ) )
  540. {
  541. searchPaths[0] = remotePath;
  542. numSearchPaths = 1;
  543. }
  544. else
  545. {
  546. searchPaths[0] = basePath;
  547. searchPaths[1] = remotePath;
  548. numSearchPaths = 2;
  549. }
  550. if ( bExcludeAll )
  551. {
  552. // override, user has excluded everything, game runs from remote path only
  553. strcpy( basePath, remotePath );
  554. }
  555. else
  556. {
  557. // -dvddev appends a -remote <remotepath> for the filesystem to detect
  558. bAddRemotePath = true;
  559. }
  560. }
  561. else
  562. {
  563. // game runs from remote path only
  564. strcpy( basePath, remotePath );
  565. }
  566. // load all the dlls specified
  567. char dllPath[MAX_PATH];
  568. for ( int i=0; i<ARRAYSIZE( pImplicitDLLs ); i++ )
  569. {
  570. hModule = NULL;
  571. for ( int j = 0; j < numSearchPaths; j++ )
  572. {
  573. sprintf( dllPath, "%s\\bin\\%s", searchPaths[j], pImplicitDLLs[i] );
  574. hModule = LoadLibrary( dllPath );
  575. if ( hModule )
  576. {
  577. break;
  578. }
  579. }
  580. if ( !hModule && bDllRequired[i] )
  581. {
  582. Spew( "FATAL: Failed to load dll: '%s'\n", dllPath );
  583. return NULL;
  584. }
  585. }
  586. char cleanCommandLine[512];
  587. char tempCommandLine[512];
  588. StripCommandLineArg( pCmdLine, tempCommandLine, "-basedir" );
  589. StripCommandLineArg( tempCommandLine, cleanCommandLine, "-game" );
  590. if ( bAddRemotePath )
  591. {
  592. // the remote path is only added for -dvddev mode
  593. StripCommandLineArg( cleanCommandLine, tempCommandLine, "-remote" );
  594. sprintf( cleanCommandLine, "%s -remote %s", tempCommandLine, remotePath );
  595. }
  596. // set the alternate command line
  597. sprintf( pNewCommandLine, "%s -basedir %s -game %s\\%s", cleanCommandLine, basePath, basePath, gameName );
  598. // the 'main' export is guaranteed to be at ordinal 1
  599. // the library is already loaded, this just causes a lookup that will resolve against the shortname
  600. const char *pLaunchDllName = pImplicitDLLs[ARRAYSIZE( pImplicitDLLs )-1];
  601. hModule = LoadLibrary( pLaunchDllName );
  602. LauncherMain_t main = (LauncherMain_t)GetProcAddress( hModule, (LPSTR)1 );
  603. if ( !main )
  604. {
  605. Spew( "FATAL: 'LauncherMain' entry point not found in %s\n", pLaunchDllName );
  606. return NULL;
  607. }
  608. return main;
  609. }
  610. //-----------------------------------------------------------------------------
  611. // 360 Application Entry Point.
  612. //-----------------------------------------------------------------------------
  613. VOID __cdecl main()
  614. {
  615. char newCmdLine[512];
  616. LauncherMain_t newMain = GetLaunchEntryPoint( newCmdLine );
  617. if ( newMain )
  618. {
  619. // 360 has no concept of instances, spoof one
  620. newMain( (HINSTANCE)1, (HINSTANCE)0, (LPSTR)newCmdLine, 0 );
  621. }
  622. }
  623. #endif