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.

1566 lines
41 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // Defines the entry point for the application.
  6. //
  7. //===========================================================================//
  8. #if defined( _WIN32 ) && !defined( _X360 )
  9. #include <windows.h>
  10. #include "shlwapi.h" // registry stuff
  11. #include <direct.h>
  12. #elif defined ( LINUX ) || defined( OSX )
  13. #define O_EXLOCK 0
  14. #include <sys/types.h>
  15. #include <sys/stat.h>
  16. #include <fcntl.h>
  17. #include <locale.h>
  18. #elif defined ( _X360 )
  19. #else
  20. #error
  21. #endif
  22. #include "appframework/ilaunchermgr.h"
  23. #include <stdio.h>
  24. #include "tier0/icommandline.h"
  25. #include "engine_launcher_api.h"
  26. #include "tier0/vcrmode.h"
  27. #include "ifilesystem.h"
  28. #include "tier1/interface.h"
  29. #include "tier0/dbg.h"
  30. #include "iregistry.h"
  31. #include "appframework/IAppSystem.h"
  32. #include "appframework/AppFramework.h"
  33. #include <vgui/VGUI.h>
  34. #include <vgui/ISurface.h>
  35. #include "tier0/platform.h"
  36. #include "tier0/memalloc.h"
  37. #include "filesystem.h"
  38. #include "tier1/utlrbtree.h"
  39. #include "materialsystem/imaterialsystem.h"
  40. #include "istudiorender.h"
  41. #include "vgui/IVGui.h"
  42. #include "IHammer.h"
  43. #include "datacache/idatacache.h"
  44. #include "datacache/imdlcache.h"
  45. #include "vphysics_interface.h"
  46. #include "filesystem_init.h"
  47. #include "vstdlib/iprocessutils.h"
  48. #include "video/ivideoservices.h"
  49. #include "tier1/tier1.h"
  50. #include "tier2/tier2.h"
  51. #include "tier3/tier3.h"
  52. #include "p4lib/ip4.h"
  53. #include "inputsystem/iinputsystem.h"
  54. #include "filesystem/IQueuedLoader.h"
  55. #include "reslistgenerator.h"
  56. #include "tier1/fmtstr.h"
  57. #include "sourcevr/isourcevirtualreality.h"
  58. #define VERSION_SAFE_STEAM_API_INTERFACES
  59. #include "steam/steam_api.h"
  60. #if defined( _X360 )
  61. #include "xbox/xbox_win32stubs.h"
  62. #include "xbox/xbox_console.h"
  63. #include "xbox/xbox_launch.h"
  64. #endif
  65. #if defined( USE_SDL )
  66. #include "SDL.h"
  67. #if !defined( _WIN32 )
  68. #define MB_OK 0x00000001
  69. #define MB_SYSTEMMODAL 0x00000002
  70. #define MB_ICONERROR 0x00000004
  71. int MessageBox( HWND hWnd, const char *message, const char *header, unsigned uType );
  72. #endif // _WIN32
  73. #endif // USE_SDL
  74. #if defined( POSIX )
  75. #define RELAUNCH_FILE "/tmp/hl2_relaunch"
  76. #endif
  77. // memdbgon must be the last include file in a .cpp file!!!
  78. #include "tier0/memdbgon.h"
  79. #define DEFAULT_HL2_GAMEDIR "hl2"
  80. #if defined( USE_SDL )
  81. extern void* CreateSDLMgr();
  82. #endif
  83. //-----------------------------------------------------------------------------
  84. // Modules...
  85. //-----------------------------------------------------------------------------
  86. static IEngineAPI *g_pEngineAPI;
  87. static IHammer *g_pHammer;
  88. bool g_bTextMode = false;
  89. static char g_szBasedir[MAX_PATH];
  90. static char g_szGamedir[MAX_PATH];
  91. // copied from sys.h
  92. struct FileAssociationInfo
  93. {
  94. char const *extension;
  95. char const *command_to_issue;
  96. };
  97. static FileAssociationInfo g_FileAssociations[] =
  98. {
  99. { ".dem", "playdemo" },
  100. { ".sav", "load" },
  101. { ".bsp", "map" },
  102. };
  103. #ifdef _WIN32
  104. #pragma warning(disable:4073)
  105. #pragma init_seg(lib)
  106. #endif
  107. class CLeakDump
  108. {
  109. public:
  110. CLeakDump()
  111. : m_bCheckLeaks( false )
  112. {
  113. }
  114. ~CLeakDump()
  115. {
  116. if ( m_bCheckLeaks )
  117. {
  118. MemAlloc_DumpStats();
  119. }
  120. }
  121. bool m_bCheckLeaks;
  122. } g_LeakDump;
  123. //-----------------------------------------------------------------------------
  124. // Spew function!
  125. //-----------------------------------------------------------------------------
  126. SpewRetval_t LauncherDefaultSpewFunc( SpewType_t spewType, char const *pMsg )
  127. {
  128. #ifndef _CERT
  129. #ifdef WIN32
  130. OutputDebugStringA( pMsg );
  131. #else
  132. fprintf( stderr, "%s", pMsg );
  133. #endif
  134. switch( spewType )
  135. {
  136. case SPEW_MESSAGE:
  137. case SPEW_LOG:
  138. return SPEW_CONTINUE;
  139. case SPEW_WARNING:
  140. if ( !stricmp( GetSpewOutputGroup(), "init" ) )
  141. {
  142. #if defined( WIN32 ) || defined( USE_SDL )
  143. ::MessageBox( NULL, pMsg, "Warning!", MB_OK | MB_SYSTEMMODAL | MB_ICONERROR );
  144. #endif
  145. }
  146. return SPEW_CONTINUE;
  147. case SPEW_ASSERT:
  148. if ( !ShouldUseNewAssertDialog() )
  149. {
  150. #if defined( WIN32 ) || defined( USE_SDL )
  151. ::MessageBox( NULL, pMsg, "Assert!", MB_OK | MB_SYSTEMMODAL | MB_ICONERROR );
  152. #endif
  153. }
  154. return SPEW_DEBUGGER;
  155. case SPEW_ERROR:
  156. default:
  157. #if defined( WIN32 ) || defined( USE_SDL )
  158. ::MessageBox( NULL, pMsg, "Error!", MB_OK | MB_SYSTEMMODAL | MB_ICONERROR );
  159. #endif
  160. _exit( 1 );
  161. }
  162. #else
  163. if ( spewType != SPEW_ERROR)
  164. return SPEW_CONTINUE;
  165. _exit( 1 );
  166. #endif
  167. }
  168. //-----------------------------------------------------------------------------
  169. // Implementation of VCRHelpers.
  170. //-----------------------------------------------------------------------------
  171. class CVCRHelpers : public IVCRHelpers
  172. {
  173. public:
  174. virtual void ErrorMessage( const char *pMsg )
  175. {
  176. #if defined( WIN32 ) || defined( LINUX )
  177. NOVCR( ::MessageBox( NULL, pMsg, "VCR Error", MB_OK ) );
  178. #endif
  179. }
  180. virtual void* GetMainWindow()
  181. {
  182. return NULL;
  183. }
  184. };
  185. static CVCRHelpers g_VCRHelpers;
  186. //-----------------------------------------------------------------------------
  187. // Purpose: Return the game directory
  188. // Output : char
  189. //-----------------------------------------------------------------------------
  190. char *GetGameDirectory( void )
  191. {
  192. return g_szGamedir;
  193. }
  194. void SetGameDirectory( const char *game )
  195. {
  196. Q_strncpy( g_szGamedir, game, sizeof(g_szGamedir) );
  197. }
  198. //-----------------------------------------------------------------------------
  199. // Gets the executable name
  200. //-----------------------------------------------------------------------------
  201. bool GetExecutableName( char *out, int outSize )
  202. {
  203. #ifdef WIN32
  204. if ( !::GetModuleFileName( ( HINSTANCE )GetModuleHandle( NULL ), out, outSize ) )
  205. {
  206. return false;
  207. }
  208. return true;
  209. #else
  210. return false;
  211. #endif
  212. }
  213. //-----------------------------------------------------------------------------
  214. // Purpose: Return the base directory
  215. // Output : char
  216. //-----------------------------------------------------------------------------
  217. char *GetBaseDirectory( void )
  218. {
  219. return g_szBasedir;
  220. }
  221. //-----------------------------------------------------------------------------
  222. // Purpose: Determine the directory where this .exe is running from
  223. //-----------------------------------------------------------------------------
  224. void UTIL_ComputeBaseDir()
  225. {
  226. g_szBasedir[0] = 0;
  227. if ( IsX360() )
  228. {
  229. char const *pBaseDir = CommandLine()->ParmValue( "-basedir" );
  230. if ( pBaseDir )
  231. {
  232. strcpy( g_szBasedir, pBaseDir );
  233. }
  234. }
  235. if ( !g_szBasedir[0] && GetExecutableName( g_szBasedir, sizeof( g_szBasedir ) ) )
  236. {
  237. char *pBuffer = strrchr( g_szBasedir, '\\' );
  238. if ( *pBuffer )
  239. {
  240. *(pBuffer+1) = '\0';
  241. }
  242. int j = strlen( g_szBasedir );
  243. if (j > 0)
  244. {
  245. if ( ( g_szBasedir[j-1] == '\\' ) ||
  246. ( g_szBasedir[j-1] == '/' ) )
  247. {
  248. g_szBasedir[j-1] = 0;
  249. }
  250. }
  251. }
  252. if ( IsPC() )
  253. {
  254. char const *pOverrideDir = CommandLine()->CheckParm( "-basedir" );
  255. if ( pOverrideDir )
  256. {
  257. strcpy( g_szBasedir, pOverrideDir );
  258. }
  259. }
  260. #ifdef WIN32
  261. Q_strlower( g_szBasedir );
  262. #endif
  263. Q_FixSlashes( g_szBasedir );
  264. }
  265. #ifdef WIN32
  266. BOOL WINAPI MyHandlerRoutine( DWORD dwCtrlType )
  267. {
  268. #if !defined( _X360 )
  269. TerminateProcess( GetCurrentProcess(), 2 );
  270. #endif
  271. return TRUE;
  272. }
  273. #endif
  274. void InitTextMode()
  275. {
  276. #ifdef WIN32
  277. #if !defined( _X360 )
  278. AllocConsole();
  279. SetConsoleCtrlHandler( MyHandlerRoutine, TRUE );
  280. freopen( "CONIN$", "rb", stdin ); // reopen stdin handle as console window input
  281. freopen( "CONOUT$", "wb", stdout ); // reopen stout handle as console window output
  282. freopen( "CONOUT$", "wb", stderr ); // reopen stderr handle as console window output
  283. #else
  284. XBX_Error( "%s %s: Not Supported", __FILE__, __LINE__ );
  285. #endif
  286. #endif
  287. }
  288. void SortResList( char const *pchFileName, char const *pchSearchPath );
  289. #define ALL_RESLIST_FILE "all.lst"
  290. #define ENGINE_RESLIST_FILE "engine.lst"
  291. // create file to dump out to
  292. class CLogAllFiles
  293. {
  294. public:
  295. CLogAllFiles();
  296. void Init();
  297. void Shutdown();
  298. void LogFile( const char *fullPathFileName, const char *options );
  299. private:
  300. static void LogAllFilesFunc( const char *fullPathFileName, const char *options );
  301. void LogToAllReslist( char const *line );
  302. bool m_bActive;
  303. char m_szCurrentDir[_MAX_PATH];
  304. // persistent across restarts
  305. CUtlRBTree< CUtlString, int > m_Logged;
  306. CUtlString m_sResListDir;
  307. CUtlString m_sFullGamePath;
  308. };
  309. static CLogAllFiles g_LogFiles;
  310. static bool AllLogLessFunc( CUtlString const &pLHS, CUtlString const &pRHS )
  311. {
  312. return CaselessStringLessThan( pLHS.Get(), pRHS.Get() );
  313. }
  314. CLogAllFiles::CLogAllFiles() :
  315. m_bActive( false ),
  316. m_Logged( 0, 0, AllLogLessFunc )
  317. {
  318. MEM_ALLOC_CREDIT();
  319. m_sResListDir = "reslists";
  320. }
  321. void CLogAllFiles::Init()
  322. {
  323. if ( IsX360() )
  324. {
  325. return;
  326. }
  327. // Can't do this in edit mode
  328. if ( CommandLine()->CheckParm( "-edit" ) )
  329. {
  330. return;
  331. }
  332. if ( !CommandLine()->CheckParm( "-makereslists" ) )
  333. {
  334. return;
  335. }
  336. m_bActive = true;
  337. char const *pszDir = NULL;
  338. if ( CommandLine()->CheckParm( "-reslistdir", &pszDir ) && pszDir )
  339. {
  340. char szDir[ MAX_PATH ];
  341. Q_strncpy( szDir, pszDir, sizeof( szDir ) );
  342. Q_StripTrailingSlash( szDir );
  343. #ifdef WIN32
  344. Q_strlower( szDir );
  345. #endif
  346. Q_FixSlashes( szDir );
  347. if ( Q_strlen( szDir ) > 0 )
  348. {
  349. m_sResListDir = szDir;
  350. }
  351. }
  352. // game directory has not been established yet, must derive ourselves
  353. char path[MAX_PATH];
  354. Q_snprintf( path, sizeof(path), "%s/%s", GetBaseDirectory(), CommandLine()->ParmValue( "-game", "hl2" ) );
  355. Q_FixSlashes( path );
  356. #ifdef WIN32
  357. Q_strlower( path );
  358. #endif
  359. m_sFullGamePath = path;
  360. // create file to dump out to
  361. char szDir[ MAX_PATH ];
  362. V_snprintf( szDir, sizeof( szDir ), "%s\\%s", m_sFullGamePath.String(), m_sResListDir.String() );
  363. g_pFullFileSystem->CreateDirHierarchy( szDir, "GAME" );
  364. g_pFullFileSystem->AddLoggingFunc( &LogAllFilesFunc );
  365. if ( !CommandLine()->FindParm( "-startmap" ) && !CommandLine()->FindParm( "-startstage" ) )
  366. {
  367. m_Logged.RemoveAll();
  368. g_pFullFileSystem->RemoveFile( CFmtStr( "%s\\%s\\%s", m_sFullGamePath.String(), m_sResListDir.String(), ALL_RESLIST_FILE ), "GAME" );
  369. }
  370. #ifdef WIN32
  371. ::GetCurrentDirectory( sizeof(m_szCurrentDir), m_szCurrentDir );
  372. Q_strncat( m_szCurrentDir, "\\", sizeof(m_szCurrentDir), 1 );
  373. _strlwr( m_szCurrentDir );
  374. #else
  375. getcwd( m_szCurrentDir, sizeof(m_szCurrentDir) );
  376. Q_strncat( m_szCurrentDir, "/", sizeof(m_szCurrentDir), 1 );
  377. #endif
  378. }
  379. void CLogAllFiles::Shutdown()
  380. {
  381. if ( !m_bActive )
  382. return;
  383. m_bActive = false;
  384. if ( CommandLine()->CheckParm( "-makereslists" ) )
  385. {
  386. g_pFullFileSystem->RemoveLoggingFunc( &LogAllFilesFunc );
  387. }
  388. // Now load and sort all.lst
  389. SortResList( CFmtStr( "%s\\%s\\%s", m_sFullGamePath.String(), m_sResListDir.String(), ALL_RESLIST_FILE ), "GAME" );
  390. // Now load and sort engine.lst
  391. SortResList( CFmtStr( "%s\\%s\\%s", m_sFullGamePath.String(), m_sResListDir.String(), ENGINE_RESLIST_FILE ), "GAME" );
  392. m_Logged.Purge();
  393. }
  394. void CLogAllFiles::LogToAllReslist( char const *line )
  395. {
  396. // Open for append, write data, close.
  397. FileHandle_t fh = g_pFullFileSystem->Open( CFmtStr( "%s\\%s\\%s", m_sFullGamePath.String(), m_sResListDir.String(), ALL_RESLIST_FILE ), "at", "GAME" );
  398. if ( fh != FILESYSTEM_INVALID_HANDLE )
  399. {
  400. g_pFullFileSystem->Write("\"", 1, fh);
  401. g_pFullFileSystem->Write( line, Q_strlen(line), fh );
  402. g_pFullFileSystem->Write("\"\n", 2, fh);
  403. g_pFullFileSystem->Close( fh );
  404. }
  405. }
  406. void CLogAllFiles::LogFile(const char *fullPathFileName, const char *options)
  407. {
  408. if ( !m_bActive )
  409. {
  410. Assert( 0 );
  411. return;
  412. }
  413. // write out to log file
  414. Assert( fullPathFileName[1] == ':' );
  415. int idx = m_Logged.Find( fullPathFileName );
  416. if ( idx != m_Logged.InvalidIndex() )
  417. {
  418. return;
  419. }
  420. m_Logged.Insert( fullPathFileName );
  421. // make it relative to our root directory
  422. const char *relative = Q_stristr( fullPathFileName, GetBaseDirectory() );
  423. if ( relative )
  424. {
  425. relative += ( Q_strlen( GetBaseDirectory() ) + 1 );
  426. char rel[ MAX_PATH ];
  427. Q_strncpy( rel, relative, sizeof( rel ) );
  428. #ifdef WIN32
  429. Q_strlower( rel );
  430. #endif
  431. Q_FixSlashes( rel );
  432. LogToAllReslist( rel );
  433. }
  434. }
  435. //-----------------------------------------------------------------------------
  436. // Purpose: callback function from filesystem
  437. //-----------------------------------------------------------------------------
  438. void CLogAllFiles::LogAllFilesFunc(const char *fullPathFileName, const char *options)
  439. {
  440. g_LogFiles.LogFile( fullPathFileName, options );
  441. }
  442. //-----------------------------------------------------------------------------
  443. // Purpose: This is a bit of a hack because it appears
  444. // Output : Returns true on success, false on failure.
  445. //-----------------------------------------------------------------------------
  446. static bool IsWin98OrOlder()
  447. {
  448. bool retval = false;
  449. #if defined( WIN32 ) && !defined( _X360 )
  450. OSVERSIONINFOEX osvi;
  451. ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
  452. osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
  453. BOOL bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi);
  454. if( !bOsVersionInfoEx )
  455. {
  456. // If OSVERSIONINFOEX doesn't work, try OSVERSIONINFO.
  457. osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
  458. if ( !GetVersionEx ( (OSVERSIONINFO *) &osvi) )
  459. {
  460. Error( "IsWin98OrOlder: Unable to get OS version information" );
  461. }
  462. }
  463. switch (osvi.dwPlatformId)
  464. {
  465. case VER_PLATFORM_WIN32_NT:
  466. // NT, XP, Win2K, etc. all OK for SSE
  467. break;
  468. case VER_PLATFORM_WIN32_WINDOWS:
  469. // Win95, 98, Me can't do SSE
  470. retval = true;
  471. break;
  472. case VER_PLATFORM_WIN32s:
  473. // Can't really run this way I don't think...
  474. retval = true;
  475. break;
  476. default:
  477. break;
  478. }
  479. #endif
  480. return retval;
  481. }
  482. //-----------------------------------------------------------------------------
  483. // Purpose: Figure out if Steam is running, then load the GameOverlayRenderer.dll
  484. //-----------------------------------------------------------------------------
  485. void TryToLoadSteamOverlayDLL()
  486. {
  487. #if defined( WIN32 ) && !defined( _X360 )
  488. // First, check if the module is already loaded, perhaps because we were run from Steam directly
  489. HMODULE hMod = GetModuleHandle( "GameOverlayRenderer" DLL_EXT_STRING );
  490. if ( hMod )
  491. {
  492. return;
  493. }
  494. if ( 0 == GetEnvironmentVariableA( "SteamGameId", NULL, 0 ) )
  495. {
  496. // Initializing the Steam client API has the side effect of setting up the AppId
  497. // which is immediately queried in GameOverlayRenderer.dll's DllMain entry point
  498. if( SteamAPI_InitSafe() )
  499. {
  500. const char *pchSteamInstallPath = SteamAPI_GetSteamInstallPath();
  501. if ( pchSteamInstallPath )
  502. {
  503. char rgchSteamPath[MAX_PATH];
  504. V_ComposeFileName( pchSteamInstallPath, "GameOverlayRenderer" DLL_EXT_STRING, rgchSteamPath, Q_ARRAYSIZE(rgchSteamPath) );
  505. // This could fail, but we can't fix it if it does so just ignore failures
  506. LoadLibrary( rgchSteamPath );
  507. }
  508. SteamAPI_Shutdown();
  509. }
  510. }
  511. #endif
  512. }
  513. //-----------------------------------------------------------------------------
  514. // Inner loop: initialize, shutdown main systems, load steam to
  515. //-----------------------------------------------------------------------------
  516. class CSourceAppSystemGroup : public CSteamAppSystemGroup
  517. {
  518. public:
  519. // Methods of IApplication
  520. virtual bool Create();
  521. virtual bool PreInit();
  522. virtual int Main();
  523. virtual void PostShutdown();
  524. virtual void Destroy();
  525. private:
  526. const char *DetermineDefaultMod();
  527. const char *DetermineDefaultGame();
  528. bool m_bEditMode;
  529. };
  530. //-----------------------------------------------------------------------------
  531. // The dirty disk error report function
  532. //-----------------------------------------------------------------------------
  533. void ReportDirtyDiskNoMaterialSystem()
  534. {
  535. #ifdef _X360
  536. for ( int i = 0; i < 4; ++i )
  537. {
  538. if ( XUserGetSigninState( i ) != eXUserSigninState_NotSignedIn )
  539. {
  540. XShowDirtyDiscErrorUI( i );
  541. return;
  542. }
  543. }
  544. XShowDirtyDiscErrorUI( 0 );
  545. #endif
  546. }
  547. //-----------------------------------------------------------------------------
  548. // Instantiate all main libraries
  549. //-----------------------------------------------------------------------------
  550. bool CSourceAppSystemGroup::Create()
  551. {
  552. IFileSystem *pFileSystem = (IFileSystem*)FindSystem( FILESYSTEM_INTERFACE_VERSION );
  553. pFileSystem->InstallDirtyDiskReportFunc( ReportDirtyDiskNoMaterialSystem );
  554. #ifdef WIN32
  555. CoInitialize( NULL );
  556. #endif
  557. // Are we running in edit mode?
  558. m_bEditMode = CommandLine()->CheckParm( "-edit" );
  559. double st = Plat_FloatTime();
  560. AppSystemInfo_t appSystems[] =
  561. {
  562. { "engine" DLL_EXT_STRING, CVAR_QUERY_INTERFACE_VERSION }, // NOTE: This one must be first!!
  563. { "inputsystem" DLL_EXT_STRING, INPUTSYSTEM_INTERFACE_VERSION },
  564. { "materialsystem" DLL_EXT_STRING, MATERIAL_SYSTEM_INTERFACE_VERSION },
  565. { "datacache" DLL_EXT_STRING, DATACACHE_INTERFACE_VERSION },
  566. { "datacache" DLL_EXT_STRING, MDLCACHE_INTERFACE_VERSION },
  567. { "datacache" DLL_EXT_STRING, STUDIO_DATA_CACHE_INTERFACE_VERSION },
  568. { "studiorender" DLL_EXT_STRING, STUDIO_RENDER_INTERFACE_VERSION },
  569. { "vphysics" DLL_EXT_STRING, VPHYSICS_INTERFACE_VERSION },
  570. { "video_services" DLL_EXT_STRING, VIDEO_SERVICES_INTERFACE_VERSION },
  571. // NOTE: This has to occur before vgui2.dll so it replaces vgui2's surface implementation
  572. { "vguimatsurface" DLL_EXT_STRING, VGUI_SURFACE_INTERFACE_VERSION },
  573. { "vgui2" DLL_EXT_STRING, VGUI_IVGUI_INTERFACE_VERSION },
  574. { "engine" DLL_EXT_STRING, VENGINE_LAUNCHER_API_VERSION },
  575. { "", "" } // Required to terminate the list
  576. };
  577. #if defined( USE_SDL )
  578. AddSystem( (IAppSystem *)CreateSDLMgr(), SDLMGR_INTERFACE_VERSION );
  579. #endif
  580. if ( !AddSystems( appSystems ) )
  581. return false;
  582. // This will be NULL for games that don't support VR. That's ok. Just don't load the DLL
  583. AppModule_t sourceVRModule = LoadModule( "sourcevr" DLL_EXT_STRING );
  584. if( sourceVRModule != APP_MODULE_INVALID )
  585. {
  586. AddSystem( sourceVRModule, SOURCE_VIRTUAL_REALITY_INTERFACE_VERSION );
  587. }
  588. // pull in our filesystem dll to pull the queued loader from it, we need to do it this way due to the
  589. // steam/stdio split for our steam filesystem
  590. char pFileSystemDLL[MAX_PATH];
  591. bool bSteam;
  592. if ( FileSystem_GetFileSystemDLLName( pFileSystemDLL, MAX_PATH, bSteam ) != FS_OK )
  593. return false;
  594. AppModule_t fileSystemModule = LoadModule( pFileSystemDLL );
  595. AddSystem( fileSystemModule, QUEUEDLOADER_INTERFACE_VERSION );
  596. // Hook in datamodel and p4 control if we're running with -tools
  597. if ( IsPC() && ( ( CommandLine()->FindParm( "-tools" ) && !CommandLine()->FindParm( "-nop4" ) ) || CommandLine()->FindParm( "-p4" ) ) )
  598. {
  599. #ifdef STAGING_ONLY
  600. AppModule_t p4libModule = LoadModule( "p4lib" DLL_EXT_STRING );
  601. IP4 *p4 = (IP4*)AddSystem( p4libModule, P4_INTERFACE_VERSION );
  602. // If we are running with -steam then that means the tools are being used by an SDK user. Don't exit in this case!
  603. if ( !p4 && !CommandLine()->FindParm( "-steam" ) )
  604. {
  605. return false;
  606. }
  607. #endif // STAGING_ONLY
  608. AppModule_t vstdlibModule = LoadModule( "vstdlib" DLL_EXT_STRING );
  609. IProcessUtils *processUtils = ( IProcessUtils* )AddSystem( vstdlibModule, PROCESS_UTILS_INTERFACE_VERSION );
  610. if ( !processUtils )
  611. return false;
  612. }
  613. // Connect to iterfaces loaded in AddSystems that we need locally
  614. IMaterialSystem *pMaterialSystem = (IMaterialSystem*)FindSystem( MATERIAL_SYSTEM_INTERFACE_VERSION );
  615. if ( !pMaterialSystem )
  616. return false;
  617. g_pEngineAPI = (IEngineAPI*)FindSystem( VENGINE_LAUNCHER_API_VERSION );
  618. // Load the hammer DLL if we're in editor mode
  619. #if defined( _WIN32 ) && defined( STAGING_ONLY )
  620. if ( m_bEditMode )
  621. {
  622. AppModule_t hammerModule = LoadModule( "hammer_dll" DLL_EXT_STRING );
  623. g_pHammer = (IHammer*)AddSystem( hammerModule, INTERFACEVERSION_HAMMER );
  624. if ( !g_pHammer )
  625. {
  626. return false;
  627. }
  628. }
  629. #endif // defined( _WIN32 ) && defined( STAGING_ONLY )
  630. // Load up the appropriate shader DLL
  631. // This has to be done before connection.
  632. char const* pDLLName = "shaderapidx9" DLL_EXT_STRING;
  633. if ( CommandLine()->FindParm( "-noshaderapi" ) )
  634. {
  635. pDLLName = "shaderapiempty" DLL_EXT_STRING;
  636. }
  637. pMaterialSystem->SetShaderAPI( pDLLName );
  638. double elapsed = Plat_FloatTime() - st;
  639. COM_TimestampedLog( "LoadAppSystems: Took %.4f secs to load libraries and get factories.", (float)elapsed );
  640. return true;
  641. }
  642. bool CSourceAppSystemGroup::PreInit()
  643. {
  644. CreateInterfaceFn factory = GetFactory();
  645. ConnectTier1Libraries( &factory, 1 );
  646. ConVar_Register( );
  647. ConnectTier2Libraries( &factory, 1 );
  648. ConnectTier3Libraries( &factory, 1 );
  649. if ( !g_pFullFileSystem || !g_pMaterialSystem )
  650. return false;
  651. CFSSteamSetupInfo steamInfo;
  652. steamInfo.m_bToolsMode = false;
  653. steamInfo.m_bSetSteamDLLPath = false;
  654. steamInfo.m_bSteam = g_pFullFileSystem->IsSteam();
  655. steamInfo.m_bOnlyUseDirectoryName = true;
  656. steamInfo.m_pDirectoryName = DetermineDefaultMod();
  657. if ( !steamInfo.m_pDirectoryName )
  658. {
  659. steamInfo.m_pDirectoryName = DetermineDefaultGame();
  660. if ( !steamInfo.m_pDirectoryName )
  661. {
  662. Error( "FileSystem_LoadFileSystemModule: no -defaultgamedir or -game specified." );
  663. }
  664. }
  665. if ( FileSystem_SetupSteamEnvironment( steamInfo ) != FS_OK )
  666. return false;
  667. CFSMountContentInfo fsInfo;
  668. fsInfo.m_pFileSystem = g_pFullFileSystem;
  669. fsInfo.m_bToolsMode = m_bEditMode;
  670. fsInfo.m_pDirectoryName = steamInfo.m_GameInfoPath;
  671. if ( FileSystem_MountContent( fsInfo ) != FS_OK )
  672. return false;
  673. if ( IsPC() || !IsX360() )
  674. {
  675. fsInfo.m_pFileSystem->AddSearchPath( "platform", "PLATFORM" );
  676. }
  677. else
  678. {
  679. // 360 needs absolute paths
  680. FileSystem_AddSearchPath_Platform( g_pFullFileSystem, steamInfo.m_GameInfoPath );
  681. }
  682. if ( IsPC() )
  683. {
  684. // This will get called multiple times due to being here, but only the first one will do anything
  685. reslistgenerator->Init( GetBaseDirectory(), CommandLine()->ParmValue( "-game", "hl2" ) );
  686. // This will also get called each time, but will actually fix up the command line as needed
  687. reslistgenerator->SetupCommandLine();
  688. }
  689. // FIXME: Logfiles is mod-specific, needs to move into the engine.
  690. g_LogFiles.Init();
  691. // Required to run through the editor
  692. if ( m_bEditMode )
  693. {
  694. g_pMaterialSystem->EnableEditorMaterials();
  695. }
  696. StartupInfo_t info;
  697. info.m_pInstance = GetAppInstance();
  698. info.m_pBaseDirectory = GetBaseDirectory();
  699. info.m_pInitialMod = DetermineDefaultMod();
  700. info.m_pInitialGame = DetermineDefaultGame();
  701. info.m_pParentAppSystemGroup = this;
  702. info.m_bTextMode = g_bTextMode;
  703. g_pEngineAPI->SetStartupInfo( info );
  704. return true;
  705. }
  706. int CSourceAppSystemGroup::Main()
  707. {
  708. return g_pEngineAPI->Run();
  709. }
  710. void CSourceAppSystemGroup::PostShutdown()
  711. {
  712. // FIXME: Logfiles is mod-specific, needs to move into the engine.
  713. g_LogFiles.Shutdown();
  714. reslistgenerator->Shutdown();
  715. DisconnectTier3Libraries();
  716. DisconnectTier2Libraries();
  717. ConVar_Unregister( );
  718. DisconnectTier1Libraries();
  719. }
  720. void CSourceAppSystemGroup::Destroy()
  721. {
  722. g_pEngineAPI = NULL;
  723. g_pMaterialSystem = NULL;
  724. g_pHammer = NULL;
  725. #ifdef WIN32
  726. CoUninitialize();
  727. #endif
  728. }
  729. //-----------------------------------------------------------------------------
  730. // Determines the initial mod to use at load time.
  731. // We eventually (hopefully) will be able to switch mods at runtime
  732. // because the engine/hammer integration really wants this feature.
  733. //-----------------------------------------------------------------------------
  734. const char *CSourceAppSystemGroup::DetermineDefaultMod()
  735. {
  736. if ( !m_bEditMode )
  737. {
  738. return CommandLine()->ParmValue( "-game", DEFAULT_HL2_GAMEDIR );
  739. }
  740. return g_pHammer->GetDefaultMod();
  741. }
  742. const char *CSourceAppSystemGroup::DetermineDefaultGame()
  743. {
  744. if ( !m_bEditMode )
  745. {
  746. return CommandLine()->ParmValue( "-defaultgamedir", DEFAULT_HL2_GAMEDIR );
  747. }
  748. return g_pHammer->GetDefaultGame();
  749. }
  750. //-----------------------------------------------------------------------------
  751. // MessageBox for SDL/OSX
  752. //-----------------------------------------------------------------------------
  753. #if defined( USE_SDL ) && !defined( _WIN32 )
  754. int MessageBox( HWND hWnd, const char *message, const char *header, unsigned uType )
  755. {
  756. SDL_ShowSimpleMessageBox( 0, header, message, GetAssertDialogParent() );
  757. return 0;
  758. }
  759. #endif
  760. //-----------------------------------------------------------------------------
  761. // Allow only one windowed source app to run at a time
  762. //-----------------------------------------------------------------------------
  763. #ifdef WIN32
  764. HANDLE g_hMutex = NULL;
  765. #elif defined(POSIX)
  766. int g_lockfd = -1;
  767. char g_lockFilename[MAX_PATH];
  768. #endif
  769. bool GrabSourceMutex()
  770. {
  771. #ifdef WIN32
  772. if ( IsPC() )
  773. {
  774. // don't allow more than one instance to run
  775. g_hMutex = ::CreateMutex(NULL, FALSE, TEXT("hl2_singleton_mutex"));
  776. unsigned int waitResult = ::WaitForSingleObject(g_hMutex, 0);
  777. // Here, we have the mutex
  778. if (waitResult == WAIT_OBJECT_0 || waitResult == WAIT_ABANDONED)
  779. return true;
  780. // couldn't get the mutex, we must be running another instance
  781. ::CloseHandle(g_hMutex);
  782. return false;
  783. }
  784. #elif defined(POSIX)
  785. // Under OSX use flock in /tmp/source_engine_<game>.lock, create the file if it doesn't exist
  786. const char *pchGameParam = CommandLine()->ParmValue( "-game", DEFAULT_HL2_GAMEDIR );
  787. CRC32_t gameCRC;
  788. CRC32_Init(&gameCRC);
  789. CRC32_ProcessBuffer( &gameCRC, (void *)pchGameParam, Q_strlen( pchGameParam ) );
  790. CRC32_Final( &gameCRC );
  791. #ifdef LINUX
  792. /*
  793. * Linux
  794. */
  795. // Check TMPDIR environment variable for temp directory.
  796. char *tmpdir = getenv( "TMPDIR" );
  797. // If it's NULL, or it doesn't exist, or it isn't a directory, fallback to /tmp.
  798. struct stat buf;
  799. if( !tmpdir || stat( tmpdir, &buf ) || !S_ISDIR ( buf.st_mode ) )
  800. tmpdir = "/tmp";
  801. V_snprintf( g_lockFilename, sizeof(g_lockFilename), "%s/source_engine_%u.lock", tmpdir, gameCRC );
  802. g_lockfd = open( g_lockFilename, O_WRONLY | O_CREAT, 0666 );
  803. if ( g_lockfd == -1 )
  804. {
  805. printf( "open(%s) failed\n", g_lockFilename );
  806. return false;
  807. }
  808. struct flock fl;
  809. fl.l_type = F_WRLCK;
  810. fl.l_whence = SEEK_SET;
  811. fl.l_start = 0;
  812. fl.l_len = 1;
  813. if ( fcntl ( g_lockfd, F_SETLK, &fl ) == -1 )
  814. {
  815. printf( "fcntl(%d) for %s failed\n", g_lockfd, g_lockFilename );
  816. return false;
  817. }
  818. return true;
  819. #else
  820. /*
  821. * OSX
  822. */
  823. V_snprintf( g_lockFilename, sizeof(g_lockFilename), "/tmp/source_engine_%u.lock", gameCRC );
  824. g_lockfd = open( g_lockFilename, O_CREAT | O_WRONLY | O_EXLOCK | O_NONBLOCK | O_TRUNC, 0777 );
  825. if (g_lockfd >= 0)
  826. {
  827. // make sure we give full perms to the file, we only one instance per machine
  828. fchmod( g_lockfd, 0777 );
  829. // we leave the file open, under unix rules when we die we'll automatically close and remove the locks
  830. return true;
  831. }
  832. // We were unable to open the file, it should be because we are unable to retain a lock
  833. if ( errno != EWOULDBLOCK)
  834. {
  835. fprintf( stderr, "unexpected error %d trying to exclusively lock %s\n", errno, g_lockFilename );
  836. }
  837. return false;
  838. #endif // OSX
  839. #endif // POSIX
  840. return true;
  841. }
  842. void ReleaseSourceMutex()
  843. {
  844. #ifdef WIN32
  845. if ( IsPC() && g_hMutex )
  846. {
  847. ::ReleaseMutex( g_hMutex );
  848. ::CloseHandle( g_hMutex );
  849. g_hMutex = NULL;
  850. }
  851. #elif defined(POSIX)
  852. if ( g_lockfd != -1 )
  853. {
  854. close( g_lockfd );
  855. g_lockfd = -1;
  856. unlink( g_lockFilename );
  857. }
  858. #endif
  859. }
  860. // Remove all but the last -game parameter.
  861. // This is for mods based off something other than Half-Life 2 (like HL2MP mods).
  862. // The Steam UI does 'steam -applaunch 320 -game c:\steam\steamapps\sourcemods\modname', but applaunch inserts
  863. // its own -game parameter, which would supercede the one we really want if we didn't intercede here.
  864. void RemoveSpuriousGameParameters()
  865. {
  866. // Find the last -game parameter.
  867. int nGameArgs = 0;
  868. char lastGameArg[MAX_PATH];
  869. for ( int i=0; i < CommandLine()->ParmCount()-1; i++ )
  870. {
  871. if ( Q_stricmp( CommandLine()->GetParm( i ), "-game" ) == 0 )
  872. {
  873. Q_snprintf( lastGameArg, sizeof( lastGameArg ), "\"%s\"", CommandLine()->GetParm( i+1 ) );
  874. ++nGameArgs;
  875. ++i;
  876. }
  877. }
  878. // We only care if > 1 was specified.
  879. if ( nGameArgs > 1 )
  880. {
  881. CommandLine()->RemoveParm( "-game" );
  882. CommandLine()->AppendParm( "-game", lastGameArg );
  883. }
  884. }
  885. /*
  886. ============
  887. va
  888. does a varargs printf into a temp buffer, so I don't need to have
  889. varargs versions of all text functions.
  890. ============
  891. */
  892. static char *va( char *format, ... )
  893. {
  894. va_list argptr;
  895. static char string[8][512];
  896. static int curstring = 0;
  897. curstring = ( curstring + 1 ) % 8;
  898. va_start (argptr, format);
  899. Q_vsnprintf( string[curstring], sizeof( string[curstring] ), format, argptr );
  900. va_end (argptr);
  901. return string[curstring];
  902. }
  903. //-----------------------------------------------------------------------------
  904. // Purpose:
  905. // Input : *param -
  906. // Output : static char const
  907. //-----------------------------------------------------------------------------
  908. static char const *Cmd_TranslateFileAssociation(char const *param )
  909. {
  910. static char sz[ 512 ];
  911. char *retval = NULL;
  912. char temp[ 512 ];
  913. Q_strncpy( temp, param, sizeof( temp ) );
  914. Q_FixSlashes( temp );
  915. #ifdef WIN32
  916. Q_strlower( temp );
  917. #endif
  918. const char *extension = V_GetFileExtension(temp);
  919. // must have an extension to map
  920. if (!extension)
  921. return retval;
  922. extension--; // back up so we have the . in the extension
  923. int c = ARRAYSIZE( g_FileAssociations );
  924. for ( int i = 0; i < c; i++ )
  925. {
  926. FileAssociationInfo& info = g_FileAssociations[ i ];
  927. if ( ! Q_strcmp( extension, info.extension ) &&
  928. ! CommandLine()->FindParm(va( "+%s", info.command_to_issue ) ) )
  929. {
  930. // Translate if haven't already got one of these commands
  931. Q_strncpy( sz, temp, sizeof( sz ) );
  932. Q_FileBase( sz, temp, sizeof( sz ) );
  933. Q_snprintf( sz, sizeof( sz ), "%s %s", info.command_to_issue, temp );
  934. retval = sz;
  935. break;
  936. }
  937. }
  938. // return null if no translation, otherwise return commands
  939. return retval;
  940. }
  941. //-----------------------------------------------------------------------------
  942. // Purpose: Converts all the convar args into a convar command
  943. // Input : none
  944. // Output : const char * series of convars
  945. //-----------------------------------------------------------------------------
  946. static const char *BuildCommand()
  947. {
  948. static CUtlBuffer build( 0, 0, CUtlBuffer::TEXT_BUFFER );
  949. build.Clear();
  950. // arg[0] is the executable name
  951. for ( int i=1; i < CommandLine()->ParmCount(); i++ )
  952. {
  953. const char *szParm = CommandLine()->GetParm(i);
  954. if (!szParm) continue;
  955. if (szParm[0] == '-')
  956. {
  957. // skip -XXX options and eat their args
  958. const char *szValue = CommandLine()->ParmValue(szParm);
  959. if ( szValue ) i++;
  960. continue;
  961. }
  962. if (szParm[0] == '+')
  963. {
  964. // convert +XXX options and stuff them into the build buffer
  965. const char *szValue = CommandLine()->ParmValue(szParm);
  966. if (szValue)
  967. {
  968. build.PutString(va("%s %s;", szParm+1, szValue));
  969. i++;
  970. }
  971. else
  972. {
  973. build.PutString(szParm+1);
  974. build.PutChar(';');
  975. }
  976. }
  977. else
  978. {
  979. // singleton values, convert to command
  980. char const *translated = Cmd_TranslateFileAssociation( CommandLine()->GetParm( i ) );
  981. if (translated)
  982. {
  983. build.PutString(translated);
  984. build.PutChar(';');
  985. }
  986. }
  987. }
  988. build.PutChar( '\0' );
  989. return (const char *)build.Base();
  990. }
  991. //-----------------------------------------------------------------------------
  992. // Purpose: The real entry point for the application
  993. // Input : hInstance -
  994. // hPrevInstance -
  995. // lpCmdLine -
  996. // nCmdShow -
  997. // Output : int APIENTRY
  998. //-----------------------------------------------------------------------------
  999. #ifdef WIN32
  1000. extern "C" __declspec(dllexport) int LauncherMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
  1001. #else
  1002. DLL_EXPORT int LauncherMain( int argc, char **argv )
  1003. #endif
  1004. {
  1005. #ifdef LINUX
  1006. // Temporary fix to stop us from crashing in printf/sscanf functions that don't expect
  1007. // localization to mess with your "." and "," float seperators. Mac OSX also sets LANG
  1008. // to en_US.UTF-8 before starting up (in info.plist I believe).
  1009. // We need to double check that localization for libcef is handled correctly
  1010. // when we slam things to en_US.UTF-8.
  1011. // Also check if C.UTF-8 exists and use it? This file: /usr/lib/locale/C.UTF-8.
  1012. // It looks like it's only installed on Debian distros right now though.
  1013. const char en_US[] = "en_US.UTF-8";
  1014. setenv( "LC_ALL", en_US, 1 );
  1015. setlocale( LC_ALL, en_US );
  1016. const char *CurrentLocale = setlocale( LC_ALL, NULL );
  1017. if ( Q_stricmp( CurrentLocale, en_US ) )
  1018. {
  1019. Warning( "WARNING: setlocale('%s') failed, using locale:'%s'. International characters may not work.\n", en_US, CurrentLocale );
  1020. }
  1021. #endif // LINUX
  1022. #ifdef WIN32
  1023. SetAppInstance( hInstance );
  1024. #elif defined( POSIX )
  1025. // Store off command line for argument searching
  1026. Plat_SetCommandLine( BuildCmdLine( argc, argv, false ) );
  1027. if( CommandLine()->CheckParm( "-sleepatstartup" ) )
  1028. {
  1029. // When launching from Steam, it can be difficult to get a debugger attached when you're
  1030. // crashing quickly at startup. So add a -sleepatstartup command line and sleep for 5
  1031. // seconds which should allow time to attach a debugger.
  1032. sleep( 5 );
  1033. }
  1034. #endif
  1035. // Hook the debug output stuff.
  1036. SpewOutputFunc( LauncherDefaultSpewFunc );
  1037. if ( 0 && IsWin98OrOlder() )
  1038. {
  1039. Error( "This build does not currently run under Windows 98/Me." );
  1040. return -1;
  1041. }
  1042. // Quickly check the hardware key, essentially a warning shot.
  1043. if ( !Plat_VerifyHardwareKeyPrompt() )
  1044. {
  1045. return -1;
  1046. }
  1047. const char *filename;
  1048. #ifdef WIN32
  1049. CommandLine()->CreateCmdLine( IsPC() ? VCRHook_GetCommandLine() : lpCmdLine );
  1050. #else
  1051. CommandLine()->CreateCmdLine( argc, argv );
  1052. #endif
  1053. // No -dxlevel or +mat_hdr_level allowed on POSIX
  1054. #ifdef POSIX
  1055. CommandLine()->RemoveParm( "-dxlevel" );
  1056. CommandLine()->RemoveParm( "+mat_hdr_level" );
  1057. CommandLine()->RemoveParm( "+mat_dxlevel" );
  1058. #endif
  1059. // If we're using -default command line parameters, get rid of DX8 settings.
  1060. if ( CommandLine()->CheckParm( "-default" ) )
  1061. {
  1062. CommandLine()->RemoveParm( "-dxlevel" );
  1063. CommandLine()->RemoveParm( "-maxdxlevel" );
  1064. CommandLine()->RemoveParm( "+mat_dxlevel" );
  1065. }
  1066. // Figure out the directory the executable is running from
  1067. UTIL_ComputeBaseDir();
  1068. #if defined( _X360 )
  1069. bool bSpewDllInfo = CommandLine()->CheckParm( "-dllinfo" );
  1070. bool bWaitForConsole = CommandLine()->CheckParm( "-vxconsole" );
  1071. XboxConsoleInit();
  1072. XBX_InitConsoleMonitor( bWaitForConsole || bSpewDllInfo );
  1073. #endif
  1074. #if defined( _X360 )
  1075. if ( bWaitForConsole )
  1076. COM_TimestampedLog( "LauncherMain: Application Start - %s", CommandLine()->GetCmdLine() );
  1077. if ( bSpewDllInfo )
  1078. {
  1079. XBX_DumpDllInfo( GetBaseDirectory() );
  1080. Error( "Stopped!\n" );
  1081. }
  1082. int storageID = XboxLaunch()->GetStorageID();
  1083. if ( storageID != XBX_INVALID_STORAGE_ID && storageID != XBX_STORAGE_DECLINED )
  1084. {
  1085. // Validate the storage device
  1086. XDEVICE_DATA deviceData;
  1087. DWORD ret = XContentGetDeviceData( storageID, &deviceData );
  1088. if ( ret != ERROR_SUCCESS )
  1089. {
  1090. // Device was removed
  1091. storageID = XBX_INVALID_STORAGE_ID;
  1092. XBX_QueueEvent( XEV_LISTENER_NOTIFICATION, WM_SYS_STORAGEDEVICESCHANGED, 0, 0 );
  1093. }
  1094. }
  1095. XBX_SetStorageDeviceId( storageID );
  1096. int userID = XboxLaunch()->GetUserID();
  1097. if ( !IsRetail() && userID == XBX_INVALID_USER_ID )
  1098. {
  1099. // didn't come from appchooser, try find a valid user id for dev purposes
  1100. XUSER_SIGNIN_INFO info;
  1101. for ( int i = 0; i < 4; ++i )
  1102. {
  1103. if ( ERROR_NO_SUCH_USER != XUserGetSigninInfo( i, 0, &info ) )
  1104. {
  1105. userID = i;
  1106. break;
  1107. }
  1108. }
  1109. }
  1110. XBX_SetPrimaryUserId( userID );
  1111. #endif // defined( _X360 )
  1112. #ifdef POSIX
  1113. {
  1114. struct stat st;
  1115. if ( stat( RELAUNCH_FILE, &st ) == 0 )
  1116. {
  1117. unlink( RELAUNCH_FILE );
  1118. }
  1119. }
  1120. #endif
  1121. // This call is to emulate steam's injection of the GameOverlay DLL into our process if we
  1122. // are running from the command line directly, this allows the same experience the user gets
  1123. // to be present when running from perforce, the call has no effect on X360
  1124. TryToLoadSteamOverlayDLL();
  1125. // Start VCR mode?
  1126. if ( CommandLine()->CheckParm( "-vcrrecord", &filename ) )
  1127. {
  1128. if ( !VCRStart( filename, true, &g_VCRHelpers ) )
  1129. {
  1130. Error( "-vcrrecord: can't open '%s' for writing.\n", filename );
  1131. return -1;
  1132. }
  1133. }
  1134. else if ( CommandLine()->CheckParm( "-vcrplayback", &filename ) )
  1135. {
  1136. if ( !VCRStart( filename, false, &g_VCRHelpers ) )
  1137. {
  1138. Error( "-vcrplayback: can't open '%s' for reading.\n", filename );
  1139. return -1;
  1140. }
  1141. }
  1142. // See the function for why we do this.
  1143. RemoveSpuriousGameParameters();
  1144. #ifdef WIN32
  1145. if ( IsPC() )
  1146. {
  1147. // initialize winsock
  1148. WSAData wsaData;
  1149. int nError = ::WSAStartup( MAKEWORD(2,0), &wsaData );
  1150. if ( nError )
  1151. {
  1152. Msg( "Warning! Failed to start Winsock via WSAStartup = 0x%x.\n", nError);
  1153. }
  1154. }
  1155. #endif
  1156. // Run in text mode? (No graphics or sound).
  1157. if ( CommandLine()->CheckParm( "-textmode" ) )
  1158. {
  1159. g_bTextMode = true;
  1160. InitTextMode();
  1161. }
  1162. #ifdef WIN32
  1163. else
  1164. {
  1165. int retval = -1;
  1166. // Can only run one windowed source app at a time
  1167. if ( !GrabSourceMutex() )
  1168. {
  1169. // Allow the user to explicitly say they want to be able to run multiple instances of the source mutex.
  1170. // Useful for side-by-side comparisons of different renderers.
  1171. bool multiRun = CommandLine()->CheckParm( "-multirun" ) != NULL;
  1172. // We're going to hijack the existing session and load a new savegame into it. This will mainly occur when users click on links in Bugzilla that will automatically copy saves and load them
  1173. // directly from the web browser. The -hijack command prevents the launcher from objecting that there is already an instance of the game.
  1174. if (CommandLine()->CheckParm( "-hijack" ))
  1175. {
  1176. HWND hwndEngine = FindWindow( "Valve001", NULL );
  1177. // Can't find the engine
  1178. if ( hwndEngine == NULL )
  1179. {
  1180. ::MessageBox( NULL, "The modified entity keyvalues could not be sent to the Source Engine because the engine does not appear to be running.", "Source Engine Not Running", MB_OK | MB_ICONEXCLAMATION );
  1181. }
  1182. else
  1183. {
  1184. const char *szCommand = BuildCommand();
  1185. //
  1186. // Fill out the data structure to send to the engine.
  1187. //
  1188. COPYDATASTRUCT copyData;
  1189. copyData.cbData = strlen( szCommand ) + 1;
  1190. copyData.dwData = 0;
  1191. copyData.lpData = ( void * )szCommand;
  1192. if ( !::SendMessage( hwndEngine, WM_COPYDATA, 0, (LPARAM)&copyData ) )
  1193. {
  1194. ::MessageBox( NULL, "The Source Engine was found running, but did not accept the request to load a savegame. It may be an old version of the engine that does not support this functionality.", "Source Engine Declined Request", MB_OK | MB_ICONEXCLAMATION );
  1195. }
  1196. else
  1197. {
  1198. retval = 0;
  1199. }
  1200. free((void *)szCommand);
  1201. }
  1202. }
  1203. else
  1204. {
  1205. if (!multiRun) {
  1206. ::MessageBox(NULL, "Only one instance of the game can be running at one time.", "Source - Warning", MB_ICONINFORMATION | MB_OK);
  1207. }
  1208. }
  1209. if (!multiRun) {
  1210. return retval;
  1211. }
  1212. }
  1213. }
  1214. #elif defined( POSIX )
  1215. else
  1216. {
  1217. if ( !GrabSourceMutex() )
  1218. {
  1219. ::MessageBox(NULL, "Only one instance of the game can be running at one time.", "Source - Warning", 0 );
  1220. return -1;
  1221. }
  1222. }
  1223. #endif
  1224. #ifdef WIN32
  1225. // Make low priority?
  1226. if ( CommandLine()->CheckParm( "-low" ) )
  1227. {
  1228. SetPriorityClass( GetCurrentProcess(), IDLE_PRIORITY_CLASS );
  1229. }
  1230. else if ( CommandLine()->CheckParm( "-high" ) )
  1231. {
  1232. SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS );
  1233. }
  1234. #endif
  1235. // If game is not run from Steam then add -insecure in order to avoid client timeout message
  1236. if ( NULL == CommandLine()->CheckParm( "-steam" ) )
  1237. {
  1238. CommandLine()->AppendParm( "-insecure", NULL );
  1239. }
  1240. // Figure out the directory the executable is running from
  1241. // and make that be the current working directory
  1242. _chdir( GetBaseDirectory() );
  1243. g_LeakDump.m_bCheckLeaks = CommandLine()->CheckParm( "-leakcheck" ) ? true : false;
  1244. bool bRestart = true;
  1245. while ( bRestart )
  1246. {
  1247. bRestart = false;
  1248. CSourceAppSystemGroup sourceSystems;
  1249. CSteamApplication steamApplication( &sourceSystems );
  1250. int nRetval = steamApplication.Run();
  1251. if ( steamApplication.GetErrorStage() == CSourceAppSystemGroup::INITIALIZATION )
  1252. {
  1253. bRestart = (nRetval == INIT_RESTART);
  1254. }
  1255. else if ( nRetval == RUN_RESTART )
  1256. {
  1257. bRestart = true;
  1258. }
  1259. bool bReslistCycle = false;
  1260. if ( !bRestart )
  1261. {
  1262. bReslistCycle = reslistgenerator->ShouldContinue();
  1263. bRestart = bReslistCycle;
  1264. }
  1265. if ( !bReslistCycle )
  1266. {
  1267. // Remove any overrides in case settings changed
  1268. CommandLine()->RemoveParm( "-w" );
  1269. CommandLine()->RemoveParm( "-h" );
  1270. CommandLine()->RemoveParm( "-width" );
  1271. CommandLine()->RemoveParm( "-height" );
  1272. CommandLine()->RemoveParm( "-sw" );
  1273. CommandLine()->RemoveParm( "-startwindowed" );
  1274. CommandLine()->RemoveParm( "-windowed" );
  1275. CommandLine()->RemoveParm( "-window" );
  1276. CommandLine()->RemoveParm( "-full" );
  1277. CommandLine()->RemoveParm( "-fullscreen" );
  1278. CommandLine()->RemoveParm( "-dxlevel" );
  1279. CommandLine()->RemoveParm( "-autoconfig" );
  1280. CommandLine()->RemoveParm( "+mat_hdr_level" );
  1281. }
  1282. }
  1283. #ifdef WIN32
  1284. if ( IsPC() )
  1285. {
  1286. // shutdown winsock
  1287. int nError = ::WSACleanup();
  1288. if ( nError )
  1289. {
  1290. Msg( "Warning! Failed to complete WSACleanup = 0x%x.\n", nError );
  1291. }
  1292. }
  1293. #endif
  1294. // Allow other source apps to run
  1295. ReleaseSourceMutex();
  1296. #if defined( WIN32 ) && !defined( _X360 )
  1297. // Now that the mutex has been released, check HKEY_CURRENT_USER\Software\Valve\Source\Relaunch URL. If there is a URL here, exec it.
  1298. // This supports the capability of immediately re-launching the the game via Steam in a different audio language
  1299. HKEY hKey;
  1300. if ( RegOpenKeyEx( HKEY_CURRENT_USER, "Software\\Valve\\Source", NULL, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS )
  1301. {
  1302. char szValue[MAX_PATH];
  1303. DWORD dwValueLen = MAX_PATH;
  1304. if ( RegQueryValueEx( hKey, "Relaunch URL", NULL, NULL, (unsigned char*)szValue, &dwValueLen ) == ERROR_SUCCESS )
  1305. {
  1306. ShellExecute (0, "open", szValue, 0, 0, SW_SHOW);
  1307. RegDeleteValue( hKey, "Relaunch URL" );
  1308. }
  1309. RegCloseKey(hKey);
  1310. }
  1311. #elif defined( OSX ) || defined( LINUX )
  1312. struct stat st;
  1313. if ( stat( RELAUNCH_FILE, &st ) == 0 )
  1314. {
  1315. FILE *fp = fopen( RELAUNCH_FILE, "r" );
  1316. if ( fp )
  1317. {
  1318. char szCmd[256];
  1319. int nChars = fread( szCmd, 1, sizeof(szCmd), fp );
  1320. if ( nChars > 0 )
  1321. {
  1322. if ( nChars > (sizeof(szCmd)-1) )
  1323. {
  1324. nChars = (sizeof(szCmd)-1);
  1325. }
  1326. szCmd[nChars] = 0;
  1327. char szOpenLine[ MAX_PATH ];
  1328. #if defined( LINUX )
  1329. Q_snprintf( szOpenLine, sizeof(szOpenLine), "xdg-open \"%s\"", szCmd );
  1330. #else
  1331. Q_snprintf( szOpenLine, sizeof(szOpenLine), "open \"%s\"", szCmd );
  1332. #endif
  1333. system( szOpenLine );
  1334. }
  1335. fclose( fp );
  1336. unlink( RELAUNCH_FILE );
  1337. }
  1338. }
  1339. #elif defined( _X360 )
  1340. #else
  1341. #error
  1342. #endif
  1343. return 0;
  1344. }