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.

1413 lines
32 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "host.h"
  9. #include <ctype.h>
  10. #include "draw.h"
  11. #include "zone.h"
  12. #include "sys.h"
  13. #include <edict.h>
  14. #include <coordsize.h>
  15. #include <characterset.h>
  16. #include <bitbuf.h>
  17. #include "common.h"
  18. #include <malloc.h>
  19. #include "traceinit.h"
  20. #include <filesystem.h>
  21. #include "filesystem_engine.h"
  22. #include <convar.h>
  23. #include "gl_matsysiface.h"
  24. #include "filesystem_init.h"
  25. #include <materialsystem/imaterialsystemhardwareconfig.h>
  26. #include <tier0/icommandline.h>
  27. #include <vstdlib/random.h>
  28. #include "sys_dll.h"
  29. #include "datacache/idatacache.h"
  30. #include "matchmaking.h"
  31. #include "tier1/KeyValues.h"
  32. #include "vgui_baseui_interface.h"
  33. #include "tier2/tier2.h"
  34. #include "language.h"
  35. #ifndef SWDS
  36. #include "cl_steamauth.h"
  37. #endif
  38. #include "tier3/tier3.h"
  39. #include <vgui/ILocalize.h>
  40. #include "tier1/lzss.h"
  41. #include "tier1/snappy.h"
  42. // memdbgon must be the last include file in a .cpp file!!!
  43. #include "tier0/memdbgon.h"
  44. // Things in other C files.
  45. #define MAX_LOG_DIRECTORIES 10000
  46. bool com_ignorecolons = false;
  47. // wordbreak parsing set
  48. static characterset_t g_BreakSet, g_BreakSetIncludingColons;
  49. #define COM_TOKEN_MAX_LENGTH 1024
  50. char com_token[COM_TOKEN_MAX_LENGTH];
  51. /*
  52. All of Quake's data access is through a hierarchical file system, but the contents of
  53. the file system can be transparently merged from several sources.
  54. The "base directory" is the path to the directory holding the quake.exe and all
  55. game directories. The sys_* files pass this to host_init in engineparms->basedir.
  56. This can be overridden with the "-basedir" command line parm to allow code
  57. debugging in a different directory. The base directory is
  58. only used during filesystem initialization.
  59. The "game directory" is the first tree on the search path and directory
  60. that all generated files (savegames, screenshots, demos, config files) will
  61. be saved to. This can be overridden with the "-game" command line parameter.
  62. The game directory can never be changed while quake is executing.
  63. This is a precacution against having a malicious server instruct clients
  64. to write files over areas they shouldn't.
  65. The "cache directory" is only used during development to save network bandwidth,
  66. especially over ISDN / T1 lines. If there is a cache directory
  67. specified, when a file is found by the normal search path, it will be mirrored
  68. into the cache directory, then opened there.
  69. FIXME:
  70. The file "parms.txt" will be read out of the game directory and appended to the
  71. current command line arguments to allow different games to initialize startup
  72. parms differently. This could be used to add a "-sspeed 22050" for the high
  73. quality sound edition. Because they are added at the end, they will not override
  74. an explicit setting on the original command line.
  75. */
  76. /*
  77. ==============================
  78. COM_ExplainDisconnection
  79. ==============================
  80. */
  81. void COM_ExplainDisconnection( bool bPrint, const char *fmt, ... )
  82. {
  83. if ( IsX360() )
  84. {
  85. g_pMatchmaking->SessionNotification( SESSION_NOTIFY_LOST_SERVER );
  86. }
  87. else
  88. {
  89. va_list argptr;
  90. char string[1024];
  91. va_start (argptr, fmt);
  92. Q_vsnprintf(string, sizeof( string ), fmt,argptr);
  93. va_end (argptr);
  94. Q_strncpy( gszDisconnectReason, string, 256 );
  95. gfExtendedError = true;
  96. }
  97. if ( bPrint )
  98. {
  99. if ( gszDisconnectReason[0] == '#' )
  100. {
  101. wchar_t formatStr[256];
  102. const wchar_t *wpchReason = g_pVGuiLocalize ? g_pVGuiLocalize->Find(gszDisconnectReason) : NULL;
  103. if ( wpchReason )
  104. {
  105. wcsncpy(formatStr, wpchReason, sizeof( formatStr ) / sizeof( wchar_t ) );
  106. char conStr[256];
  107. g_pVGuiLocalize->ConvertUnicodeToANSI(formatStr, conStr, sizeof( conStr ));
  108. ConMsg( "%s\n", conStr );
  109. }
  110. else
  111. ConMsg( "%s\n", gszDisconnectReason );
  112. }
  113. else
  114. {
  115. ConMsg( "%s\n", gszDisconnectReason );
  116. }
  117. }
  118. }
  119. /*
  120. ==============================
  121. COM_ExtendedExplainDisconnection
  122. ==============================
  123. */
  124. void COM_ExtendedExplainDisconnection( bool bPrint, const char *fmt, ... )
  125. {
  126. if ( IsX360() )
  127. {
  128. g_pMatchmaking->SessionNotification( SESSION_NOTIFY_LOST_SERVER );
  129. }
  130. else
  131. {
  132. va_list argptr;
  133. char string[1024];
  134. va_start (argptr, fmt);
  135. Q_vsnprintf(string, sizeof( string ), fmt,argptr);
  136. va_end (argptr);
  137. Q_strncpy( gszExtendedDisconnectReason, string, 256 );
  138. }
  139. if ( bPrint )
  140. {
  141. ConMsg( "%s\n", gszExtendedDisconnectReason );
  142. }
  143. }
  144. /*
  145. ==============
  146. COM_Parse
  147. Parse a token out of a string
  148. ==============
  149. */
  150. const char *COM_Parse (const char *data)
  151. {
  152. unsigned char c;
  153. int len;
  154. characterset_t *breaks;
  155. breaks = &g_BreakSetIncludingColons;
  156. if ( com_ignorecolons )
  157. breaks = &g_BreakSet;
  158. len = 0;
  159. com_token[0] = 0;
  160. if (!data)
  161. return NULL;
  162. // skip whitespace
  163. skipwhite:
  164. while ( (c = *data) <= ' ')
  165. {
  166. if (c == 0)
  167. return NULL; // end of file;
  168. data++;
  169. }
  170. // skip // comments
  171. if (c=='/' && data[1] == '/')
  172. {
  173. while (*data && *data != '\n')
  174. data++;
  175. goto skipwhite;
  176. }
  177. // handle quoted strings specially
  178. if (c == '\"')
  179. {
  180. data++;
  181. while (1)
  182. {
  183. c = *data++;
  184. if (c=='\"' || !c)
  185. {
  186. com_token[len] = 0;
  187. return data;
  188. }
  189. com_token[len] = c;
  190. len++;
  191. }
  192. }
  193. // parse single characters
  194. if ( IN_CHARACTERSET( *breaks, c ) )
  195. {
  196. com_token[len] = c;
  197. len++;
  198. com_token[len] = 0;
  199. return data+1;
  200. }
  201. // parse a regular word
  202. do
  203. {
  204. com_token[len] = c;
  205. data++;
  206. len++;
  207. c = *data;
  208. if ( IN_CHARACTERSET( *breaks, c ) )
  209. break;
  210. } while (c>32);
  211. com_token[len] = 0;
  212. return data;
  213. }
  214. /*
  215. ==============
  216. COM_AddNoise
  217. Changes n random bits in a data block
  218. ==============
  219. */
  220. void COM_AddNoise( unsigned char *data, int length, int number )
  221. {
  222. for ( int i = 0; i < number; i++ )
  223. {
  224. int randomByte = RandomInt( 0, length-1 );
  225. int randomBit = RandomInt( 0, 7 );
  226. // get original data
  227. unsigned char dataByte = data[randomByte];
  228. // flip bit
  229. if ( dataByte & randomBit )
  230. {
  231. dataByte &= ~randomBit;
  232. }
  233. else
  234. {
  235. dataByte |= randomBit;
  236. }
  237. // write back
  238. data[randomByte] = dataByte;
  239. }
  240. }
  241. /*
  242. ==============
  243. COM_Parse_Line
  244. Parse a line out of a string
  245. ==============
  246. */
  247. const char *COM_ParseLine (const char *data)
  248. {
  249. int c;
  250. int len;
  251. len = 0;
  252. com_token[0] = 0;
  253. if (!data)
  254. return NULL;
  255. c = *data;
  256. // parse a line out of the data
  257. do
  258. {
  259. com_token[len] = c;
  260. data++;
  261. len++;
  262. c = *data;
  263. } while ( ( c>=' ' || c < 0 || c == '\t' ) && ( len < COM_TOKEN_MAX_LENGTH - 1 ) );
  264. com_token[len] = 0;
  265. if (c==0) // end of file
  266. return NULL;
  267. // eat whitespace (LF,CR,etc.) at the end of this line
  268. while ( (c = *data) < ' ' )
  269. {
  270. if (c == 0)
  271. return NULL; // end of file;
  272. data++;
  273. }
  274. return data;
  275. }
  276. /*
  277. ==============
  278. COM_TokenWaiting
  279. Returns 1 if additional data is waiting to be processed on this line
  280. ==============
  281. */
  282. int COM_TokenWaiting( const char *buffer )
  283. {
  284. const char *p;
  285. p = buffer;
  286. while ( *p && *p!='\n')
  287. {
  288. if ( !V_isspace( *p ) || V_isalnum( *p ) )
  289. return 1;
  290. p++;
  291. }
  292. return 0;
  293. }
  294. /*
  295. ============
  296. tmpstr512
  297. rotates through a bunch of string buffers of 512 bytes each
  298. ============
  299. */
  300. char *tmpstr512()
  301. {
  302. static char string[32][512];
  303. static int curstring = 0;
  304. curstring = ( curstring + 1 ) & 31;
  305. return string[curstring];
  306. }
  307. /*
  308. ============
  309. va
  310. does a varargs printf into a temp buffer, so I don't need to have
  311. varargs versions of all text functions.
  312. ============
  313. */
  314. char *va( const char *format, ... )
  315. {
  316. char* outbuf = tmpstr512();
  317. va_list argptr;
  318. va_start (argptr, format);
  319. Q_vsnprintf( outbuf, 512, format, argptr );
  320. va_end (argptr);
  321. return outbuf;
  322. }
  323. /*
  324. ============
  325. vstr
  326. prints a vector into a temporary string
  327. bufffer.
  328. ============
  329. */
  330. const char *vstr(Vector& v)
  331. {
  332. char* outbuf = tmpstr512();
  333. Q_snprintf(outbuf, 512, "%.2f %.2f %.2f", v[0], v[1], v[2]);
  334. return outbuf;
  335. }
  336. char com_basedir[MAX_OSPATH];
  337. char com_gamedir[MAX_OSPATH];
  338. /*
  339. ==================
  340. CL_CheckGameDirectory
  341. Client side game directory change.
  342. ==================
  343. */
  344. bool COM_CheckGameDirectory( const char *gamedir )
  345. {
  346. // Switch game directories if needed, or abort if it's not good.
  347. char szGD[ MAX_OSPATH ];
  348. if ( !gamedir || !gamedir[0] )
  349. {
  350. ConMsg( "Server didn't specify a gamedir, assuming no change\n" );
  351. return true;
  352. }
  353. // Rip out the current gamedir.
  354. Q_FileBase( com_gamedir, szGD, sizeof( szGD ) );
  355. if ( Q_stricmp( szGD, gamedir ) )
  356. {
  357. // Changing game directories without restarting is not permitted any more
  358. ConMsg( "COM_CheckGameDirectory: game directories don't match (%s / %s)\n", szGD, gamedir );
  359. return false;
  360. }
  361. return true;
  362. }
  363. //-----------------------------------------------------------------------------
  364. // Purpose: Finds the file in the search path.
  365. // Input : *filename -
  366. // *file -
  367. // Output : int
  368. //-----------------------------------------------------------------------------
  369. int COM_FindFile( const char *filename, FileHandle_t *file )
  370. {
  371. Assert( file );
  372. int filesize = -1;
  373. *file = g_pFileSystem->Open( filename, "rb" );
  374. if ( *file )
  375. {
  376. filesize = g_pFileSystem->Size( *file );
  377. }
  378. return filesize;
  379. }
  380. //-----------------------------------------------------------------------------
  381. // Purpose:
  382. // Input : *filename -
  383. // *file -
  384. // Output : int
  385. //-----------------------------------------------------------------------------
  386. int COM_OpenFile( const char *filename, FileHandle_t *file )
  387. {
  388. return COM_FindFile( (char *)filename, file );
  389. }
  390. /*
  391. ============
  392. COM_WriteFile
  393. The filename will be prefixed by the current game directory
  394. ============
  395. */
  396. void COM_WriteFile (const char *filename, void *data, int len)
  397. {
  398. FileHandle_t handle;
  399. int nameLen = strlen( filename ) + 2;
  400. char *pName = ( char * )_alloca( nameLen );
  401. Q_snprintf( pName, nameLen, "%s", filename);
  402. Q_FixSlashes( pName );
  403. COM_CreatePath( pName );
  404. handle = g_pFileSystem->Open( pName, "wb" );
  405. if ( !handle )
  406. {
  407. Warning ("COM_WriteFile: failed on %s\n", pName);
  408. return;
  409. }
  410. g_pFileSystem->Write( data, len, handle );
  411. g_pFileSystem->Close( handle );
  412. }
  413. /*
  414. ============
  415. COM_CreatePath
  416. Only used for CopyFile
  417. ============
  418. */
  419. void COM_CreatePath (const char *path)
  420. {
  421. char temppath[1024];
  422. Q_strncpy(temppath, path, sizeof(temppath));
  423. Q_StripFilename( temppath );
  424. Sys_mkdir( temppath );
  425. }
  426. /*
  427. ===========
  428. COM_CopyFile
  429. Copies a file from pSourcePath to pDestPath.
  430. ===========
  431. */
  432. bool COM_CopyFile ( const char *pSourcePath, const char *pDestPath )
  433. {
  434. if ( IsX360() )
  435. return false;
  436. int remaining, count;
  437. char buf[4096];
  438. FileHandle_t in, out;
  439. in = g_pFileSystem->Open( pSourcePath, "rb" );
  440. AssertMsg( in, "COM_CopyFile(): Input file failed to open" );
  441. if ( in == FILESYSTEM_INVALID_HANDLE )
  442. return false;
  443. // create directories up to the cache file
  444. COM_CreatePath( pDestPath );
  445. out = g_pFileSystem->Open( pDestPath, "wb" );
  446. AssertMsg( out, "COM_CopyFile(): Output file failed to open" );
  447. if ( out == FILESYSTEM_INVALID_HANDLE )
  448. {
  449. g_pFileSystem->Close( in );
  450. return false;
  451. }
  452. remaining = g_pFileSystem->Size( in );
  453. while ( remaining > 0 )
  454. {
  455. if (remaining < sizeof(buf))
  456. {
  457. count = remaining;
  458. }
  459. else
  460. {
  461. count = sizeof(buf);
  462. }
  463. g_pFileSystem->Read( buf, count, in );
  464. g_pFileSystem->Write( buf, count, out );
  465. remaining -= count;
  466. }
  467. g_pFileSystem->Close( in );
  468. g_pFileSystem->Close( out );
  469. return true;
  470. }
  471. /*
  472. ===========
  473. COM_ExpandFilename
  474. Finds the file in the search path, copies over the name with the full path name.
  475. This doesn't search in the pak file.
  476. ===========
  477. */
  478. int COM_ExpandFilename( char *filename, int maxlength )
  479. {
  480. char expanded[MAX_OSPATH];
  481. if ( g_pFileSystem->GetLocalPath( filename, expanded, sizeof(expanded) ) != NULL )
  482. {
  483. Q_strncpy( filename, expanded, maxlength );
  484. return 1;
  485. }
  486. if ( filename && filename[0] != '*' )
  487. {
  488. Warning ("COM_ExpandFilename: can't find %s\n", filename);
  489. }
  490. return 0;
  491. }
  492. /*
  493. ===========
  494. COM_FileSize
  495. Returns the size of the file only.
  496. ===========
  497. */
  498. int COM_FileSize (const char *filename)
  499. {
  500. return g_pFileSystem->Size(filename);
  501. }
  502. //-----------------------------------------------------------------------------
  503. // Purpose: Close file handle
  504. // Input : hFile -
  505. //-----------------------------------------------------------------------------
  506. void COM_CloseFile( FileHandle_t hFile )
  507. {
  508. g_pFileSystem->Close( hFile );
  509. }
  510. /*
  511. ============
  512. COM_LoadFile
  513. Filename are reletive to the quake directory.
  514. Allways appends a 0 byte.
  515. ============
  516. */
  517. cache_user_t *loadcache;
  518. byte *loadbuf;
  519. int loadsize;
  520. byte *COM_LoadFile (const char *path, int usehunk, int *pLength)
  521. {
  522. FileHandle_t hFile;
  523. byte *buf = NULL;
  524. char base[128];
  525. int len;
  526. if (pLength)
  527. {
  528. *pLength = 0;
  529. }
  530. // look for it in the filesystem or pack files
  531. len = COM_OpenFile( path, &hFile );
  532. if ( !hFile )
  533. {
  534. return NULL;
  535. }
  536. // Extract the filename base name for hunk tag
  537. Q_FileBase( path, base, sizeof( base ) );
  538. unsigned bufSize = len + 1;
  539. if ( IsX360() )
  540. {
  541. bufSize = g_pFileSystem->GetOptimalReadSize( hFile, bufSize ); // align to sector
  542. }
  543. switch ( usehunk )
  544. {
  545. case 1:
  546. buf = (byte *)Hunk_AllocName (bufSize, base);
  547. break;
  548. case 2:
  549. AssertMsg( 0, "Temp alloc no longer supported\n" );
  550. break;
  551. case 3:
  552. AssertMsg( 0, "Cache alloc no longer supported\n" );
  553. break;
  554. case 4:
  555. {
  556. if (len+1 > loadsize)
  557. buf = (byte *)malloc(bufSize);
  558. else
  559. buf = loadbuf;
  560. }
  561. break;
  562. case 5:
  563. buf = (byte *)malloc(bufSize); // YWB: FIXME, this is evil.
  564. break;
  565. default:
  566. Sys_Error ("COM_LoadFile: bad usehunk");
  567. }
  568. if ( !buf )
  569. {
  570. Sys_Error ("COM_LoadFile: not enough space for %s", path);
  571. COM_CloseFile(hFile); // exit here to prevent fault on oom (kdb)
  572. return NULL;
  573. }
  574. g_pFileSystem->ReadEx( buf, bufSize, len, hFile );
  575. COM_CloseFile( hFile );
  576. ((byte *)buf)[ len ] = 0;
  577. if ( pLength )
  578. {
  579. *pLength = len;
  580. }
  581. return buf;
  582. }
  583. /*
  584. ===============
  585. COM_CopyFileChunk
  586. ===============
  587. */
  588. void COM_CopyFileChunk( FileHandle_t dst, FileHandle_t src, int nSize )
  589. {
  590. int copysize = nSize;
  591. char copybuf[COM_COPY_CHUNK_SIZE];
  592. while (copysize > COM_COPY_CHUNK_SIZE)
  593. {
  594. g_pFileSystem->Read ( copybuf, COM_COPY_CHUNK_SIZE, src );
  595. g_pFileSystem->Write( copybuf, COM_COPY_CHUNK_SIZE, dst );
  596. copysize -= COM_COPY_CHUNK_SIZE;
  597. }
  598. g_pFileSystem->Read ( copybuf, copysize, src );
  599. g_pFileSystem->Write( copybuf, copysize, dst );
  600. g_pFileSystem->Flush ( src );
  601. g_pFileSystem->Flush ( dst );
  602. }
  603. // uses malloc if larger than bufsize
  604. byte *COM_LoadStackFile (const char *path, void *buffer, int bufsize, int& filesize )
  605. {
  606. byte *buf;
  607. loadbuf = (byte *)buffer;
  608. loadsize = bufsize;
  609. buf = COM_LoadFile (path, 4, &filesize );
  610. return buf;
  611. }
  612. void COM_ShutdownFileSystem( void )
  613. {
  614. }
  615. /*
  616. ================
  617. COM_Shutdown
  618. Remove the searchpaths
  619. ================
  620. */
  621. void COM_Shutdown( void )
  622. {
  623. }
  624. //-----------------------------------------------------------------------------
  625. // Purpose: allocates memory and copys source text
  626. // Input : *in -
  627. // Output : char *CopyString
  628. //-----------------------------------------------------------------------------
  629. char *COM_StringCopy(const char *in)
  630. {
  631. int len = Q_strlen(in)+1;
  632. char *out = (char *)new char[ len ];
  633. Q_strncpy (out, in, len );
  634. return out;
  635. }
  636. void COM_StringFree(const char *in)
  637. {
  638. delete [] in;
  639. }
  640. void COM_SetupLogDir( const char *mapname )
  641. {
  642. char gameDir[MAX_OSPATH];
  643. COM_GetGameDir( gameDir, sizeof( gameDir ) );
  644. // Blat out the all directories in the LOGDIR path
  645. g_pFileSystem->RemoveSearchPath( NULL, "LOGDIR" );
  646. // set the log directory
  647. if ( mapname && CommandLine()->FindParm("-uselogdir") )
  648. {
  649. int i;
  650. char sRelativeLogDir[MAX_PATH];
  651. for ( i = 0; i < MAX_LOG_DIRECTORIES; i++ )
  652. {
  653. Q_snprintf( sRelativeLogDir, sizeof( sRelativeLogDir ), "logs/%s/%04i", mapname, i );
  654. if ( !g_pFileSystem->IsDirectory( sRelativeLogDir, "GAME" ) )
  655. break;
  656. }
  657. // Loop at max
  658. if ( i == MAX_LOG_DIRECTORIES )
  659. {
  660. i = 0;
  661. Q_snprintf( sRelativeLogDir, sizeof( sRelativeLogDir ), "logs/%s/%04i", mapname, i );
  662. }
  663. // Make sure the directories we need exist.
  664. g_pFileSystem->CreateDirHierarchy( sRelativeLogDir, "GAME" );
  665. {
  666. static bool pathsetup = false;
  667. if ( !pathsetup )
  668. {
  669. pathsetup = true;
  670. // Set the search path
  671. char sLogDir[MAX_PATH];
  672. Q_snprintf( sLogDir, sizeof( sLogDir ), "%s/%s", gameDir, sRelativeLogDir );
  673. g_pFileSystem->AddSearchPath( sLogDir, "LOGDIR" );
  674. }
  675. }
  676. }
  677. else
  678. {
  679. // Default to the base game directory for logs.
  680. g_pFileSystem->AddSearchPath( gameDir, "LOGDIR" );
  681. }
  682. }
  683. /*
  684. ================
  685. COM_GetModDirectory - return the final directory in the game dir (i.e "cstrike", "hl2", rather than c:\blah\cstrike )
  686. ================
  687. */
  688. const char *COM_GetModDirectory()
  689. {
  690. static char modDir[MAX_PATH];
  691. if ( Q_strlen( modDir ) == 0 )
  692. {
  693. const char *gamedir = CommandLine()->ParmValue("-game", CommandLine()->ParmValue( "-defaultgamedir", "hl2" ) );
  694. Q_strncpy( modDir, gamedir, sizeof(modDir) );
  695. if ( strchr( modDir, '/' ) || strchr( modDir, '\\' ) )
  696. {
  697. Q_StripLastDir( modDir, sizeof(modDir) );
  698. int dirlen = Q_strlen( modDir );
  699. Q_strncpy( modDir, gamedir + dirlen, sizeof(modDir) - dirlen );
  700. }
  701. }
  702. return modDir;
  703. }
  704. /*
  705. ================
  706. Return if we should load content from the _hd folder for this mod
  707. This logic needs to match with the gameui/OptionsSubVideo.cpp code
  708. ================
  709. */
  710. bool BLoadHDContent( const char *pchModDir, const char *pchBaseDir )
  711. {
  712. char szModSteamInfPath[ 1024 ];
  713. V_ComposeFileName( pchModDir, "game_hd.txt", szModSteamInfPath, sizeof( szModSteamInfPath ) );
  714. char szFullPath[ 1024 ];
  715. V_MakeAbsolutePath( szFullPath, sizeof( szFullPath ), szModSteamInfPath, pchBaseDir );
  716. FILE *fp = fopen( szFullPath, "rb" );
  717. if ( fp )
  718. {
  719. fclose(fp);
  720. return true;
  721. }
  722. return false;
  723. }
  724. extern void Host_CheckGore( void );
  725. /*
  726. ================
  727. COM_InitFilesystem
  728. ================
  729. */
  730. void COM_InitFilesystem( const char *pFullModPath )
  731. {
  732. CFSSearchPathsInit initInfo;
  733. #ifndef SWDS
  734. if ( IsPC() )
  735. {
  736. static char language[128];
  737. language[0] = 0;
  738. // There are two language at play here. The Audio language which is controled by the
  739. // properties on the game itself in Steam (at least for now). And the language Steam is set to.
  740. // Under Windows the text in the game is controled by the language Steam is set in, but the audio
  741. // is controled by the language set in the game's properties which we can get from Steam3Client
  742. // A command line override for audio language has also been added.
  743. // -audiolanguage <language>
  744. // User must have the .vpk files for the language installed though in order to use the command line switch
  745. if ( Steam3Client().SteamApps() )
  746. {
  747. // use -audiolanguage command line to override audio language, otherwise take language from steam
  748. Q_strncpy(language, CommandLine()->ParmValue("-audiolanguage", Steam3Client().SteamApps()->GetCurrentGameLanguage()), sizeof( language ) - 1);
  749. }
  750. else
  751. {
  752. // still allow command line override even when not running steam
  753. if (CommandLine()->CheckParm("-audiolanguage"))
  754. {
  755. Q_strncpy(language, CommandLine()->ParmValue("-audiolanguage", "english"), sizeof( language ) - 1);
  756. }
  757. }
  758. if ( ( Q_strlen(language) > 0 ) && ( Q_stricmp(language, "english") ) )
  759. {
  760. initInfo.m_pLanguage = language;
  761. }
  762. }
  763. #endif
  764. initInfo.m_pFileSystem = g_pFileSystem;
  765. initInfo.m_pDirectoryName = pFullModPath;
  766. if ( !initInfo.m_pDirectoryName )
  767. {
  768. initInfo.m_pDirectoryName = GetCurrentGame();
  769. }
  770. Host_CheckGore();
  771. initInfo.m_bLowViolence = g_bLowViolence;
  772. initInfo.m_bMountHDContent = BLoadHDContent( initInfo.m_pDirectoryName, GetBaseDirectory() );
  773. // Load gameinfo.txt and setup all the search paths, just like the tools do.
  774. FileSystem_LoadSearchPaths( initInfo );
  775. // The mod path becomes com_gamedir.
  776. Q_MakeAbsolutePath( com_gamedir, sizeof( com_gamedir ), initInfo.m_ModPath );
  777. // Set com_basedir.
  778. Q_strncpy ( com_basedir, GetBaseDirectory(), sizeof( com_basedir ) ); // the "root" directory where hl2.exe is
  779. Q_strlower( com_basedir );
  780. Q_FixSlashes( com_basedir );
  781. #if !defined( SWDS ) && !defined( DEDICATED )
  782. EngineVGui()->SetVGUIDirectories();
  783. #endif
  784. // Set LOGDIR to be something reasonable
  785. COM_SetupLogDir( NULL );
  786. // g_pFileSystem->PrintSearchPaths();
  787. }
  788. const char *COM_DXLevelToString( int dxlevel )
  789. {
  790. bool bHalfPrecision = false;
  791. const char *pShaderDLLName = g_pMaterialSystemHardwareConfig->GetShaderDLLName();
  792. if( pShaderDLLName && Q_stristr( pShaderDLLName, "nvfx" ) )
  793. {
  794. bHalfPrecision = true;
  795. }
  796. if( CommandLine()->CheckParm( "-dxlevel" ) )
  797. {
  798. switch( dxlevel )
  799. {
  800. case 0:
  801. return "default";
  802. case 60:
  803. return "6.0";
  804. case 70:
  805. return "7.0";
  806. case 80:
  807. return "8.0";
  808. case 81:
  809. return "8.1";
  810. case 82:
  811. if( bHalfPrecision )
  812. {
  813. return "8.1 with some 9.0 (half-precision)";
  814. }
  815. else
  816. {
  817. return "8.1 with some 9.0 (full-precision)";
  818. }
  819. case 90:
  820. if( bHalfPrecision )
  821. {
  822. return "9.0 (half-precision)";
  823. }
  824. else
  825. {
  826. return "9.0 (full-precision)";
  827. }
  828. default:
  829. return "UNKNOWN";
  830. }
  831. }
  832. else
  833. {
  834. switch( dxlevel )
  835. {
  836. case 60:
  837. return "gamemode - 6.0";
  838. case 70:
  839. return "gamemode - 7.0";
  840. case 80:
  841. return "gamemode - 8.0";
  842. case 81:
  843. return "gamemode - 8.1";
  844. case 82:
  845. if( bHalfPrecision )
  846. {
  847. return "gamemode - 8.1 with some 9.0 (half-precision)";
  848. }
  849. else
  850. {
  851. return "gamemode - 8.1 with some 9.0 (full-precision)";
  852. }
  853. case 90:
  854. if( bHalfPrecision )
  855. {
  856. return "gamemode - 9.0 (half-precision)";
  857. }
  858. else
  859. {
  860. return "gamemode - 9.0 (full-precision)";
  861. }
  862. default:
  863. return "gamemode";
  864. }
  865. }
  866. }
  867. const char *COM_FormatSeconds( int seconds )
  868. {
  869. static char string[64];
  870. int hours = 0;
  871. int minutes = seconds / 60;
  872. if ( minutes > 0 )
  873. {
  874. seconds -= (minutes * 60);
  875. hours = minutes / 60;
  876. if ( hours > 0 )
  877. {
  878. minutes -= (hours * 60);
  879. }
  880. }
  881. if ( hours > 0 )
  882. {
  883. Q_snprintf( string, sizeof(string), "%2i:%02i:%02i", hours, minutes, seconds );
  884. }
  885. else
  886. {
  887. Q_snprintf( string, sizeof(string), "%02i:%02i", minutes, seconds );
  888. }
  889. return string;
  890. }
  891. // Non-VarArgs version
  892. void COM_LogString( char const *pchFile, char const *pchString )
  893. {
  894. if ( !g_pFileSystem )
  895. {
  896. Assert( 0 );
  897. return;
  898. }
  899. FileHandle_t fp;
  900. const char *pfilename;
  901. if ( !pchFile )
  902. {
  903. pfilename = "hllog.txt";
  904. }
  905. else
  906. {
  907. pfilename = pchFile;
  908. }
  909. fp = g_pFileSystem->Open( pfilename, "a+t");
  910. if (fp)
  911. {
  912. g_pFileSystem->Write( pchString, strlen( pchString), fp );
  913. g_pFileSystem->Close(fp);
  914. }
  915. }
  916. void COM_Log( const char *pszFile, const char *fmt, ...)
  917. {
  918. if ( !g_pFileSystem )
  919. {
  920. Assert( 0 );
  921. return;
  922. }
  923. va_list argptr;
  924. char string[8192];
  925. va_start (argptr,fmt);
  926. Q_vsnprintf(string, sizeof( string ), fmt,argptr);
  927. va_end (argptr);
  928. COM_LogString( pszFile, string );
  929. }
  930. //-----------------------------------------------------------------------------
  931. // Purpose:
  932. // Input : *filename1 -
  933. // *filename2 -
  934. // *iCompare -
  935. // Output : int
  936. //-----------------------------------------------------------------------------
  937. int COM_CompareFileTime(const char *filename1, const char *filename2, int *iCompare)
  938. {
  939. int bRet = 0;
  940. if ( iCompare )
  941. {
  942. *iCompare = 0;
  943. }
  944. if (filename1 && filename2)
  945. {
  946. long ft1 = g_pFileSystem->GetFileTime( filename1 );
  947. long ft2 = g_pFileSystem->GetFileTime( filename2 );
  948. if ( iCompare )
  949. {
  950. *iCompare = Sys_CompareFileTime( ft1, ft2 );
  951. }
  952. bRet = 1;
  953. }
  954. return bRet;
  955. }
  956. //-----------------------------------------------------------------------------
  957. // Purpose:
  958. // Input : *szGameDir -
  959. //-----------------------------------------------------------------------------
  960. void COM_GetGameDir(char *szGameDir, int maxlen)
  961. {
  962. if (!szGameDir) return;
  963. Q_strncpy(szGameDir, com_gamedir, maxlen );
  964. }
  965. //-----------------------------------------------------------------------------
  966. // Purpose: Parse a token from a file stream
  967. // Input : *data -
  968. // *token -
  969. // Output : char
  970. //-----------------------------------------------------------------------------
  971. const char *COM_ParseFile(const char *data, char *token, int maxtoken )
  972. {
  973. const char *return_data = COM_Parse(data);
  974. Q_strncpy(token, com_token, maxtoken);
  975. return return_data;
  976. }
  977. //-----------------------------------------------------------------------------
  978. // Purpose:
  979. // Output : void COM_Init
  980. //-----------------------------------------------------------------------------
  981. void COM_Init ( void )
  982. {
  983. CharacterSetBuild( &g_BreakSet, "{}()'" );
  984. CharacterSetBuild( &g_BreakSetIncludingColons, "{}()':" );
  985. }
  986. //-----------------------------------------------------------------------------
  987. // Purpose:
  988. //-----------------------------------------------------------------------------
  989. bool COM_IsValidPath( const char *pszFilename )
  990. {
  991. if ( !pszFilename )
  992. {
  993. return false;
  994. }
  995. if ( Q_strlen( pszFilename ) <= 0 ||
  996. Q_strstr( pszFilename, "\\\\" ) || // to protect network paths
  997. Q_strstr( pszFilename, ":" ) || // to protect absolute paths
  998. Q_strstr( pszFilename, ".." ) || // to protect relative paths
  999. Q_strstr( pszFilename, "\n" ) || // CFileSystem_Stdio::FS_fopen doesn't allow this
  1000. Q_strstr( pszFilename, "\r" ) ) // CFileSystem_Stdio::FS_fopen doesn't allow this
  1001. {
  1002. return false;
  1003. }
  1004. return true;
  1005. }
  1006. //-----------------------------------------------------------------------------
  1007. // Purpose:
  1008. //-----------------------------------------------------------------------------
  1009. bool COM_IsValidLogFilename( const char *pszFilename )
  1010. {
  1011. if ( !pszFilename || !pszFilename[0] )
  1012. return false;
  1013. if ( V_stristr( pszFilename, " " ) || V_stristr( pszFilename, "\t" ) ) // don't multiple spaces or tab
  1014. return false;
  1015. const char *extension = V_strrchr( pszFilename, '.' );
  1016. if ( extension )
  1017. {
  1018. if ( Q_stricmp( extension, ".log" ) && Q_stricmp( extension, ".txt" ) ) // must use .log or .txt if an extension is specified
  1019. return false;
  1020. if ( extension == pszFilename ) // bad filename (just an extension)
  1021. return false;
  1022. }
  1023. return true;
  1024. }
  1025. //-----------------------------------------------------------------------------
  1026. unsigned int COM_GetIdealDestinationCompressionBufferSize_Snappy( unsigned int uncompressedSize )
  1027. {
  1028. // 4 for the ID, plus whatever Snappy says it would need.
  1029. return 4 + snappy::MaxCompressedLength( uncompressedSize );
  1030. }
  1031. //-----------------------------------------------------------------------------
  1032. void *COM_CompressBuffer_Snappy( const void *source, unsigned int sourceLen, unsigned int *compressedLen, unsigned int maxCompressedLen )
  1033. {
  1034. Assert( source );
  1035. Assert( compressedLen );
  1036. // Allocate a buffer big enough to hold the worst case.
  1037. unsigned nMaxCompressedSize = COM_GetIdealDestinationCompressionBufferSize_Snappy( sourceLen );
  1038. char *pCompressed = (char*)malloc( nMaxCompressedSize );
  1039. if ( pCompressed == NULL )
  1040. return NULL;
  1041. // Do the compression
  1042. *(uint32 *)pCompressed = SNAPPY_ID;
  1043. size_t compressed_length;
  1044. snappy::RawCompress( (const char *)source, sourceLen, pCompressed + sizeof(uint32), &compressed_length );
  1045. compressed_length += 4;
  1046. Assert( compressed_length <= nMaxCompressedSize );
  1047. // Check if this result is OK
  1048. if ( maxCompressedLen != 0 && compressed_length > maxCompressedLen )
  1049. {
  1050. free( pCompressed );
  1051. return NULL;
  1052. }
  1053. *compressedLen = compressed_length;
  1054. return pCompressed;
  1055. }
  1056. //-----------------------------------------------------------------------------
  1057. bool COM_BufferToBufferCompress_Snappy( void *dest, unsigned int *destLen, const void *source, unsigned int sourceLen )
  1058. {
  1059. Assert( dest );
  1060. Assert( destLen );
  1061. Assert( source );
  1062. // Check if we need to use a temporary buffer
  1063. unsigned nMaxCompressedSize = COM_GetIdealDestinationCompressionBufferSize_Snappy( sourceLen );
  1064. unsigned compressedLen = *destLen;
  1065. if ( compressedLen < nMaxCompressedSize )
  1066. {
  1067. // Yep. Use the other function to allocate the buffer of the right size and comrpess into it
  1068. void *temp = COM_CompressBuffer_Snappy( source, sourceLen, &compressedLen, compressedLen );
  1069. if ( temp == NULL )
  1070. return false;
  1071. // Copy over the data
  1072. V_memcpy( dest, temp, compressedLen );
  1073. *destLen = compressedLen;
  1074. free( temp );
  1075. return true;
  1076. }
  1077. // We have room and should be able to compress directly
  1078. *(uint32 *)dest = SNAPPY_ID;
  1079. size_t compressed_length;
  1080. snappy::RawCompress( (const char *)source, sourceLen, (char *)dest + sizeof(uint32), &compressed_length );
  1081. compressed_length += 4;
  1082. Assert( compressed_length <= nMaxCompressedSize );
  1083. *destLen = compressed_length;
  1084. return true;
  1085. }
  1086. //-----------------------------------------------------------------------------
  1087. unsigned COM_GetIdealDestinationCompressionBufferSize_LZSS( unsigned int uncompressedSize )
  1088. {
  1089. // Our LZSS compressor doesn't need any extra space because it will stop and fail
  1090. // as soon as it figures out it's unable to reduce the size of the data by more than
  1091. // 32 bytes
  1092. return uncompressedSize;
  1093. }
  1094. //-----------------------------------------------------------------------------
  1095. void *COM_CompressBuffer_LZSS( const void *source, unsigned int sourceLen, unsigned int *compressedLen, unsigned int maxCompressedLen )
  1096. {
  1097. Assert( source );
  1098. Assert( compressedLen );
  1099. CLZSS s;
  1100. unsigned int uCompressedLen = 0;
  1101. byte *pbOut = s.Compress( (const byte *)source, sourceLen, &uCompressedLen );
  1102. if ( pbOut && uCompressedLen > 0 && ( uCompressedLen <= maxCompressedLen || maxCompressedLen == 0 ) )
  1103. {
  1104. *compressedLen = uCompressedLen;
  1105. return pbOut;
  1106. }
  1107. if ( pbOut )
  1108. {
  1109. free( pbOut );
  1110. }
  1111. return NULL;
  1112. }
  1113. //-----------------------------------------------------------------------------
  1114. bool COM_BufferToBufferCompress_LZSS( void *dest, unsigned int *destLen, const void *source, unsigned int sourceLen )
  1115. {
  1116. Assert( dest );
  1117. Assert( destLen );
  1118. Assert( source );
  1119. CLZSS s;
  1120. unsigned int uCompressedLen = 0;
  1121. if ( !s.CompressNoAlloc( (const byte *)source, sourceLen, (unsigned char *)dest, &uCompressedLen ) )
  1122. return false;
  1123. *destLen = uCompressedLen;
  1124. return true;
  1125. }
  1126. //-----------------------------------------------------------------------------
  1127. int COM_GetUncompressedSize( const void *compressed, unsigned int compressedLen )
  1128. {
  1129. const lzss_header_t *pHeader = (const lzss_header_t *)compressed;
  1130. // Check for our own LZSS compressed data
  1131. if ( ( compressedLen >= sizeof(lzss_header_t) ) && pHeader->id == LZSS_ID )
  1132. return LittleLong( pHeader->actualSize );
  1133. // Check for Snappy compressed
  1134. if ( compressedLen > sizeof(pHeader->id) && pHeader->id == SNAPPY_ID )
  1135. {
  1136. size_t snappySize;
  1137. if ( snappy::GetUncompressedLength( (const char *)compressed + sizeof(pHeader->id), compressedLen-sizeof(pHeader->id), &snappySize ) )
  1138. return (int)snappySize;
  1139. }
  1140. return -1;
  1141. }
  1142. //-----------------------------------------------------------------------------
  1143. // Purpose: Generic buffer decompression from source into dest
  1144. //-----------------------------------------------------------------------------
  1145. bool COM_BufferToBufferDecompress( void *dest, unsigned int *destLen, const void *source, unsigned int sourceLen )
  1146. {
  1147. int nDecompressedSize = COM_GetUncompressedSize( source, sourceLen );
  1148. if ( nDecompressedSize >= 0 )
  1149. {
  1150. // Check buffer size
  1151. if ( (unsigned)nDecompressedSize > *destLen )
  1152. {
  1153. Warning( "NET_BufferToBufferDecompress with improperly sized dest buffer (%u in, %u needed)\n", *destLen, nDecompressedSize );
  1154. return false;
  1155. }
  1156. const lzss_header_t *pHeader = (const lzss_header_t *)source;
  1157. if ( pHeader->id == LZSS_ID )
  1158. {
  1159. CLZSS s;
  1160. int nActualDecompressedSize = s.SafeUncompress( (byte *)source, (byte *)dest, *destLen );
  1161. if ( nActualDecompressedSize != nDecompressedSize )
  1162. {
  1163. Warning( "NET_BufferToBufferDecompress: header said %d bytes would be decompressed, but we LZSS decompressed %d\n", nDecompressedSize, nActualDecompressedSize );
  1164. return false;
  1165. }
  1166. *destLen = nDecompressedSize;
  1167. return true;
  1168. }
  1169. if ( pHeader->id == SNAPPY_ID )
  1170. {
  1171. if ( !snappy::RawUncompress( (const char *)source + 4, sourceLen - 4, (char *)dest ) )
  1172. {
  1173. Warning( "NET_BufferToBufferDecompress: Snappy decompression failed\n" );
  1174. return false;
  1175. }
  1176. *destLen = nDecompressedSize;
  1177. return true;
  1178. }
  1179. // Mismatch between this routine and COM_GetUncompressedSize
  1180. AssertMsg( false, "Unknown compression type?" );
  1181. return false;
  1182. }
  1183. else
  1184. {
  1185. if ( sourceLen > *destLen )
  1186. {
  1187. Warning( "NET_BufferToBufferDecompress with improperly sized dest buffer (%u in, %u needed)\n", *destLen, sourceLen );
  1188. return false;
  1189. }
  1190. V_memcpy( dest, source, sourceLen );
  1191. *destLen = sourceLen;
  1192. }
  1193. return true;
  1194. }