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.

1600 lines
43 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #if defined(_WIN32) && !defined(_X360)
  9. #include "winlite.h"
  10. #elif defined(OSX)
  11. #include <Carbon/Carbon.h>
  12. #include <sys/sysctl.h>
  13. #endif
  14. #if defined(LINUX)
  15. #include <unistd.h>
  16. #include <fcntl.h>
  17. #endif
  18. #if defined( USE_SDL )
  19. #include "SDL.h"
  20. #endif
  21. #include "quakedef.h"
  22. #include "igame.h"
  23. #include "errno.h"
  24. #include "host.h"
  25. #include "profiling.h"
  26. #include "server.h"
  27. #include "vengineserver_impl.h"
  28. #include "filesystem_engine.h"
  29. #include "sys.h"
  30. #include "sys_dll.h"
  31. #include "ivideomode.h"
  32. #include "host_cmd.h"
  33. #include "crtmemdebug.h"
  34. #include "sv_log.h"
  35. #include "sv_main.h"
  36. #include "traceinit.h"
  37. #include "dt_test.h"
  38. #include "keys.h"
  39. #include "gl_matsysiface.h"
  40. #include "tier0/vcrmode.h"
  41. #include "tier0/icommandline.h"
  42. #include "cmd.h"
  43. #include <ihltvdirector.h>
  44. #if defined( REPLAY_ENABLED )
  45. #include "replay/ireplaysystem.h"
  46. #endif
  47. #include "MapReslistGenerator.h"
  48. #include "DevShotGenerator.h"
  49. #include "cdll_engine_int.h"
  50. #include "dt_send.h"
  51. #include "idedicatedexports.h"
  52. #include "eifacev21.h"
  53. #include "cl_steamauth.h"
  54. #include "tier0/etwprof.h"
  55. #include "vgui_baseui_interface.h"
  56. #include "tier0/systeminformation.h"
  57. #ifdef _WIN32
  58. #if !defined( _X360 )
  59. #include <io.h>
  60. #endif
  61. #endif
  62. #include "toolframework/itoolframework.h"
  63. #if defined( _X360 )
  64. #include "xbox/xbox_win32stubs.h"
  65. #endif
  66. // memdbgon must be the last include file in a .cpp file!!!
  67. #include "tier0/memdbgon.h"
  68. #define ONE_HUNDRED_TWENTY_EIGHT_MB (128 * 1024 * 1024)
  69. ConVar mem_min_heapsize( "mem_min_heapsize", "48", FCVAR_INTERNAL_USE, "Minimum amount of memory to dedicate to engine hunk and datacache (in mb)" );
  70. ConVar mem_max_heapsize( "mem_max_heapsize", "256", FCVAR_INTERNAL_USE, "Maximum amount of memory to dedicate to engine hunk and datacache (in mb)" );
  71. ConVar mem_max_heapsize_dedicated( "mem_max_heapsize_dedicated", "64", FCVAR_INTERNAL_USE, "Maximum amount of memory to dedicate to engine hunk and datacache, for dedicated server (in mb)" );
  72. #define MINIMUM_WIN_MEMORY (unsigned)(mem_min_heapsize.GetInt()*1024*1024)
  73. #define MAXIMUM_WIN_MEMORY max( (unsigned)(mem_max_heapsize.GetInt()*1024*1024), MINIMUM_WIN_MEMORY )
  74. #define MAXIMUM_DEDICATED_MEMORY (unsigned)(mem_max_heapsize_dedicated.GetInt()*1024*1024)
  75. char *CheckParm(const char *psz, char **ppszValue = NULL);
  76. void SeedRandomNumberGenerator( bool random_invariant );
  77. void Con_ColorPrintf( const Color& clr, PRINTF_FORMAT_STRING const char *fmt, ... ) FMTFUNCTION( 2, 3 );
  78. void COM_ShutdownFileSystem( void );
  79. void COM_InitFilesystem( const char *pFullModPath );
  80. modinfo_t gmodinfo;
  81. #ifdef PLATFORM_WINDOWS
  82. HWND *pmainwindow = NULL;
  83. #endif
  84. char gszDisconnectReason[256];
  85. char gszExtendedDisconnectReason[256];
  86. bool gfExtendedError = false;
  87. uint8 g_eSteamLoginFailure = 0;
  88. bool g_bV3SteamInterface = false;
  89. CreateInterfaceFn g_AppSystemFactory = NULL;
  90. static bool s_bIsDedicated = false;
  91. ConVar *sv_noclipduringpause = NULL;
  92. // Special mode where the client uses a console window and has no graphics. Useful for stress-testing a server
  93. // without having to round up 32 people.
  94. bool g_bTextMode = false;
  95. // Set to true when we exit from an error.
  96. bool g_bInErrorExit = false;
  97. static FileFindHandle_t g_hfind = FILESYSTEM_INVALID_FIND_HANDLE;
  98. // The extension DLL directory--one entry per loaded DLL
  99. CSysModule *g_GameDLL = NULL;
  100. // Prototype of an global method function
  101. typedef void (DLLEXPORT * PFN_GlobalMethod)( edict_t *pEntity );
  102. IServerGameDLL *serverGameDLL = NULL;
  103. int g_iServerGameDLLVersion = 0;
  104. IServerGameEnts *serverGameEnts = NULL;
  105. IServerGameClients *serverGameClients = NULL;
  106. int g_iServerGameClientsVersion = 0; // This matches the number at the end of the interface name (so for "ServerGameClients004", this would be 4).
  107. IHLTVDirector *serverGameDirector = NULL;
  108. IServerGameTags *serverGameTags = NULL;
  109. void Sys_InitArgv( char *lpCmdLine );
  110. void Sys_ShutdownArgv( void );
  111. //-----------------------------------------------------------------------------
  112. // Purpose: Compare file times
  113. // Input : ft1 -
  114. // ft2 -
  115. // Output : int
  116. //-----------------------------------------------------------------------------
  117. int Sys_CompareFileTime( long ft1, long ft2 )
  118. {
  119. if ( ft1 < ft2 )
  120. {
  121. return -1;
  122. }
  123. else if ( ft1 > ft2 )
  124. {
  125. return 1;
  126. }
  127. return 0;
  128. }
  129. //-----------------------------------------------------------------------------
  130. // Is slash?
  131. //-----------------------------------------------------------------------------
  132. inline bool IsSlash( char c )
  133. {
  134. return ( c == '\\') || ( c == '/' );
  135. }
  136. //-----------------------------------------------------------------------------
  137. // Purpose: Create specified directory
  138. // Input : *path -
  139. // Output : void Sys_mkdir
  140. //-----------------------------------------------------------------------------
  141. void Sys_mkdir( const char *path )
  142. {
  143. char testpath[ MAX_OSPATH ];
  144. // Remove any terminal backslash or /
  145. Q_strncpy( testpath, path, sizeof( testpath ) );
  146. int nLen = Q_strlen( testpath );
  147. if ( (nLen > 0) && IsSlash( testpath[ nLen - 1 ] ) )
  148. {
  149. testpath[ nLen - 1 ] = 0;
  150. }
  151. // Look for URL
  152. const char *pPathID = "MOD";
  153. if ( IsSlash( testpath[0] ) && IsSlash( testpath[1] ) )
  154. {
  155. pPathID = NULL;
  156. }
  157. if ( g_pFileSystem->FileExists( testpath, pPathID ) )
  158. {
  159. // if there is a file of the same name as the directory we want to make, just kill it
  160. if ( !g_pFileSystem->IsDirectory( testpath, pPathID ) )
  161. {
  162. g_pFileSystem->RemoveFile( testpath, pPathID );
  163. }
  164. }
  165. g_pFileSystem->CreateDirHierarchy( path, pPathID );
  166. }
  167. //-----------------------------------------------------------------------------
  168. // Purpose:
  169. // Input : *path -
  170. // *basename -
  171. // Output : char *Sys_FindFirst
  172. //-----------------------------------------------------------------------------
  173. const char *Sys_FindFirst(const char *path, char *basename, int namelength )
  174. {
  175. if (g_hfind != FILESYSTEM_INVALID_FIND_HANDLE)
  176. {
  177. Sys_Error ("Sys_FindFirst without close");
  178. g_pFileSystem->FindClose(g_hfind);
  179. }
  180. const char* psz = g_pFileSystem->FindFirst(path, &g_hfind);
  181. if (basename && psz)
  182. {
  183. Q_FileBase(psz, basename, namelength );
  184. }
  185. return psz;
  186. }
  187. //-----------------------------------------------------------------------------
  188. // Purpose: Sys_FindFirst with a path ID filter.
  189. //-----------------------------------------------------------------------------
  190. const char *Sys_FindFirstEx( const char *pWildcard, const char *pPathID, char *basename, int namelength )
  191. {
  192. if (g_hfind != FILESYSTEM_INVALID_FIND_HANDLE)
  193. {
  194. Sys_Error ("Sys_FindFirst without close");
  195. g_pFileSystem->FindClose(g_hfind);
  196. }
  197. const char* psz = g_pFileSystem->FindFirstEx( pWildcard, pPathID, &g_hfind);
  198. if (basename && psz)
  199. {
  200. Q_FileBase(psz, basename, namelength );
  201. }
  202. return psz;
  203. }
  204. //-----------------------------------------------------------------------------
  205. // Purpose:
  206. // Input : *basename -
  207. // Output : char *Sys_FindNext
  208. //-----------------------------------------------------------------------------
  209. const char* Sys_FindNext(char *basename, int namelength)
  210. {
  211. const char *psz = g_pFileSystem->FindNext(g_hfind);
  212. if ( basename && psz )
  213. {
  214. Q_FileBase(psz, basename, namelength );
  215. }
  216. return psz;
  217. }
  218. //-----------------------------------------------------------------------------
  219. // Purpose:
  220. // Output : void Sys_FindClose
  221. //-----------------------------------------------------------------------------
  222. void Sys_FindClose(void)
  223. {
  224. if ( FILESYSTEM_INVALID_FIND_HANDLE != g_hfind )
  225. {
  226. g_pFileSystem->FindClose(g_hfind);
  227. g_hfind = FILESYSTEM_INVALID_FIND_HANDLE;
  228. }
  229. }
  230. //-----------------------------------------------------------------------------
  231. // Purpose: OS Specific initializations
  232. //-----------------------------------------------------------------------------
  233. void Sys_Init( void )
  234. {
  235. // Set default FPU control word to truncate (chop) mode for optimized _ftol()
  236. // This does not "stick", the mode is restored somewhere down the line.
  237. // Sys_TruncateFPU();
  238. }
  239. //-----------------------------------------------------------------------------
  240. // Purpose:
  241. //-----------------------------------------------------------------------------
  242. void Sys_Shutdown( void )
  243. {
  244. }
  245. //-----------------------------------------------------------------------------
  246. // Purpose: Print to system console
  247. // Input : *fmt -
  248. // ... -
  249. // Output : void Sys_Printf
  250. //-----------------------------------------------------------------------------
  251. void Sys_Printf(char *fmt, ...)
  252. {
  253. va_list argptr;
  254. char text[1024];
  255. va_start (argptr,fmt);
  256. Q_vsnprintf (text, sizeof( text ), fmt, argptr);
  257. va_end (argptr);
  258. if ( developer.GetInt() )
  259. {
  260. #ifdef _WIN32
  261. wchar_t unicode[2048];
  262. ::MultiByteToWideChar(CP_UTF8, 0, text, -1, unicode, sizeof( unicode ) / sizeof(wchar_t));
  263. unicode[(sizeof( unicode ) / sizeof(wchar_t)) - 1] = L'\0';
  264. OutputDebugStringW( unicode );
  265. Sleep( 0 );
  266. #else
  267. fprintf( stderr, "%s", text );
  268. #endif
  269. }
  270. if ( s_bIsDedicated )
  271. {
  272. printf( "%s", text );
  273. }
  274. }
  275. bool Sys_MessageBox(const char *title, const char *info, bool bShowOkAndCancel)
  276. {
  277. #ifdef _WIN32
  278. if ( IDOK == ::MessageBox( NULL, title, info, MB_ICONEXCLAMATION | ( bShowOkAndCancel ? MB_OKCANCEL : MB_OK ) ) )
  279. {
  280. return true;
  281. }
  282. return false;
  283. #elif defined( USE_SDL )
  284. int buttonid = 0;
  285. SDL_MessageBoxData messageboxdata = { 0 };
  286. SDL_MessageBoxButtonData buttondata[] =
  287. {
  288. { SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, 1, "OK" },
  289. { SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, 0, "Cancel" },
  290. };
  291. messageboxdata.window = GetAssertDialogParent();
  292. messageboxdata.title = title;
  293. messageboxdata.message = info;
  294. messageboxdata.numbuttons = bShowOkAndCancel ? 2 : 1;
  295. messageboxdata.buttons = buttondata;
  296. SDL_ShowMessageBox( &messageboxdata, &buttonid );
  297. return ( buttonid == 1 );
  298. #elif defined( POSIX )
  299. Warning( "%s\n", info );
  300. return true;
  301. #else
  302. #error "implement me"
  303. #endif
  304. }
  305. bool g_bUpdateMinidumpComment = true;
  306. void BuildMinidumpComment( char const *pchSysErrorText, bool bRealCrash );
  307. void Sys_Error_Internal( bool bMinidump, const char *error, va_list argsList )
  308. {
  309. char text[1024];
  310. static bool bReentry = false; // Don't meltdown
  311. Q_vsnprintf( text, sizeof( text ), error, argsList );
  312. if ( bReentry )
  313. {
  314. fprintf( stderr, "%s\n", text );
  315. return;
  316. }
  317. bReentry = true;
  318. if ( s_bIsDedicated )
  319. {
  320. printf( "%s\n", text );
  321. }
  322. else
  323. {
  324. Sys_Printf( "%s\n", text );
  325. }
  326. // Write the error to the log and ensure the log contents get written to disk
  327. g_Log.Printf( "Engine error: %s\n", text );
  328. g_Log.Flush();
  329. g_bInErrorExit = true;
  330. #if !defined( SWDS )
  331. if ( IsPC() && videomode )
  332. videomode->Shutdown();
  333. #endif
  334. if ( IsPC() &&
  335. !CommandLine()->FindParm( "-makereslists" ) &&
  336. !CommandLine()->FindParm( "-nomessagebox" ) &&
  337. !CommandLine()->FindParm( "-nocrashdialog" ) )
  338. {
  339. #ifdef _WIN32
  340. ::MessageBox( NULL, text, "Engine Error", MB_OK | MB_TOPMOST );
  341. #elif defined( USE_SDL )
  342. Sys_MessageBox( "Engine Error", text, false );
  343. #endif
  344. }
  345. if ( IsPC() )
  346. {
  347. DebuggerBreakIfDebugging();
  348. }
  349. else if ( !IsRetail() )
  350. {
  351. DebuggerBreak();
  352. }
  353. #if !defined( _X360 )
  354. BuildMinidumpComment( text, true );
  355. g_bUpdateMinidumpComment = false;
  356. if ( bMinidump && !Plat_IsInDebugSession() && !CommandLine()->FindParm( "-nominidumps") )
  357. {
  358. #if defined( WIN32 )
  359. // MiniDumpWrite() has problems capturing the calling thread's context
  360. // unless it is called with an exception context. So fake an exception.
  361. __try
  362. {
  363. RaiseException
  364. (
  365. 0, // dwExceptionCode
  366. EXCEPTION_NONCONTINUABLE, // dwExceptionFlags
  367. 0, // nNumberOfArguments,
  368. NULL // const ULONG_PTR* lpArguments
  369. );
  370. // Never get here (non-continuable exception)
  371. }
  372. // Write the minidump from inside the filter (GetExceptionInformation() is only
  373. // valid in the filter)
  374. __except ( SteamAPI_WriteMiniDump( 0, GetExceptionInformation(), build_number() ), EXCEPTION_EXECUTE_HANDLER )
  375. {
  376. // We always get here because the above filter evaluates to EXCEPTION_EXECUTE_HANDLER
  377. }
  378. #elif defined( OSX )
  379. // Doing this doesn't quite work the way we want because there is no "crashing" thread
  380. // and we see "No thread was identified as the cause of the crash; No signature could be created because we do not know which thread crashed" on the back end
  381. //SteamAPI_WriteMiniDump( 0, NULL, build_number() );
  382. printf("\n ##### Sys_Error: %s", text );
  383. fflush(stdout );
  384. int *p = 0;
  385. *p = 0xdeadbeef;
  386. #elif defined( LINUX )
  387. // Doing this doesn't quite work the way we want because there is no "crashing" thread
  388. // and we see "No thread was identified as the cause of the crash; No signature could be created because we do not know which thread crashed" on the back end
  389. //SteamAPI_WriteMiniDump( 0, NULL, build_number() );
  390. int *p = 0;
  391. *p = 0xdeadbeef;
  392. #else
  393. #warning "need minidump impl on sys_error"
  394. #endif
  395. }
  396. #endif // _X360
  397. host_initialized = false;
  398. #if defined(_WIN32) && !defined( _X360 )
  399. // We don't want global destructors in our process OR in any DLL to get executed.
  400. // _exit() avoids calling global destructors in our module, but not in other DLLs.
  401. TerminateProcess( GetCurrentProcess(), 100 );
  402. #else
  403. _exit( 100 );
  404. #endif
  405. }
  406. //-----------------------------------------------------------------------------
  407. // Purpose: Exit engine with error
  408. // Input : *error -
  409. // ... -
  410. // Output : void Sys_Error
  411. //-----------------------------------------------------------------------------
  412. void Sys_Error(const char *error, ...)
  413. {
  414. va_list argptr;
  415. va_start( argptr, error );
  416. Sys_Error_Internal( true, error, argptr );
  417. va_end( argptr );
  418. }
  419. //-----------------------------------------------------------------------------
  420. // Purpose: Exit engine with error
  421. // Input : *error -
  422. // ... -
  423. // Output : void Sys_Error
  424. //-----------------------------------------------------------------------------
  425. void Sys_Exit(const char *error, ...)
  426. {
  427. va_list argptr;
  428. va_start( argptr, error );
  429. Sys_Error_Internal( false, error, argptr );
  430. va_end( argptr );
  431. }
  432. bool IsInErrorExit()
  433. {
  434. return g_bInErrorExit;
  435. }
  436. //-----------------------------------------------------------------------------
  437. // Purpose:
  438. // Input : msec -
  439. // Output : void Sys_Sleep
  440. //-----------------------------------------------------------------------------
  441. void Sys_Sleep( int msec )
  442. {
  443. #ifdef _WIN32
  444. Sleep ( msec );
  445. #elif POSIX
  446. usleep( msec * 1000 );
  447. #endif
  448. }
  449. //-----------------------------------------------------------------------------
  450. // Purpose:
  451. // Input : hInst -
  452. // ulInit -
  453. // lpReserved -
  454. // Output : BOOL WINAPI DllMain
  455. //-----------------------------------------------------------------------------
  456. #if defined(_WIN32) && !defined( _X360 )
  457. BOOL WINAPI DllMain(HANDLE hInst, ULONG ulInit, LPVOID lpReserved)
  458. {
  459. InitCRTMemDebug();
  460. if (ulInit == DLL_PROCESS_ATTACH)
  461. {
  462. }
  463. else if (ulInit == DLL_PROCESS_DETACH)
  464. {
  465. }
  466. return TRUE;
  467. }
  468. #endif
  469. //-----------------------------------------------------------------------------
  470. // Purpose: Allocate memory for engine hunk
  471. // Input : *parms -
  472. //-----------------------------------------------------------------------------
  473. void Sys_InitMemory( void )
  474. {
  475. // Allow overrides
  476. if ( CommandLine()->FindParm( "-minmemory" ) )
  477. {
  478. host_parms.memsize = MINIMUM_WIN_MEMORY;
  479. return;
  480. }
  481. host_parms.memsize = 0;
  482. #ifdef _WIN32
  483. #if (_MSC_VER > 1200)
  484. // MSVC 6.0 doesn't support GlobalMemoryStatusEx()
  485. if ( IsPC() )
  486. {
  487. OSVERSIONINFOEX osvi;
  488. ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
  489. osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
  490. if ( GetVersionEx ((OSVERSIONINFO *)&osvi) )
  491. {
  492. if ( osvi.dwPlatformId >= VER_PLATFORM_WIN32_NT && osvi.dwMajorVersion >= 5 )
  493. {
  494. MEMORYSTATUSEX memStat;
  495. ZeroMemory(&memStat, sizeof(MEMORYSTATUSEX));
  496. memStat.dwLength = sizeof(MEMORYSTATUSEX);
  497. if ( GlobalMemoryStatusEx( &memStat ) )
  498. {
  499. if ( memStat.ullTotalPhys > 0xFFFFFFFFUL )
  500. {
  501. host_parms.memsize = 0xFFFFFFFFUL;
  502. }
  503. else
  504. {
  505. host_parms.memsize = memStat.ullTotalPhys;
  506. }
  507. }
  508. }
  509. }
  510. }
  511. #endif // (_MSC_VER > 1200)
  512. if ( !IsX360() )
  513. {
  514. if ( host_parms.memsize == 0 )
  515. {
  516. MEMORYSTATUS lpBuffer;
  517. // Get OS Memory status
  518. lpBuffer.dwLength = sizeof(MEMORYSTATUS);
  519. GlobalMemoryStatus( &lpBuffer );
  520. if ( lpBuffer.dwTotalPhys <= 0 )
  521. {
  522. host_parms.memsize = MAXIMUM_WIN_MEMORY;
  523. }
  524. else
  525. {
  526. host_parms.memsize = lpBuffer.dwTotalPhys;
  527. }
  528. }
  529. if ( host_parms.memsize < ONE_HUNDRED_TWENTY_EIGHT_MB )
  530. {
  531. Sys_Error( "Available memory less than 128MB!!! %i\n", host_parms.memsize );
  532. }
  533. // take one quarter the physical memory
  534. if ( host_parms.memsize <= 512*1024*1024)
  535. {
  536. host_parms.memsize >>= 2;
  537. // Apply cap of 64MB for 512MB systems
  538. // this keeps the code the same as HL2 gold
  539. // but allows us to use more memory on 1GB+ systems
  540. if (host_parms.memsize > MAXIMUM_DEDICATED_MEMORY)
  541. {
  542. host_parms.memsize = MAXIMUM_DEDICATED_MEMORY;
  543. }
  544. }
  545. else
  546. {
  547. // just take one quarter, no cap
  548. host_parms.memsize >>= 2;
  549. }
  550. // At least MINIMUM_WIN_MEMORY mb, even if we have to swap a lot.
  551. if (host_parms.memsize < MINIMUM_WIN_MEMORY)
  552. {
  553. host_parms.memsize = MINIMUM_WIN_MEMORY;
  554. }
  555. // Apply cap
  556. if (host_parms.memsize > MAXIMUM_WIN_MEMORY)
  557. {
  558. host_parms.memsize = MAXIMUM_WIN_MEMORY;
  559. }
  560. }
  561. else
  562. {
  563. host_parms.memsize = 128*1024*1024;
  564. }
  565. #elif defined(POSIX)
  566. uint64_t memsize = ONE_HUNDRED_TWENTY_EIGHT_MB;
  567. #if defined(OSX)
  568. int mib[2] = { CTL_HW, HW_MEMSIZE };
  569. u_int namelen = sizeof(mib) / sizeof(mib[0]);
  570. size_t len = sizeof(memsize);
  571. if (sysctl(mib, namelen, &memsize, &len, NULL, 0) < 0)
  572. {
  573. memsize = ONE_HUNDRED_TWENTY_EIGHT_MB;
  574. }
  575. #elif defined(LINUX)
  576. const int fd = open("/proc/meminfo", O_RDONLY);
  577. if (fd < 0)
  578. {
  579. Sys_Error( "Can't open /proc/meminfo (%s)!\n", strerror(errno) );
  580. }
  581. char buf[1024 * 16];
  582. const ssize_t br = read(fd, buf, sizeof (buf));
  583. close(fd);
  584. if (br < 0)
  585. {
  586. Sys_Error( "Can't read /proc/meminfo (%s)!\n", strerror(errno) );
  587. }
  588. buf[br] = '\0';
  589. // Split up the buffer by lines...
  590. char *line = buf;
  591. for (char *ptr = buf; *ptr; ptr++)
  592. {
  593. if (*ptr == '\n')
  594. {
  595. // we've got a complete line.
  596. *ptr = '\0';
  597. unsigned long long ull = 0;
  598. if (sscanf(line, "MemTotal: %llu kB", &ull) == 1)
  599. {
  600. // found it!
  601. memsize = ((uint64_t) ull) * 1024;
  602. break;
  603. }
  604. line = ptr;
  605. }
  606. }
  607. #else
  608. #error Write me.
  609. #endif
  610. if ( memsize > 0xFFFFFFFFUL )
  611. {
  612. host_parms.memsize = 0xFFFFFFFFUL;
  613. }
  614. else
  615. {
  616. host_parms.memsize = memsize;
  617. }
  618. if ( host_parms.memsize < ONE_HUNDRED_TWENTY_EIGHT_MB )
  619. {
  620. Sys_Error( "Available memory less than 128MB!!! %i\n", host_parms.memsize );
  621. }
  622. // take one quarter the physical memory
  623. if ( host_parms.memsize <= 512*1024*1024)
  624. {
  625. host_parms.memsize >>= 2;
  626. // Apply cap of 64MB for 512MB systems
  627. // this keeps the code the same as HL2 gold
  628. // but allows us to use more memory on 1GB+ systems
  629. if (host_parms.memsize > MAXIMUM_DEDICATED_MEMORY)
  630. {
  631. host_parms.memsize = MAXIMUM_DEDICATED_MEMORY;
  632. }
  633. }
  634. else
  635. {
  636. // just take one quarter, no cap
  637. host_parms.memsize >>= 2;
  638. }
  639. // At least MINIMUM_WIN_MEMORY mb, even if we have to swap a lot.
  640. if (host_parms.memsize < MINIMUM_WIN_MEMORY)
  641. {
  642. host_parms.memsize = MINIMUM_WIN_MEMORY;
  643. }
  644. // Apply cap
  645. if (host_parms.memsize > MAXIMUM_WIN_MEMORY)
  646. {
  647. host_parms.memsize = MAXIMUM_WIN_MEMORY;
  648. }
  649. #else
  650. #error Write me.
  651. #endif
  652. }
  653. //-----------------------------------------------------------------------------
  654. // Purpose:
  655. // Input : *parms -
  656. // Output : Returns true on success, false on failure.
  657. //-----------------------------------------------------------------------------
  658. void Sys_ShutdownMemory( void )
  659. {
  660. host_parms.memsize = 0;
  661. }
  662. //-----------------------------------------------------------------------------
  663. // Purpose:
  664. //-----------------------------------------------------------------------------
  665. void Sys_InitAuthentication( void )
  666. {
  667. }
  668. //-----------------------------------------------------------------------------
  669. // Purpose:
  670. //-----------------------------------------------------------------------------
  671. void Sys_ShutdownAuthentication( void )
  672. {
  673. }
  674. //-----------------------------------------------------------------------------
  675. // Debug library spew output
  676. //-----------------------------------------------------------------------------
  677. CThreadLocalInt<> g_bInSpew;
  678. #include "tier1/fmtstr.h"
  679. static ConVar sys_minidumpspewlines( "sys_minidumpspewlines", "500", 0, "Lines of crash dump console spew to keep." );
  680. static CUtlLinkedList< CUtlString > g_SpewHistory;
  681. static int g_nSpewLines = 1;
  682. static CThreadFastMutex g_SpewMutex;
  683. static void AddSpewRecord( char const *pMsg )
  684. {
  685. #if !defined( _X360 )
  686. AUTO_LOCK( g_SpewMutex );
  687. static bool s_bReentrancyGuard = false;
  688. if ( s_bReentrancyGuard )
  689. return;
  690. s_bReentrancyGuard = true;
  691. if ( g_SpewHistory.Count() > sys_minidumpspewlines.GetInt() )
  692. {
  693. g_SpewHistory.Remove( g_SpewHistory.Head() );
  694. }
  695. int i = g_SpewHistory.AddToTail();
  696. g_SpewHistory[ i ].Format( "%d(%f): %s", g_nSpewLines++, Plat_FloatTime(), pMsg );
  697. s_bReentrancyGuard = false;
  698. #endif
  699. }
  700. void GetSpew( char *buf, size_t buflen )
  701. {
  702. AUTO_LOCK( g_SpewMutex );
  703. // Walk list backward
  704. char *pcur = buf;
  705. int remainder = (int)buflen - 1;
  706. // Walk backward(
  707. for ( int i = g_SpewHistory.Tail(); i != g_SpewHistory.InvalidIndex(); i = g_SpewHistory.Previous( i ) )
  708. {
  709. const CUtlString &rec = g_SpewHistory[ i ];
  710. int len = rec.Length();
  711. int tocopy = MIN( len, remainder );
  712. if ( tocopy <= 0 )
  713. break;
  714. Q_memcpy( pcur, rec.String(), tocopy );
  715. remainder -= tocopy;
  716. pcur += tocopy;
  717. if ( remainder <= 0 )
  718. break;
  719. }
  720. *pcur = 0;
  721. }
  722. ConVar spew_consolelog_to_debugstring( "spew_consolelog_to_debugstring", "0", 0, "Send console log to PLAT_DebugString()" );
  723. SpewRetval_t Sys_SpewFunc( SpewType_t spewType, const char *pMsg )
  724. {
  725. bool suppress = g_bInSpew;
  726. g_bInSpew = true;
  727. AddSpewRecord( pMsg );
  728. // Text output shows up on dedicated server profiles, both as consuming CPU
  729. // time and causing IPC delays. Sending the messages to ETW will help us
  730. // understand why, and save us time when server operators are triggering
  731. // excessive spew. Having the output in traces is also generically useful
  732. // for understanding slowdowns.
  733. ETWMark1I( pMsg, spewType );
  734. if ( !suppress )
  735. {
  736. // If this is a dedicated server, then we have taken over its spew function, but we still
  737. // want its vgui console to show the spew, so pass it into the dedicated server.
  738. if ( dedicated )
  739. dedicated->Sys_Printf( (char*)pMsg );
  740. if( spew_consolelog_to_debugstring.GetBool() )
  741. {
  742. Plat_DebugString( pMsg );
  743. }
  744. if ( g_bTextMode )
  745. {
  746. printf( "%s", pMsg );
  747. }
  748. if ((spewType != SPEW_LOG) || (sv.GetMaxClients() == 1))
  749. {
  750. Color color;
  751. switch ( spewType )
  752. {
  753. #ifndef SWDS
  754. case SPEW_WARNING:
  755. {
  756. color.SetColor( 255, 90, 90, 255 );
  757. }
  758. break;
  759. case SPEW_ASSERT:
  760. {
  761. color.SetColor( 255, 20, 20, 255 );
  762. }
  763. break;
  764. case SPEW_ERROR:
  765. {
  766. color.SetColor( 20, 70, 255, 255 );
  767. }
  768. break;
  769. #endif
  770. default:
  771. {
  772. color = *GetSpewOutputColor();
  773. }
  774. break;
  775. }
  776. Con_ColorPrintf( color, "%s", pMsg );
  777. }
  778. else
  779. {
  780. g_Log.Printf( "%s", pMsg );
  781. }
  782. }
  783. g_bInSpew = false;
  784. if (spewType == SPEW_ERROR)
  785. {
  786. Sys_Error( "%s", pMsg );
  787. return SPEW_ABORT;
  788. }
  789. if (spewType == SPEW_ASSERT)
  790. {
  791. if ( CommandLine()->FindParm( "-noassert" ) == 0 )
  792. return SPEW_DEBUGGER;
  793. else
  794. return SPEW_CONTINUE;
  795. }
  796. return SPEW_CONTINUE;
  797. }
  798. void DeveloperChangeCallback( IConVar *pConVar, const char *pOldString, float flOldValue )
  799. {
  800. // Set the "developer" spew group to the value...
  801. ConVarRef var( pConVar );
  802. int val = var.GetInt();
  803. SpewActivate( "developer", val );
  804. // Activate console spew (spew value 2 == developer console spew)
  805. SpewActivate( "console", val ? 2 : 1 );
  806. }
  807. //-----------------------------------------------------------------------------
  808. // Purpose: factory comglomerator, gets the client, server, and gameui dlls together
  809. //-----------------------------------------------------------------------------
  810. void *GameFactory( const char *pName, int *pReturnCode )
  811. {
  812. void *pRetVal = NULL;
  813. // first ask the app factory
  814. pRetVal = g_AppSystemFactory( pName, pReturnCode );
  815. if (pRetVal)
  816. return pRetVal;
  817. #ifndef SWDS
  818. // now ask the client dll
  819. if (ClientDLL_GetFactory())
  820. {
  821. pRetVal = ClientDLL_GetFactory()( pName, pReturnCode );
  822. if (pRetVal)
  823. return pRetVal;
  824. }
  825. // gameui.dll
  826. if (EngineVGui()->GetGameUIFactory())
  827. {
  828. pRetVal = EngineVGui()->GetGameUIFactory()( pName, pReturnCode );
  829. if (pRetVal)
  830. return pRetVal;
  831. }
  832. #endif
  833. // server dll factory access would go here when needed
  834. return NULL;
  835. }
  836. // factory instance
  837. CreateInterfaceFn g_GameSystemFactory = GameFactory;
  838. //-----------------------------------------------------------------------------
  839. // Purpose:
  840. // Input : *lpOrgCmdLine -
  841. // launcherFactory -
  842. // *pwnd -
  843. // bIsDedicated -
  844. // Output : int
  845. //-----------------------------------------------------------------------------
  846. int Sys_InitGame( CreateInterfaceFn appSystemFactory, const char* pBaseDir, void *pwnd, int bIsDedicated )
  847. {
  848. #ifdef BENCHMARK
  849. if ( bIsDedicated )
  850. {
  851. Error( "Dedicated server isn't supported by this benchmark!" );
  852. }
  853. #endif
  854. extern void InitMathlib( void );
  855. InitMathlib();
  856. FileSystem_SetWhitelistSpewFlags();
  857. // Activate console spew
  858. // Must happen before developer.InstallChangeCallback because that callback may reset it
  859. SpewActivate( "console", 1 );
  860. // Install debug spew output....
  861. developer.InstallChangeCallback( DeveloperChangeCallback );
  862. SpewOutputFunc( Sys_SpewFunc );
  863. // Assume failure
  864. host_initialized = false;
  865. #ifdef PLATFORM_WINDOWS
  866. // Grab main window pointer
  867. pmainwindow = (HWND *)pwnd;
  868. #endif
  869. // Remember that this is a dedicated server
  870. s_bIsDedicated = bIsDedicated ? true : false;
  871. memset( &gmodinfo, 0, sizeof( modinfo_t ) );
  872. static char s_pBaseDir[256];
  873. Q_strncpy( s_pBaseDir, pBaseDir, sizeof( s_pBaseDir ) );
  874. Q_strlower( s_pBaseDir );
  875. Q_FixSlashes( s_pBaseDir );
  876. host_parms.basedir = s_pBaseDir;
  877. #ifndef _X360
  878. if ( CommandLine()->FindParm ( "-pidfile" ) )
  879. {
  880. FileHandle_t pidFile = g_pFileSystem->Open( CommandLine()->ParmValue ( "-pidfile", "srcds.pid" ), "w+" );
  881. if ( pidFile )
  882. {
  883. g_pFileSystem->FPrintf( pidFile, "%i\n", getpid() );
  884. g_pFileSystem->Close(pidFile);
  885. }
  886. else
  887. {
  888. Warning("Unable to open pidfile (%s)\n", CommandLine()->CheckParm ( "-pidfile" ));
  889. }
  890. }
  891. #endif
  892. // Initialize clock
  893. TRACEINIT( Sys_Init(), Sys_Shutdown() );
  894. #if defined(_DEBUG)
  895. if ( IsPC() )
  896. {
  897. if( !CommandLine()->FindParm( "-nodttest" ) && !CommandLine()->FindParm( "-dti" ) )
  898. {
  899. RunDataTableTest();
  900. }
  901. }
  902. #endif
  903. // NOTE: Can't use COM_CheckParm here because it hasn't been set up yet.
  904. SeedRandomNumberGenerator( CommandLine()->FindParm( "-random_invariant" ) != 0 );
  905. TRACEINIT( Sys_InitMemory(), Sys_ShutdownMemory() );
  906. TRACEINIT( Host_Init( s_bIsDedicated ), Host_Shutdown() );
  907. if ( !host_initialized )
  908. {
  909. return 0;
  910. }
  911. TRACEINIT( Sys_InitAuthentication(), Sys_ShutdownAuthentication() );
  912. MapReslistGenerator_BuildMapList();
  913. BuildMinidumpComment( NULL, false );
  914. return 1;
  915. }
  916. //-----------------------------------------------------------------------------
  917. // Purpose:
  918. //-----------------------------------------------------------------------------
  919. void Sys_ShutdownGame( void )
  920. {
  921. TRACESHUTDOWN( Sys_ShutdownAuthentication() );
  922. TRACESHUTDOWN( Host_Shutdown() );
  923. TRACESHUTDOWN( Sys_ShutdownMemory() );
  924. // TRACESHUTDOWN( Sys_ShutdownArgv() );
  925. TRACESHUTDOWN( Sys_Shutdown() );
  926. // Remove debug spew output....
  927. developer.InstallChangeCallback( 0 );
  928. SpewOutputFunc( 0 );
  929. }
  930. //
  931. // Try to load a single DLL. If it conforms to spec, keep it loaded, and add relevant
  932. // info to the DLL directory. If not, ignore it entirely.
  933. //
  934. CreateInterfaceFn g_ServerFactory;
  935. #pragma optimize( "g", off )
  936. static bool LoadThisDll( char *szDllFilename, bool bIsServerOnly )
  937. {
  938. CSysModule *pDLL = NULL;
  939. // check signature, don't let users with modified binaries connect to secure servers, they will get VAC banned
  940. if ( !Host_AllowLoadModule( szDllFilename, "GAMEBIN", true, bIsServerOnly ) )
  941. {
  942. // not supposed to load this but we will anyway
  943. Host_DisallowSecureServers();
  944. Host_AllowLoadModule( szDllFilename, "GAMEBIN", true, bIsServerOnly );
  945. }
  946. // Load DLL, ignore if cannot
  947. // ensures that the game.dll is running under Steam
  948. // this will have to be undone when we want mods to be able to run
  949. if ((pDLL = g_pFileSystem->LoadModule(szDllFilename, "GAMEBIN", false)) == NULL)
  950. {
  951. ConMsg("Failed to load %s\n", szDllFilename);
  952. goto IgnoreThisDLL;
  953. }
  954. // Load interface factory and any interfaces exported by the game .dll
  955. g_iServerGameDLLVersion = 0;
  956. g_ServerFactory = Sys_GetFactory( pDLL );
  957. if ( g_ServerFactory )
  958. {
  959. // Figure out latest version we understand
  960. g_iServerGameDLLVersion = INTERFACEVERSION_SERVERGAMEDLL_INT;
  961. // Scan for most recent version the game DLL understands.
  962. for (;;)
  963. {
  964. char archVersion[64];
  965. V_sprintf_safe( archVersion, "ServerGameDLL%03d", g_iServerGameDLLVersion );
  966. serverGameDLL = (IServerGameDLL*)g_ServerFactory(archVersion, NULL);
  967. if ( serverGameDLL )
  968. break;
  969. --g_iServerGameDLLVersion;
  970. if ( g_iServerGameDLLVersion < 4 )
  971. {
  972. g_iServerGameDLLVersion = 0;
  973. Msg( "Could not get IServerGameDLL interface from library %s", szDllFilename );
  974. goto IgnoreThisDLL;
  975. }
  976. }
  977. serverGameEnts = (IServerGameEnts*)g_ServerFactory(INTERFACEVERSION_SERVERGAMEENTS, NULL);
  978. if ( !serverGameEnts )
  979. {
  980. ConMsg( "Could not get IServerGameEnts interface from library %s", szDllFilename );
  981. goto IgnoreThisDLL;
  982. }
  983. serverGameClients = (IServerGameClients*)g_ServerFactory(INTERFACEVERSION_SERVERGAMECLIENTS, NULL);
  984. if ( serverGameClients )
  985. {
  986. g_iServerGameClientsVersion = 4;
  987. }
  988. else
  989. {
  990. // Try the previous version.
  991. const char *pINTERFACEVERSION_SERVERGAMECLIENTS_V3 = "ServerGameClients003";
  992. serverGameClients = (IServerGameClients*)g_ServerFactory(pINTERFACEVERSION_SERVERGAMECLIENTS_V3, NULL);
  993. if ( serverGameClients )
  994. {
  995. g_iServerGameClientsVersion = 3;
  996. }
  997. else
  998. {
  999. ConMsg( "Could not get IServerGameClients interface from library %s", szDllFilename );
  1000. goto IgnoreThisDLL;
  1001. }
  1002. }
  1003. serverGameDirector = (IHLTVDirector*)g_ServerFactory(INTERFACEVERSION_HLTVDIRECTOR, NULL);
  1004. if ( !serverGameDirector )
  1005. {
  1006. ConMsg( "Could not get IHLTVDirector interface from library %s", szDllFilename );
  1007. // this is not a critical
  1008. }
  1009. serverGameTags = (IServerGameTags*)g_ServerFactory(INTERFACEVERSION_SERVERGAMETAGS, NULL);
  1010. // Possible that this is NULL - optional interface
  1011. }
  1012. else
  1013. {
  1014. ConMsg( "Could not find factory interface in library %s", szDllFilename );
  1015. goto IgnoreThisDLL;
  1016. }
  1017. g_GameDLL = pDLL;
  1018. return true;
  1019. IgnoreThisDLL:
  1020. if (pDLL != NULL)
  1021. {
  1022. g_pFileSystem->UnloadModule(pDLL);
  1023. serverGameDLL = NULL;
  1024. serverGameEnts = NULL;
  1025. serverGameClients = NULL;
  1026. }
  1027. return false;
  1028. }
  1029. #pragma optimize( "", on )
  1030. //
  1031. // Scan DLL directory, load all DLLs that conform to spec.
  1032. //
  1033. void LoadEntityDLLs( const char *szBaseDir, bool bIsServerOnly )
  1034. {
  1035. memset( &gmodinfo, 0, sizeof( modinfo_t ) );
  1036. gmodinfo.version = 1;
  1037. gmodinfo.svonly = true;
  1038. // Run through all DLLs found in the extension DLL directory
  1039. g_GameDLL = NULL;
  1040. sv_noclipduringpause = NULL;
  1041. // Listing file for this game.
  1042. KeyValues *modinfo = new KeyValues("modinfo");
  1043. MEM_ALLOC_CREDIT();
  1044. if (modinfo->LoadFromFile(g_pFileSystem, "gameinfo.txt"))
  1045. {
  1046. Q_strncpy( gmodinfo.szInfo, modinfo->GetString("url_info"), sizeof( gmodinfo.szInfo ) );
  1047. Q_strncpy( gmodinfo.szDL, modinfo->GetString("url_dl"), sizeof( gmodinfo.szDL ) );
  1048. gmodinfo.version = modinfo->GetInt("version");
  1049. gmodinfo.size = modinfo->GetInt("size");
  1050. gmodinfo.svonly = modinfo->GetInt("svonly") ? true : false;
  1051. gmodinfo.cldll = modinfo->GetInt("cldll") ? true : false;
  1052. Q_strncpy( gmodinfo.szHLVersion, modinfo->GetString("hlversion"), sizeof( gmodinfo.szHLVersion ) );
  1053. }
  1054. modinfo->deleteThis();
  1055. // Load the game .dll
  1056. LoadThisDll( "server" DLL_EXT_STRING, bIsServerOnly );
  1057. if ( serverGameDLL )
  1058. {
  1059. Msg("server%s loaded for \"%s\"\n", DLL_EXT_STRING, (char *)serverGameDLL->GetGameDescription());
  1060. }
  1061. }
  1062. //-----------------------------------------------------------------------------
  1063. // Purpose: Retrieves a string value from the registry
  1064. //-----------------------------------------------------------------------------
  1065. #if defined(_WIN32)
  1066. void Sys_GetRegKeyValueUnderRoot( HKEY rootKey, const char *pszSubKey, const char *pszElement, OUT_Z_CAP(nReturnLength) char *pszReturnString, int nReturnLength, const char *pszDefaultValue )
  1067. {
  1068. LONG lResult; // Registry function result code
  1069. HKEY hKey; // Handle of opened/created key
  1070. char szBuff[128]; // Temp. buffer
  1071. ULONG dwDisposition; // Type of key opening event
  1072. DWORD dwType; // Type of key
  1073. DWORD dwSize; // Size of element data
  1074. // Copying a string to itself is both unnecessary and illegal.
  1075. // Address sanitizer prohibits this so we have to fix this in order
  1076. // to continue testing with it.
  1077. if ( pszReturnString != pszDefaultValue )
  1078. {
  1079. // Assume the worst
  1080. Q_strncpy(pszReturnString, pszDefaultValue, nReturnLength );
  1081. }
  1082. // Create it if it doesn't exist. (Create opens the key otherwise)
  1083. lResult = VCRHook_RegCreateKeyEx(
  1084. rootKey, // handle of open key
  1085. pszSubKey, // address of name of subkey to open
  1086. 0ul, // DWORD ulOptions, // reserved
  1087. "String", // Type of value
  1088. REG_OPTION_NON_VOLATILE, // Store permanently in reg.
  1089. KEY_ALL_ACCESS, // REGSAM samDesired, // security access mask
  1090. NULL,
  1091. &hKey, // Key we are creating
  1092. &dwDisposition); // Type of creation
  1093. if (lResult != ERROR_SUCCESS) // Failure
  1094. return;
  1095. // First time, just set to Valve default
  1096. if (dwDisposition == REG_CREATED_NEW_KEY)
  1097. {
  1098. // Just Set the Values according to the defaults
  1099. lResult = VCRHook_RegSetValueEx( hKey, pszElement, 0, REG_SZ, (CONST BYTE *)pszDefaultValue, Q_strlen(pszDefaultValue) + 1 );
  1100. }
  1101. else
  1102. {
  1103. // We opened the existing key. Now go ahead and find out how big the key is.
  1104. dwSize = nReturnLength;
  1105. lResult = VCRHook_RegQueryValueEx( hKey, pszElement, 0, &dwType, (unsigned char *)szBuff, &dwSize );
  1106. // Success?
  1107. if (lResult == ERROR_SUCCESS)
  1108. {
  1109. // Only copy strings, and only copy as much data as requested.
  1110. if (dwType == REG_SZ)
  1111. {
  1112. Q_strncpy(pszReturnString, szBuff, nReturnLength);
  1113. pszReturnString[nReturnLength - 1] = '\0';
  1114. }
  1115. }
  1116. else
  1117. // Didn't find it, so write out new value
  1118. {
  1119. // Just Set the Values according to the defaults
  1120. lResult = VCRHook_RegSetValueEx( hKey, pszElement, 0, REG_SZ, (CONST BYTE *)pszDefaultValue, Q_strlen(pszDefaultValue) + 1 );
  1121. }
  1122. };
  1123. // Always close this key before exiting.
  1124. VCRHook_RegCloseKey(hKey);
  1125. }
  1126. //-----------------------------------------------------------------------------
  1127. // Purpose: Retrieves a DWORD value from the registry
  1128. //-----------------------------------------------------------------------------
  1129. void Sys_GetRegKeyValueUnderRootInt( HKEY rootKey, const char *pszSubKey, const char *pszElement, long *plReturnValue, const long lDefaultValue )
  1130. {
  1131. LONG lResult; // Registry function result code
  1132. HKEY hKey; // Handle of opened/created key
  1133. ULONG dwDisposition; // Type of key opening event
  1134. DWORD dwType; // Type of key
  1135. DWORD dwSize; // Size of element data
  1136. // Assume the worst
  1137. // Set the return value to the default
  1138. *plReturnValue = lDefaultValue;
  1139. // Create it if it doesn't exist. (Create opens the key otherwise)
  1140. lResult = VCRHook_RegCreateKeyEx(
  1141. rootKey, // handle of open key
  1142. pszSubKey, // address of name of subkey to open
  1143. 0ul, // DWORD ulOptions, // reserved
  1144. "String", // Type of value
  1145. REG_OPTION_NON_VOLATILE, // Store permanently in reg.
  1146. KEY_ALL_ACCESS, // REGSAM samDesired, // security access mask
  1147. NULL,
  1148. &hKey, // Key we are creating
  1149. &dwDisposition); // Type of creation
  1150. if (lResult != ERROR_SUCCESS) // Failure
  1151. return;
  1152. // First time, just set to Valve default
  1153. if (dwDisposition == REG_CREATED_NEW_KEY)
  1154. {
  1155. // Just Set the Values according to the defaults
  1156. lResult = VCRHook_RegSetValueEx( hKey, pszElement, 0, REG_DWORD, (CONST BYTE *)&lDefaultValue, sizeof( DWORD ) );
  1157. }
  1158. else
  1159. {
  1160. // We opened the existing key. Now go ahead and find out how big the key is.
  1161. dwSize = sizeof( DWORD );
  1162. lResult = VCRHook_RegQueryValueEx( hKey, pszElement, 0, &dwType, (unsigned char *)plReturnValue, &dwSize );
  1163. // Success?
  1164. if (lResult != ERROR_SUCCESS)
  1165. // Didn't find it, so write out new value
  1166. {
  1167. // Just Set the Values according to the defaults
  1168. lResult = VCRHook_RegSetValueEx( hKey, pszElement, 0, REG_DWORD, (LPBYTE)&lDefaultValue, sizeof( DWORD ) );
  1169. }
  1170. };
  1171. // Always close this key before exiting.
  1172. VCRHook_RegCloseKey(hKey);
  1173. }
  1174. void Sys_SetRegKeyValueUnderRoot( HKEY rootKey, const char *pszSubKey, const char *pszElement, const char *pszValue )
  1175. {
  1176. LONG lResult; // Registry function result code
  1177. HKEY hKey; // Handle of opened/created key
  1178. //char szBuff[128]; // Temp. buffer
  1179. ULONG dwDisposition; // Type of key opening event
  1180. //DWORD dwType; // Type of key
  1181. //DWORD dwSize; // Size of element data
  1182. // Create it if it doesn't exist. (Create opens the key otherwise)
  1183. lResult = VCRHook_RegCreateKeyEx(
  1184. rootKey, // handle of open key
  1185. pszSubKey, // address of name of subkey to open
  1186. 0ul, // DWORD ulOptions, // reserved
  1187. "String", // Type of value
  1188. REG_OPTION_NON_VOLATILE, // Store permanently in reg.
  1189. KEY_ALL_ACCESS, // REGSAM samDesired, // security access mask
  1190. NULL,
  1191. &hKey, // Key we are creating
  1192. &dwDisposition); // Type of creation
  1193. if (lResult != ERROR_SUCCESS) // Failure
  1194. return;
  1195. // First time, just set to Valve default
  1196. if (dwDisposition == REG_CREATED_NEW_KEY)
  1197. {
  1198. // Just Set the Values according to the defaults
  1199. lResult = VCRHook_RegSetValueEx( hKey, pszElement, 0, REG_SZ, (CONST BYTE *)pszValue, Q_strlen(pszValue) + 1 );
  1200. }
  1201. else
  1202. {
  1203. /*
  1204. // FIXE: We might want to support a mode where we only create this key, we don't overwrite values already present
  1205. // We opened the existing key. Now go ahead and find out how big the key is.
  1206. dwSize = nReturnLength;
  1207. lResult = VCRHook_RegQueryValueEx( hKey, pszElement, 0, &dwType, (unsigned char *)szBuff, &dwSize );
  1208. // Success?
  1209. if (lResult == ERROR_SUCCESS)
  1210. {
  1211. // Only copy strings, and only copy as much data as requested.
  1212. if (dwType == REG_SZ)
  1213. {
  1214. Q_strncpy(pszReturnString, szBuff, nReturnLength);
  1215. pszReturnString[nReturnLength - 1] = '\0';
  1216. }
  1217. }
  1218. else
  1219. */
  1220. // Didn't find it, so write out new value
  1221. {
  1222. // Just Set the Values according to the defaults
  1223. lResult = VCRHook_RegSetValueEx( hKey, pszElement, 0, REG_SZ, (CONST BYTE *)pszValue, Q_strlen(pszValue) + 1 );
  1224. }
  1225. };
  1226. // Always close this key before exiting.
  1227. VCRHook_RegCloseKey(hKey);
  1228. }
  1229. #endif
  1230. void Sys_GetRegKeyValue( const char *pszSubKey, const char *pszElement, OUT_Z_CAP(nReturnLength) char *pszReturnString, int nReturnLength, const char *pszDefaultValue )
  1231. {
  1232. #if defined(_WIN32)
  1233. Sys_GetRegKeyValueUnderRoot( HKEY_CURRENT_USER, pszSubKey, pszElement, pszReturnString, nReturnLength, pszDefaultValue );
  1234. #else
  1235. //hushed Assert( !"Impl me" );
  1236. // Copying a string to itself is both unnecessary and illegal.
  1237. if ( pszReturnString != pszDefaultValue )
  1238. {
  1239. Q_strncpy( pszReturnString, pszDefaultValue, nReturnLength );
  1240. }
  1241. #endif
  1242. }
  1243. void Sys_GetRegKeyValueInt( const char *pszSubKey, const char *pszElement, long *plReturnValue, long lDefaultValue)
  1244. {
  1245. #if defined(_WIN32)
  1246. Sys_GetRegKeyValueUnderRootInt( HKEY_CURRENT_USER, pszSubKey, pszElement, plReturnValue, lDefaultValue );
  1247. #else
  1248. //hushed Assert( !"Impl me" );
  1249. *plReturnValue = lDefaultValue;
  1250. #endif
  1251. }
  1252. void Sys_SetRegKeyValue( const char *pszSubKey, const char *pszElement, const char *pszValue )
  1253. {
  1254. #if defined(_WIN32)
  1255. Sys_SetRegKeyValueUnderRoot( HKEY_CURRENT_USER, pszSubKey, pszElement, pszValue );
  1256. #else
  1257. //hushed Assert( !"Impl me" );
  1258. #endif
  1259. }
  1260. #define SOURCE_ENGINE_APP_CLASS "Valve.Source"
  1261. void Sys_CreateFileAssociations( int count, FileAssociationInfo *list )
  1262. {
  1263. #if defined(_WIN32)
  1264. if ( IsX360() )
  1265. return;
  1266. char appname[ 512 ];
  1267. GetModuleFileName( 0, appname, sizeof( appname ) );
  1268. Q_FixSlashes( appname );
  1269. Q_strlower( appname );
  1270. char quoted_appname_with_arg[ 512 ];
  1271. Q_snprintf( quoted_appname_with_arg, sizeof( quoted_appname_with_arg ), "\"%s\" \"%%1\"", appname );
  1272. char base_exe_name[ 256 ];
  1273. Q_FileBase( appname, base_exe_name, sizeof( base_exe_name) );
  1274. Q_DefaultExtension( base_exe_name, ".exe", sizeof( base_exe_name ) );
  1275. // HKEY_CLASSES_ROOT/Valve.Source/shell/open/command == "u:\tf2\hl2.exe" "%1" quoted
  1276. Sys_SetRegKeyValueUnderRoot( HKEY_CLASSES_ROOT, va( "%s\\shell\\open\\command", SOURCE_ENGINE_APP_CLASS ), "", quoted_appname_with_arg );
  1277. // HKEY_CLASSES_ROOT/Applications/hl2.exe/shell/open/command == "u:\tf2\hl2.exe" "%1" quoted
  1278. Sys_SetRegKeyValueUnderRoot( HKEY_CLASSES_ROOT, va( "Applications\\%s\\shell\\open\\command", base_exe_name ), "", quoted_appname_with_arg );
  1279. for ( int i = 0; i < count ; i++ )
  1280. {
  1281. FileAssociationInfo *fa = &list[ i ];
  1282. char binding[32];
  1283. binding[0] = 0;
  1284. // Create file association for our .exe
  1285. // HKEY_CLASSES_ROOT/.dem == "Valve.Source"
  1286. Sys_GetRegKeyValueUnderRoot( HKEY_CLASSES_ROOT, fa->extension, "", binding, sizeof(binding), "" );
  1287. if ( Q_strlen( binding ) == 0 )
  1288. {
  1289. Sys_SetRegKeyValueUnderRoot( HKEY_CLASSES_ROOT, fa->extension, "", SOURCE_ENGINE_APP_CLASS );
  1290. }
  1291. }
  1292. #endif
  1293. }
  1294. void Sys_NoCrashDialog()
  1295. {
  1296. #if defined(_WIN32)
  1297. ::SetErrorMode(SetErrorMode(SEM_NOGPFAULTERRORBOX) | SEM_NOGPFAULTERRORBOX);
  1298. #endif
  1299. }
  1300. void Sys_TestSendKey( const char *pKey )
  1301. {
  1302. #if defined(_WIN32) && !defined(USE_SDL) && !defined(_XBOX)
  1303. int key = pKey[0];
  1304. if ( pKey[0] == '\\' && pKey[1] == 'r' )
  1305. {
  1306. key = VK_RETURN;
  1307. }
  1308. HWND hWnd = (HWND)game->GetMainWindow();
  1309. PostMessageA( hWnd, WM_KEYDOWN, key, 0 );
  1310. PostMessageA( hWnd, WM_KEYUP, key, 0 );
  1311. //void Key_Event (int key, bool down);
  1312. //Key_Event( key, 1 );
  1313. //Key_Event( key, 0 );
  1314. #endif
  1315. }
  1316. void Sys_OutputDebugString(const char *msg)
  1317. {
  1318. Plat_DebugString( msg );
  1319. }
  1320. //-----------------------------------------------------------------------------
  1321. // Purpose:
  1322. //-----------------------------------------------------------------------------
  1323. void UnloadEntityDLLs( void )
  1324. {
  1325. if ( !g_GameDLL )
  1326. return;
  1327. // Unlink the cvars associated with game DLL
  1328. FileSystem_UnloadModule( g_GameDLL );
  1329. g_GameDLL = NULL;
  1330. serverGameDLL = NULL;
  1331. serverGameEnts = NULL;
  1332. serverGameClients = NULL;
  1333. sv_noclipduringpause = NULL;
  1334. }
  1335. CON_COMMAND( star_memory, "Dump memory stats" )
  1336. {
  1337. // get a current stat of available memory
  1338. // 32 MB is reserved and fixed by OS, so not reporting to allow memory loggers sync
  1339. #ifdef LINUX
  1340. struct mallinfo memstats = mallinfo( );
  1341. Msg( "sbrk size: %.2f MB, Used: %.2f MB, #mallocs = %d\n",
  1342. memstats.arena / ( 1024.0 * 1024.0), memstats.uordblks / ( 1024.0 * 1024.0 ), memstats.hblks );
  1343. #elif OSX
  1344. struct mstats memstats = mstats( );
  1345. Msg( "Available %.2f MB, Used: %.2f MB, #mallocs = %lu\n",
  1346. memstats.bytes_free / ( 1024.0 * 1024.0), memstats.bytes_used / ( 1024.0 * 1024.0 ), memstats.chunks_used );
  1347. #else
  1348. MEMORYSTATUS stat;
  1349. GlobalMemoryStatus( &stat );
  1350. Msg( "Available: %.2f MB, Used: %.2f MB, Free: %.2f MB\n",
  1351. stat.dwTotalPhys/( 1024.0f*1024.0f ) - 32.0f,
  1352. ( stat.dwTotalPhys - stat.dwAvailPhys )/( 1024.0f*1024.0f ) - 32.0f,
  1353. stat.dwAvailPhys/( 1024.0f*1024.0f ) );
  1354. #endif
  1355. }