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.

684 lines
18 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include <vgui/ILocalize.h>
  7. #include <vgui/IScheme.h>
  8. #include <vgui/ISurface.h>
  9. #include <vgui/IVGui.h>
  10. #include <vgui/IInput.h>
  11. #include <vgui/isystem.h>
  12. #include <vgui_controls/MessageBox.h>
  13. #include <vgui_controls/Controls.h>
  14. #include <vgui_controls/Panel.h>
  15. #include "SDKLauncherDialog.h"
  16. #include "appframework/tier3app.h"
  17. #include "tier0/icommandline.h"
  18. #include "filesystem_tools.h"
  19. #include "sdklauncher_main.h"
  20. #include "configs.h"
  21. #include "min_footprint_files.h"
  22. #include "CreateModWizard.h"
  23. #include "inputsystem/iinputsystem.h"
  24. #include <io.h>
  25. #include <stdio.h>
  26. // Since windows redefines MessageBox.
  27. typedef vgui::MessageBox vguiMessageBox;
  28. #include <winsock2.h>
  29. #include "steam/steam_api.h"
  30. // memdbgon must be the last include file in a .cpp file!!!
  31. #include <tier0/memdbgon.h>
  32. HANDLE g_dwChangeHandle = NULL;
  33. #define DEFAULTGAMEDIR_KEYNAME "DefaultGameDir"
  34. // Dummy window
  35. static WNDCLASS staticWndclass = { NULL };
  36. static ATOM staticWndclassAtom = 0;
  37. static HWND staticHwnd = 0;
  38. CSteamAPIContext g_SteamAPIContext;
  39. CSteamAPIContext *steamapicontext = &g_SteamAPIContext;
  40. // This is the base engine + mod-specific game dir (e.g. "c:\tf2\mytfmod\")
  41. char gamedir[1024];
  42. extern char g_engineDir[50];
  43. CSDKLauncherDialog *g_pMainFrame = 0;
  44. bool g_bAutoHL2Mod = false;
  45. bool g_bModWizard_CmdLineFields = false;
  46. char g_ModWizard_CmdLine_ModDir[MAX_PATH];
  47. char g_ModWizard_CmdLine_ModName[256];
  48. bool g_bAppQuit = false;
  49. //-----------------------------------------------------------------------------
  50. // Purpose: Message handler for dummy app
  51. //-----------------------------------------------------------------------------
  52. static LRESULT CALLBACK messageProc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
  53. {
  54. // See if we've gotten a VPROJECT change
  55. if ( msg == WM_SETTINGCHANGE )
  56. {
  57. if ( g_pMainFrame != NULL )
  58. {
  59. char szCurrentGame[MAX_PATH];
  60. // Get VCONFIG from the registry
  61. GetVConfigRegistrySetting( GAMEDIR_TOKEN, szCurrentGame, sizeof( szCurrentGame ) );
  62. g_pMainFrame->SetCurrentGame( szCurrentGame );
  63. }
  64. }
  65. return ::DefWindowProc(hwnd,msg,wparam,lparam);
  66. }
  67. const char* GetLastWindowsErrorString()
  68. {
  69. static char err[2048];
  70. LPVOID lpMsgBuf;
  71. FormatMessage(
  72. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  73. FORMAT_MESSAGE_FROM_SYSTEM |
  74. FORMAT_MESSAGE_IGNORE_INSERTS,
  75. NULL,
  76. GetLastError(),
  77. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
  78. (LPTSTR) &lpMsgBuf,
  79. 0,
  80. NULL
  81. );
  82. strncpy( err, (char*)lpMsgBuf, sizeof( err ) );
  83. LocalFree( lpMsgBuf );
  84. err[ sizeof( err ) - 1 ] = 0;
  85. return err;
  86. }
  87. //-----------------------------------------------------------------------------
  88. // Purpose: Creates a dummy window that handles windows messages
  89. //-----------------------------------------------------------------------------
  90. void CreateMessageWindow( void )
  91. {
  92. // Make and register a very simple window class
  93. memset(&staticWndclass, 0, sizeof(staticWndclass));
  94. staticWndclass.style = 0;
  95. staticWndclass.lpfnWndProc = messageProc;
  96. staticWndclass.hInstance = GetModuleHandle(NULL);
  97. staticWndclass.lpszClassName = "SDKLauncher_Window";
  98. staticWndclassAtom = ::RegisterClass( &staticWndclass );
  99. // Create an empty window just for message handling
  100. staticHwnd = CreateWindowEx(0, "SDKLauncher_Window", "Hidden Window", 0, 0, 0, 1, 1, NULL, NULL, GetModuleHandle(NULL), NULL);
  101. }
  102. //-----------------------------------------------------------------------------
  103. // Purpose:
  104. //-----------------------------------------------------------------------------
  105. void ShutdownMessageWindow( void )
  106. {
  107. // Kill our windows instance
  108. ::DestroyWindow( staticHwnd );
  109. ::UnregisterClass("VConfig_Window", ::GetModuleHandle(NULL));
  110. }
  111. SpewRetval_t SDKLauncherSpewOutputFunc( SpewType_t spewType, char const *pMsg )
  112. {
  113. #ifdef _WIN32
  114. OutputDebugString( pMsg );
  115. #endif
  116. if (spewType == SPEW_ERROR)
  117. {
  118. // In Windows vgui mode, make a message box or they won't ever see the error.
  119. #ifdef _WIN32
  120. MessageBox( NULL, pMsg, "Error", MB_OK | MB_TASKMODAL );
  121. TerminateProcess( GetCurrentProcess(), 1 );
  122. #elif _LINUX
  123. _exit(1);
  124. #else
  125. #error "Implement me"
  126. #endif
  127. return SPEW_ABORT;
  128. }
  129. if (spewType == SPEW_ASSERT)
  130. {
  131. if ( CommandLine()->FindParm( "-noassert" ) == 0 )
  132. return SPEW_DEBUGGER;
  133. else
  134. return SPEW_CONTINUE;
  135. }
  136. return SPEW_CONTINUE;
  137. }
  138. const char* GetSDKLauncherBinDirectory()
  139. {
  140. static char path[MAX_PATH] = {0};
  141. if ( path[0] == 0 )
  142. {
  143. GetModuleFileName( (HMODULE)GetAppInstance(), path, sizeof( path ) );
  144. Q_StripLastDir( path, sizeof( path ) ); // Get rid of the filename.
  145. Q_StripTrailingSlash( path );
  146. }
  147. return path;
  148. }
  149. const char* GetSDKToolsBinDirectory( )
  150. {
  151. static char path[MAX_PATH] = {0};
  152. if ( path[0] == 0 )
  153. {
  154. GetModuleFileName( (HMODULE)GetAppInstance(), path, sizeof( path ) );
  155. Q_StripLastDir( path, sizeof( path ) ); // Get rid of the filename.
  156. V_strncat( path, g_engineDir, sizeof( path ) );
  157. V_strncat( path, "\\bin", sizeof( path ) );
  158. }
  159. return path;
  160. }
  161. const char* GetSDKLauncherBaseDirectory()
  162. {
  163. static char basedir[512] = {0};
  164. if ( basedir[0] == 0 )
  165. {
  166. Q_strncpy( basedir, GetSDKLauncherBinDirectory(), sizeof( basedir ) );
  167. Q_StripLastDir( basedir, sizeof( basedir ) ); // Get rid of the bin directory.
  168. Q_StripTrailingSlash( basedir );
  169. }
  170. return basedir;
  171. }
  172. void SubstituteBaseDir( const char *pIn, char *pOut, int outLen )
  173. {
  174. Q_StrSubst( pIn, "%basedir%", GetSDKLauncherBaseDirectory(), pOut, outLen );
  175. }
  176. CUtlVector<char> g_FileData;
  177. CUtlVector<char> g_ReplacementData[2];
  178. CUtlVector<char>* GetFileStringWithReplacements(
  179. const char *pInputFilename,
  180. const char **ppReplacements, int nReplacements,
  181. int &dataWriteLen )
  182. {
  183. Assert( nReplacements % 2 == 0 );
  184. // Read in the file data.
  185. FileHandle_t hFile = g_pFullFileSystem->Open( pInputFilename, "rb" );
  186. if ( !hFile )
  187. {
  188. return false;
  189. }
  190. g_FileData.SetSize( g_pFullFileSystem->Size( hFile ) );
  191. g_pFullFileSystem->Read( g_FileData.Base(), g_FileData.Count(), hFile );
  192. g_pFullFileSystem->Close( hFile );
  193. CUtlVector<char> *pCurData = &g_FileData;
  194. dataWriteLen = g_FileData.Count();
  195. if ( nReplacements )
  196. {
  197. // Null-terminate it.
  198. g_FileData.AddToTail( 0 );
  199. // Apply all the string substitutions.
  200. int iCurCount = g_FileData.Count() * 2;
  201. g_ReplacementData[0].EnsureCount( iCurCount );
  202. g_ReplacementData[1].EnsureCount( iCurCount );
  203. for ( int i=0; i < nReplacements/2; i++ )
  204. {
  205. for ( int iTestCount=0; iTestCount < 64; iTestCount++ )
  206. {
  207. if ( Q_StrSubst( pCurData->Base(), ppReplacements[i*2], ppReplacements[i*2+1], g_ReplacementData[i&1].Base(), g_ReplacementData[i&1].Count() ) )
  208. break;
  209. // Ok, we would overflow the string.. add more space to do the string substitution into.
  210. iCurCount += 2048;
  211. g_ReplacementData[0].EnsureCount( iCurCount );
  212. g_ReplacementData[1].EnsureCount( iCurCount );
  213. }
  214. pCurData = &g_ReplacementData[i&1];
  215. dataWriteLen = strlen( pCurData->Base() );
  216. }
  217. }
  218. return pCurData;
  219. }
  220. bool CopyWithReplacements(
  221. const char *pInputFilename,
  222. const char **ppReplacements, int nReplacements,
  223. const char *pOutputFilenameFormat, ... )
  224. {
  225. int dataWriteLen;
  226. CUtlVector<char> *pCurData = GetFileStringWithReplacements( pInputFilename, ppReplacements, nReplacements, dataWriteLen );
  227. if ( !pCurData )
  228. {
  229. char msg[512];
  230. Q_snprintf( msg, sizeof( msg ), "Can't open %s for reading.", pInputFilename );
  231. ::MessageBox( NULL, msg, "Error", MB_OK );
  232. return false;
  233. }
  234. // Get the output filename.
  235. char outFilename[MAX_PATH];
  236. va_list marker;
  237. va_start( marker, pOutputFilenameFormat );
  238. Q_vsnprintf( outFilename, sizeof( outFilename ), pOutputFilenameFormat, marker );
  239. va_end( marker );
  240. // Write it out. I'd like to use IFileSystem, but Steam lowercases all filenames, which screws case-sensitive linux
  241. // (since the linux makefiles are tuned to the casing in Perforce).
  242. FILE *hFile = fopen( outFilename, "wb" );
  243. if ( !hFile )
  244. {
  245. char msg[512];
  246. Q_snprintf( msg, sizeof( msg ), "Can't open %s for writing.", outFilename );
  247. ::MessageBox( NULL, msg, "Error", MB_OK );
  248. return false;
  249. }
  250. fwrite( pCurData->Base(), 1, dataWriteLen, hFile );
  251. fclose( hFile );
  252. return true;
  253. }
  254. int InitializeVGui()
  255. {
  256. vgui::ivgui()->SetSleep(false);
  257. // find our configuration directory
  258. char szConfigDir[512];
  259. const char *steamPath = getenv("SteamInstallPath");
  260. if (steamPath)
  261. {
  262. // put the config dir directly under steam
  263. Q_snprintf(szConfigDir, sizeof(szConfigDir), "%s/config", steamPath);
  264. }
  265. else
  266. {
  267. // we're not running steam, so just put the config dir under the platform
  268. Q_strncpy( szConfigDir, "platform/config", sizeof(szConfigDir));
  269. }
  270. g_pFullFileSystem->CreateDirHierarchy("config", "PLATFORM");
  271. g_pFullFileSystem->AddSearchPath(szConfigDir, "CONFIG", PATH_ADD_TO_HEAD);
  272. // initialize the user configuration file
  273. vgui::system()->SetUserConfigFile("DedicatedServerDialogConfig.vdf", "CONFIG");
  274. // Init the surface
  275. vgui::Panel *pPanel = new vgui::Panel(NULL, "TopPanel");
  276. pPanel->SetVisible(true);
  277. vgui::surface()->SetEmbeddedPanel(pPanel->GetVPanel());
  278. // load the scheme
  279. vgui::scheme()->LoadSchemeFromFile("Resource/sdklauncher_scheme.res", NULL);
  280. // localization
  281. g_pVGuiLocalize->AddFile( "resource/platform_english.txt" );
  282. g_pVGuiLocalize->AddFile( "vgui/resource/vgui_english.txt" );
  283. g_pVGuiLocalize->AddFile( "sdklauncher_english.txt" );
  284. // Start vgui
  285. vgui::ivgui()->Start();
  286. // add our main window
  287. g_pMainFrame = new CSDKLauncherDialog(pPanel, "SDKLauncherDialog");
  288. // show main window
  289. g_pMainFrame->MoveToCenterOfScreen();
  290. g_pMainFrame->Activate();
  291. return 0;
  292. }
  293. void ShutdownVGui()
  294. {
  295. delete g_pMainFrame;
  296. }
  297. KeyValues* LoadGameDirsFile()
  298. {
  299. char filename[MAX_PATH];
  300. Q_snprintf( filename, sizeof( filename ), "%ssdklauncher_gamedirs.txt", gamedir );
  301. KeyValues *dataFile = new KeyValues("gamedirs");
  302. dataFile->UsesEscapeSequences( true );
  303. dataFile->LoadFromFile( g_pFullFileSystem, filename, NULL );
  304. return dataFile;
  305. }
  306. bool SaveGameDirsFile( KeyValues *pFile )
  307. {
  308. char filename[MAX_PATH];
  309. Q_snprintf( filename, sizeof( filename ), "%ssdklauncher_gamedirs.txt", gamedir );
  310. return pFile->SaveToFile( g_pFullFileSystem, filename );
  311. }
  312. class CModalPreserveMessageBox : public vguiMessageBox
  313. {
  314. public:
  315. CModalPreserveMessageBox(const char *title, const char *text, vgui::Panel *parent)
  316. : vguiMessageBox( title, text, parent )
  317. {
  318. m_PrevAppFocusPanel = vgui::input()->GetAppModalSurface();
  319. }
  320. ~CModalPreserveMessageBox()
  321. {
  322. vgui::input()->SetAppModalSurface( m_PrevAppFocusPanel );
  323. }
  324. public:
  325. vgui::VPANEL m_PrevAppFocusPanel;
  326. };
  327. void VGUIMessageBox( vgui::Panel *pParent, const char *pTitle, const char *pMsg, ... )
  328. {
  329. char msg[4096];
  330. va_list marker;
  331. va_start( marker, pMsg );
  332. Q_vsnprintf( msg, sizeof( msg ), pMsg, marker );
  333. va_end( marker );
  334. vguiMessageBox *dlg = new CModalPreserveMessageBox( pTitle, msg, pParent );
  335. dlg->DoModal();
  336. dlg->RequestFocus();
  337. }
  338. //-----------------------------------------------------------------------------
  339. // Purpose: Startup our file watch
  340. //-----------------------------------------------------------------------------
  341. void UpdateConfigsStatus_Init( void )
  342. {
  343. // Watch our config file for changes
  344. if ( g_dwChangeHandle == NULL)
  345. {
  346. char szConfigDir[MAX_PATH];
  347. Q_strncpy( szConfigDir, GetSDKLauncherBinDirectory(), sizeof( szConfigDir ) );
  348. Q_strncat ( szConfigDir, "\\", MAX_PATH );
  349. Q_strncat ( szConfigDir, g_engineDir, MAX_PATH );
  350. Q_strncat ( szConfigDir, "\\bin", MAX_PATH );
  351. g_dwChangeHandle = FindFirstChangeNotification(
  352. szConfigDir, // directory to watch
  353. false, // watch the subtree
  354. (FILE_NOTIFY_CHANGE_DIR_NAME|FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_LAST_WRITE|FILE_NOTIFY_CHANGE_SIZE|FILE_NOTIFY_CHANGE_ATTRIBUTES)); // watch file and dir name changes
  355. if ( g_dwChangeHandle == INVALID_HANDLE_VALUE )
  356. {
  357. // FIXME: Unable to watch the file
  358. }
  359. }
  360. }
  361. //-----------------------------------------------------------------------------
  362. // Purpose: Update our status
  363. //-----------------------------------------------------------------------------
  364. void UpdateConfigsStatus( void )
  365. {
  366. // Wait for notification.
  367. DWORD dwWaitStatus = WaitForSingleObject( g_dwChangeHandle, 0 );
  368. if ( dwWaitStatus == WAIT_OBJECT_0 )
  369. {
  370. // Something in the watched folder changed!
  371. if ( g_pMainFrame != NULL )
  372. {
  373. g_pMainFrame->RefreshConfigs();
  374. }
  375. // Start the next update
  376. if ( FindNextChangeNotification( g_dwChangeHandle ) == FALSE )
  377. {
  378. // This means that something unknown happened to our search handle!
  379. Assert( 0 );
  380. return;
  381. }
  382. }
  383. }
  384. //-----------------------------------------------------------------------------
  385. // Purpose: Stop watching the file
  386. //-----------------------------------------------------------------------------
  387. void UpdateConfigsStatus_Shutdown( void )
  388. {
  389. FindCloseChangeNotification( g_dwChangeHandle );
  390. }
  391. void QuickLaunchCommandLine( char *pCommandLine )
  392. {
  393. STARTUPINFO si;
  394. memset( &si, 0, sizeof( si ) );
  395. si.cb = sizeof( si );
  396. PROCESS_INFORMATION pi;
  397. memset( &pi, 0, sizeof( pi ) );
  398. DWORD dwFlags = 0;
  399. if ( !CreateProcess(
  400. 0,
  401. pCommandLine,
  402. NULL, // security
  403. NULL,
  404. TRUE,
  405. dwFlags, // flags
  406. NULL, // environment
  407. GetSDKLauncherBaseDirectory(), // current directory
  408. &si,
  409. &pi ) )
  410. {
  411. ::MessageBoxA( NULL, GetLastWindowsErrorString(), "Error", MB_OK | MB_ICONINFORMATION | MB_APPLMODAL );
  412. }
  413. }
  414. bool RunQuickLaunch()
  415. {
  416. char cmdLine[512];
  417. if ( CommandLine()->FindParm( "-runhammer" ) )
  418. {
  419. Q_snprintf( cmdLine, sizeof( cmdLine ), "\"%s\\%s\\bin\\hammer.exe\"", GetSDKLauncherBinDirectory(), g_engineDir );
  420. QuickLaunchCommandLine( cmdLine );
  421. return true;
  422. }
  423. else if ( CommandLine()->FindParm( "-runmodelviewer" ) )
  424. {
  425. Q_snprintf( cmdLine, sizeof( cmdLine ), "\"%s\\%s\\bin\\hlmv.exe\"", GetSDKLauncherBinDirectory(), g_engineDir );
  426. QuickLaunchCommandLine( cmdLine );
  427. return true;
  428. }
  429. else if ( CommandLine()->FindParm( "-runfaceposer" ) )
  430. {
  431. Q_snprintf( cmdLine, sizeof( cmdLine ), "\"%s\\%s\\bin\\hlfaceposer.exe\"", GetSDKLauncherBinDirectory(), g_engineDir );
  432. QuickLaunchCommandLine( cmdLine );
  433. return true;
  434. }
  435. return false;
  436. }
  437. void CheckCreateModParameters()
  438. {
  439. if ( CommandLine()->FindParm( "-AutoHL2Mod" ) )
  440. g_bAutoHL2Mod = true;
  441. int iParm = CommandLine()->FindParm( "-CreateMod" );
  442. if ( iParm == 0 )
  443. return;
  444. if ( (iParm + 2) < CommandLine()->ParmCount() )
  445. {
  446. // Set it up so the mod wizard can skip the mod dir/mod name panel.
  447. g_bModWizard_CmdLineFields = true;
  448. Q_strncpy( g_ModWizard_CmdLine_ModDir, CommandLine()->GetParm( iParm + 1 ), sizeof( g_ModWizard_CmdLine_ModDir ) );
  449. Q_strncpy( g_ModWizard_CmdLine_ModName, CommandLine()->GetParm( iParm + 2 ), sizeof( g_ModWizard_CmdLine_ModName ) );
  450. RunCreateModWizard( true );
  451. }
  452. }
  453. //-----------------------------------------------------------------------------
  454. // The application object
  455. //-----------------------------------------------------------------------------
  456. class CSDKLauncherApp : public CVguiSteamApp
  457. {
  458. typedef CVguiSteamApp BaseClass;
  459. public:
  460. // Methods of IApplication
  461. virtual bool Create();
  462. virtual bool PreInit();
  463. virtual int Main();
  464. virtual void PostShutdown();
  465. virtual void Destroy() {}
  466. };
  467. DEFINE_WINDOWED_STEAM_APPLICATION_OBJECT( CSDKLauncherApp );
  468. //-----------------------------------------------------------------------------
  469. // The application object
  470. //-----------------------------------------------------------------------------
  471. bool CSDKLauncherApp::Create()
  472. {
  473. SpewOutputFunc( SDKLauncherSpewOutputFunc );
  474. AppSystemInfo_t appSystems[] =
  475. {
  476. { "inputsystem.dll", INPUTSYSTEM_INTERFACE_VERSION },
  477. { "vgui2.dll", VGUI_IVGUI_INTERFACE_VERSION },
  478. { "", "" } // Required to terminate the list
  479. };
  480. return AddSystems( appSystems );
  481. }
  482. //-----------------------------------------------------------------------------
  483. // Purpose: Entry point
  484. //-----------------------------------------------------------------------------
  485. bool CSDKLauncherApp::PreInit()
  486. {
  487. if ( !BaseClass::PreInit() )
  488. return false;
  489. // Make sure we're using the proper environment variable
  490. ConvertObsoleteVConfigRegistrySetting( GAMEDIR_TOKEN );
  491. if ( !CommandLine()->ParmValue( "-game" ) )
  492. {
  493. Error( "SDKLauncher requires -game on the command line." );
  494. return false;
  495. }
  496. // winsock aware
  497. WSAData wsaData;
  498. WSAStartup( MAKEWORD(2,0), &wsaData );
  499. // Create a window to capture messages
  500. CreateMessageWindow();
  501. FileSystem_SetErrorMode( FS_ERRORMODE_AUTO );
  502. if ( !BaseClass::SetupSearchPaths( NULL, false, true ) )
  503. {
  504. ::MessageBox( NULL, "Error", "Unable to initialize file system\n", MB_OK );
  505. return false;
  506. }
  507. // Set gamedir.
  508. Q_MakeAbsolutePath( gamedir, sizeof( gamedir ), GetGameInfoPath() );
  509. Q_AppendSlash( gamedir, sizeof( gamedir ) );
  510. // the "base dir" so we can scan mod name
  511. g_pFullFileSystem->AddSearchPath(GetSDKLauncherBaseDirectory(), SDKLAUNCHER_MAIN_PATH_ID);
  512. // the main platform dir
  513. g_pFullFileSystem->AddSearchPath("platform","PLATFORM", PATH_ADD_TO_HEAD);
  514. return true;
  515. }
  516. void CSDKLauncherApp::PostShutdown()
  517. {
  518. // Stop our message window
  519. ShutdownMessageWindow();
  520. ::WSACleanup();
  521. BaseClass::PostShutdown();
  522. }
  523. //-----------------------------------------------------------------------------
  524. // Purpose: Entry point
  525. //-----------------------------------------------------------------------------
  526. int CSDKLauncherApp::Main()
  527. {
  528. SetVConfigRegistrySetting( "sourcesdk", GetSDKLauncherBaseDirectory() );
  529. // If they just want to run Hammer or hlmv, just do that and exit.
  530. if ( RunQuickLaunch() )
  531. return 1;
  532. // Run app frame loop
  533. int ret = InitializeVGui();
  534. if ( ret != 0 )
  535. return ret;
  536. DumpMinFootprintFiles( false );
  537. SteamAPI_InitSafe();
  538. SteamAPI_SetTryCatchCallbacks( false ); // We don't use exceptions, so tell steam not to use try/catch in callback handlers
  539. g_SteamAPIContext.Init();
  540. // Start looking for file updates
  541. // UpdateConfigsStatus_Init();
  542. // Check if they want to run the Create Mod wizard right off the bat.
  543. CheckCreateModParameters();
  544. while ( vgui::ivgui()->IsRunning() && !g_bAppQuit )
  545. {
  546. Sleep( 10 );
  547. // UpdateConfigsStatus();
  548. vgui::ivgui()->RunFrame();
  549. }
  550. ShutdownVGui();
  551. // UpdateConfigsStatus_Shutdown();
  552. return 1;
  553. }