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.

1009 lines
19 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. // -----------------------
  9. // cmdlib.c
  10. // -----------------------
  11. #include "tier0/platform.h"
  12. #ifdef IS_WINDOWS_PC
  13. #include <windows.h>
  14. #endif
  15. #include "cmdlib.h"
  16. #include <sys/types.h>
  17. #include <sys/stat.h>
  18. #include "tier1/strtools.h"
  19. #ifdef _WIN32
  20. #include <conio.h>
  21. #endif
  22. #include "utlvector.h"
  23. #include "filesystem_helpers.h"
  24. #include "utllinkedlist.h"
  25. #include "tier0/icommandline.h"
  26. #include "KeyValues.h"
  27. #include "filesystem_tools.h"
  28. #if defined( MPI )
  29. #include "vmpi.h"
  30. #include "vmpi_tools_shared.h"
  31. #endif
  32. #if defined( _WIN32 ) || defined( WIN32 )
  33. #include <direct.h>
  34. #endif
  35. #if defined( _X360 )
  36. #include "xbox/xbox_win32stubs.h"
  37. #endif
  38. #include "tier0/memdbgon.h"
  39. // set these before calling CheckParm
  40. int myargc;
  41. char **myargv;
  42. char com_token[1024];
  43. qboolean archive;
  44. char archivedir[1024];
  45. FileHandle_t g_pLogFile = 0;
  46. CUtlLinkedList<CleanupFn, unsigned short> g_CleanupFunctions;
  47. CUtlLinkedList<SpewHookFn, unsigned short> g_ExtraSpewHooks;
  48. bool g_bStopOnExit = false;
  49. void (*g_ExtraSpewHook)(const char*) = NULL;
  50. #if defined( _WIN32 ) || defined( WIN32 )
  51. void CmdLib_FPrintf( FileHandle_t hFile, const char *pFormat, ... )
  52. {
  53. static CUtlVector<char> buf;
  54. if ( buf.Count() == 0 )
  55. buf.SetCount( 1024 );
  56. va_list marker;
  57. va_start( marker, pFormat );
  58. while ( 1 )
  59. {
  60. int ret = Q_vsnprintf( buf.Base(), buf.Count(), pFormat, marker );
  61. if ( ret >= 0 )
  62. {
  63. // Write the string.
  64. g_pFileSystem->Write( buf.Base(), ret, hFile );
  65. break;
  66. }
  67. else
  68. {
  69. // Make the buffer larger.
  70. int newSize = buf.Count() * 2;
  71. buf.SetCount( newSize );
  72. if ( buf.Count() != newSize )
  73. {
  74. Error( "CmdLib_FPrintf: can't allocate space for text." );
  75. }
  76. }
  77. }
  78. va_end( marker );
  79. }
  80. char* CmdLib_FGets( char *pOut, int outSize, FileHandle_t hFile )
  81. {
  82. int iCur=0;
  83. for ( ; iCur < (outSize-1); iCur++ )
  84. {
  85. char c;
  86. if ( !g_pFileSystem->Read( &c, 1, hFile ) )
  87. {
  88. if ( iCur == 0 )
  89. return NULL;
  90. else
  91. break;
  92. }
  93. pOut[iCur] = c;
  94. if ( c == '\n' )
  95. break;
  96. if ( c == EOF )
  97. {
  98. if ( iCur == 0 )
  99. return NULL;
  100. else
  101. break;
  102. }
  103. }
  104. pOut[iCur] = 0;
  105. return pOut;
  106. }
  107. #if !defined( _X360 )
  108. #include <wincon.h>
  109. #endif
  110. // This pauses before exiting if they use -StopOnExit. Useful for debugging.
  111. class CExitStopper
  112. {
  113. public:
  114. ~CExitStopper()
  115. {
  116. if ( g_bStopOnExit )
  117. {
  118. Warning( "\nPress any key to quit.\n" );
  119. getch();
  120. }
  121. }
  122. } g_ExitStopper;
  123. static unsigned short g_InitialColor = 0xFFFF;
  124. static unsigned short g_LastColor = 0xFFFF;
  125. static unsigned short g_BadColor = 0xFFFF;
  126. static WORD g_BackgroundFlags = 0xFFFF;
  127. static void GetInitialColors( )
  128. {
  129. #if !defined( _X360 )
  130. // Get the old background attributes.
  131. CONSOLE_SCREEN_BUFFER_INFO oldInfo;
  132. GetConsoleScreenBufferInfo( GetStdHandle( STD_OUTPUT_HANDLE ), &oldInfo );
  133. g_InitialColor = g_LastColor = oldInfo.wAttributes & (FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY);
  134. g_BackgroundFlags = oldInfo.wAttributes & (BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE|BACKGROUND_INTENSITY);
  135. g_BadColor = 0;
  136. if (g_BackgroundFlags & BACKGROUND_RED)
  137. g_BadColor |= FOREGROUND_RED;
  138. if (g_BackgroundFlags & BACKGROUND_GREEN)
  139. g_BadColor |= FOREGROUND_GREEN;
  140. if (g_BackgroundFlags & BACKGROUND_BLUE)
  141. g_BadColor |= FOREGROUND_BLUE;
  142. if (g_BackgroundFlags & BACKGROUND_INTENSITY)
  143. g_BadColor |= FOREGROUND_INTENSITY;
  144. #endif
  145. }
  146. WORD SetConsoleTextColor( int red, int green, int blue, int intensity )
  147. {
  148. WORD ret = g_LastColor;
  149. #if !defined( _X360 )
  150. g_LastColor = 0;
  151. if( red ) g_LastColor |= FOREGROUND_RED;
  152. if( green ) g_LastColor |= FOREGROUND_GREEN;
  153. if( blue ) g_LastColor |= FOREGROUND_BLUE;
  154. if( intensity ) g_LastColor |= FOREGROUND_INTENSITY;
  155. // Just use the initial color if there's a match...
  156. if (g_LastColor == g_BadColor)
  157. g_LastColor = g_InitialColor;
  158. SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), g_LastColor | g_BackgroundFlags );
  159. #endif
  160. return ret;
  161. }
  162. void RestoreConsoleTextColor( WORD color )
  163. {
  164. #if !defined( _X360 )
  165. SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), color | g_BackgroundFlags );
  166. g_LastColor = color;
  167. #endif
  168. }
  169. #if defined( CMDLIB_NODBGLIB )
  170. // This can go away when everything is in bin.
  171. void Error( char const *pMsg, ... )
  172. {
  173. va_list marker;
  174. va_start( marker, pMsg );
  175. vprintf( pMsg, marker );
  176. va_end( marker );
  177. exit( -1 );
  178. }
  179. #else
  180. CRITICAL_SECTION g_SpewCS;
  181. bool g_bSpewCSInitted = false;
  182. bool g_bSuppressPrintfOutput = false;
  183. SpewRetval_t CmdLib_SpewOutputFunc( SpewType_t type, char const *pMsg )
  184. {
  185. // Hopefully two threads won't call this simultaneously right at the start!
  186. if ( !g_bSpewCSInitted )
  187. {
  188. InitializeCriticalSection( &g_SpewCS );
  189. g_bSpewCSInitted = true;
  190. }
  191. WORD old;
  192. SpewRetval_t retVal;
  193. EnterCriticalSection( &g_SpewCS );
  194. {
  195. if (( type == SPEW_MESSAGE ) || (type == SPEW_LOG ))
  196. {
  197. Color c = *GetSpewOutputColor();
  198. if ( c.r() != 255 || c.g() != 255 || c.b() != 255 )
  199. {
  200. // custom color
  201. old = SetConsoleTextColor( c.r(), c.g(), c.b(), c.a() );
  202. }
  203. else
  204. {
  205. old = SetConsoleTextColor( 1, 1, 1, 0 );
  206. }
  207. retVal = SPEW_CONTINUE;
  208. }
  209. else if( type == SPEW_WARNING )
  210. {
  211. old = SetConsoleTextColor( 1, 1, 0, 1 );
  212. retVal = SPEW_CONTINUE;
  213. }
  214. else if( type == SPEW_ASSERT )
  215. {
  216. old = SetConsoleTextColor( 1, 0, 0, 1 );
  217. retVal = SPEW_DEBUGGER;
  218. #ifdef MPI
  219. // VMPI workers don't want to bring up dialogs and suchlike.
  220. // They need to have a special function installed to handle
  221. // the exceptions and write the minidumps.
  222. // Install the function after VMPI_Init with a call:
  223. // SetupToolsMinidumpHandler( VMPI_ExceptionFilter );
  224. if ( g_bUseMPI && !g_bMPIMaster && !Plat_IsInDebugSession() )
  225. {
  226. // Generating an exception and letting the
  227. // installed handler handle it
  228. ::RaiseException
  229. (
  230. 0, // dwExceptionCode
  231. EXCEPTION_NONCONTINUABLE, // dwExceptionFlags
  232. 0, // nNumberOfArguments,
  233. NULL // const ULONG_PTR* lpArguments
  234. );
  235. // Never get here (non-continuable exception)
  236. VMPI_HandleCrash( pMsg, NULL, true );
  237. exit( 0 );
  238. }
  239. #endif
  240. }
  241. else if( type == SPEW_ERROR )
  242. {
  243. old = SetConsoleTextColor( 1, 0, 0, 1 );
  244. retVal = SPEW_ABORT; // doesn't matter.. we exit below so we can return an errorlevel (which dbg.dll doesn't do).
  245. }
  246. else
  247. {
  248. old = SetConsoleTextColor( 1, 1, 1, 1 );
  249. retVal = SPEW_CONTINUE;
  250. }
  251. if ( !g_bSuppressPrintfOutput || type == SPEW_ERROR )
  252. printf( "%s", pMsg );
  253. OutputDebugString( pMsg );
  254. if ( type == SPEW_ERROR )
  255. {
  256. printf( "\n" );
  257. OutputDebugString( "\n" );
  258. }
  259. if( g_pLogFile )
  260. {
  261. CmdLib_FPrintf( g_pLogFile, "%s", pMsg );
  262. g_pFileSystem->Flush( g_pLogFile );
  263. }
  264. // Dispatch to other spew hooks.
  265. FOR_EACH_LL( g_ExtraSpewHooks, i )
  266. g_ExtraSpewHooks[i]( pMsg );
  267. RestoreConsoleTextColor( old );
  268. }
  269. LeaveCriticalSection( &g_SpewCS );
  270. if ( type == SPEW_ERROR )
  271. {
  272. CmdLib_Exit( 1 );
  273. }
  274. return retVal;
  275. }
  276. void InstallSpewFunction()
  277. {
  278. setvbuf( stdout, NULL, _IONBF, 0 );
  279. setvbuf( stderr, NULL, _IONBF, 0 );
  280. SpewOutputFunc( CmdLib_SpewOutputFunc );
  281. GetInitialColors();
  282. }
  283. void InstallExtraSpewHook( SpewHookFn pFn )
  284. {
  285. g_ExtraSpewHooks.AddToTail( pFn );
  286. }
  287. #if 0
  288. void CmdLib_AllocError( unsigned long size )
  289. {
  290. Error( "Error trying to allocate %d bytes.\n", size );
  291. }
  292. int CmdLib_NewHandler( size_t size )
  293. {
  294. CmdLib_AllocError( size );
  295. return 0;
  296. }
  297. #endif
  298. void InstallAllocationFunctions()
  299. {
  300. // _set_new_mode( 1 ); // so if malloc() fails, we exit.
  301. // _set_new_handler( CmdLib_NewHandler );
  302. }
  303. void SetSpewFunctionLogFile( char const *pFilename )
  304. {
  305. Assert( (!g_pLogFile) );
  306. g_pLogFile = g_pFileSystem->Open( pFilename, "a" );
  307. Assert( g_pLogFile );
  308. if (!g_pLogFile)
  309. Error("Can't create LogFile:\"%s\"\n", pFilename );
  310. CmdLib_FPrintf( g_pLogFile, "\n\n\n" );
  311. }
  312. void CloseSpewFunctionLogFile()
  313. {
  314. if ( g_pFileSystem && g_pLogFile )
  315. {
  316. g_pFileSystem->Close( g_pLogFile );
  317. g_pLogFile = FILESYSTEM_INVALID_HANDLE;
  318. }
  319. }
  320. void CmdLib_AtCleanup( CleanupFn pFn )
  321. {
  322. g_CleanupFunctions.AddToTail( pFn );
  323. }
  324. void CmdLib_Cleanup()
  325. {
  326. CloseSpewFunctionLogFile();
  327. CmdLib_TermFileSystem();
  328. FOR_EACH_LL( g_CleanupFunctions, i )
  329. g_CleanupFunctions[i]();
  330. #if defined( MPI )
  331. // Unfortunately, when you call exit(), even if you have things registered with atexit(),
  332. // threads go into a seemingly undefined state where GetExitCodeThread gives STILL_ACTIVE
  333. // and WaitForSingleObject will stall forever on the thread. Because of this, we must cleanup
  334. // everything that uses threads before exiting.
  335. VMPI_Finalize();
  336. #endif
  337. }
  338. void CmdLib_Exit( int exitCode )
  339. {
  340. TerminateProcess( GetCurrentProcess(), 1 );
  341. }
  342. #endif
  343. #endif
  344. /*
  345. ===================
  346. ExpandWildcards
  347. Mimic unix command line expansion
  348. ===================
  349. */
  350. #define MAX_EX_ARGC 1024
  351. int ex_argc;
  352. char *ex_argv[MAX_EX_ARGC];
  353. #if defined( _WIN32 ) && !defined( _X360 )
  354. #include "io.h"
  355. void ExpandWildcards (int *argc, char ***argv)
  356. {
  357. struct _finddata_t fileinfo;
  358. int handle;
  359. int i;
  360. char filename[1024];
  361. char filebase[1024];
  362. char *path;
  363. ex_argc = 0;
  364. for (i=0 ; i<*argc ; i++)
  365. {
  366. path = (*argv)[i];
  367. if ( path[0] == '-'
  368. || ( !strstr(path, "*") && !strstr(path, "?") ) )
  369. {
  370. ex_argv[ex_argc++] = path;
  371. continue;
  372. }
  373. handle = _findfirst (path, &fileinfo);
  374. if (handle == -1)
  375. return;
  376. Q_ExtractFilePath (path, filebase, sizeof( filebase ));
  377. do
  378. {
  379. sprintf (filename, "%s%s", filebase, fileinfo.name);
  380. ex_argv[ex_argc++] = copystring (filename);
  381. } while (_findnext( handle, &fileinfo ) != -1);
  382. _findclose (handle);
  383. }
  384. *argc = ex_argc;
  385. *argv = ex_argv;
  386. }
  387. #else
  388. void ExpandWildcards (int *argc, char ***argv)
  389. {
  390. }
  391. #endif
  392. // only printf if in verbose mode
  393. qboolean verbose = false;
  394. void qprintf (const char *format, ...)
  395. {
  396. if (!verbose)
  397. return;
  398. va_list argptr;
  399. va_start (argptr,format);
  400. char str[2048];
  401. Q_vsnprintf( str, sizeof(str), format, argptr );
  402. #if defined( CMDLIB_NODBGLIB )
  403. printf( "%s", str );
  404. #else
  405. Msg( "%s", str );
  406. #endif
  407. va_end (argptr);
  408. }
  409. // ---------------------------------------------------------------------------------------------------- //
  410. // Helpers.
  411. // ---------------------------------------------------------------------------------------------------- //
  412. static void CmdLib_getwd( char *out, int outSize )
  413. {
  414. #if defined( _WIN32 ) || defined( WIN32 )
  415. _getcwd( out, outSize );
  416. Q_strncat( out, "\\", outSize, COPY_ALL_CHARACTERS );
  417. #else
  418. getcwd(out, outSize);
  419. strcat(out, "/");
  420. #endif
  421. Q_FixSlashes( out );
  422. }
  423. char *ExpandArg (char *path)
  424. {
  425. static char full[1024];
  426. if (path[0] != '/' && path[0] != '\\' && path[1] != ':')
  427. {
  428. CmdLib_getwd (full, sizeof( full ));
  429. Q_strncat (full, path, sizeof( full ), COPY_ALL_CHARACTERS);
  430. }
  431. else
  432. Q_strncpy (full, path, sizeof( full ));
  433. return full;
  434. }
  435. char *ExpandPath (char *path)
  436. {
  437. static char full[1024];
  438. if (path[0] == '/' || path[0] == '\\' || path[1] == ':')
  439. return path;
  440. sprintf (full, "%s%s", qdir, path);
  441. return full;
  442. }
  443. char *copystring(const char *s)
  444. {
  445. char *b;
  446. b = (char *)malloc(strlen(s)+1);
  447. strcpy (b, s);
  448. return b;
  449. }
  450. void GetHourMinuteSeconds( int nInputSeconds, int &nHours, int &nMinutes, int &nSeconds )
  451. {
  452. }
  453. void GetHourMinuteSecondsString( int nInputSeconds, char *pOut, int outLen )
  454. {
  455. int nMinutes = nInputSeconds / 60;
  456. int nSeconds = nInputSeconds - nMinutes * 60;
  457. int nHours = nMinutes / 60;
  458. nMinutes -= nHours * 60;
  459. const char *extra[2] = { "", "s" };
  460. if ( nHours > 0 )
  461. Q_snprintf( pOut, outLen, "%d hour%s, %d minute%s, %d second%s", nHours, extra[nHours != 1], nMinutes, extra[nMinutes != 1], nSeconds, extra[nSeconds != 1] );
  462. else if ( nMinutes > 0 )
  463. Q_snprintf( pOut, outLen, "%d minute%s, %d second%s", nMinutes, extra[nMinutes != 1], nSeconds, extra[nSeconds != 1] );
  464. else
  465. Q_snprintf( pOut, outLen, "%d second%s", nSeconds, extra[nSeconds != 1] );
  466. }
  467. void Q_mkdir (char *path)
  468. {
  469. #if defined( _WIN32 ) || defined( WIN32 )
  470. if (_mkdir (path) != -1)
  471. return;
  472. #else
  473. if (mkdir (path, 0777) != -1)
  474. return;
  475. #endif
  476. // if (errno != EEXIST)
  477. Error ("mkdir failed %s\n", path );
  478. }
  479. void CmdLib_InitFileSystem( const char *pFilename, int maxMemoryUsage )
  480. {
  481. FileSystem_Init( pFilename, maxMemoryUsage );
  482. if ( !g_pFileSystem )
  483. Error( "CmdLib_InitFileSystem failed." );
  484. }
  485. void CmdLib_TermFileSystem()
  486. {
  487. FileSystem_Term();
  488. }
  489. CreateInterfaceFn CmdLib_GetFileSystemFactory()
  490. {
  491. return FileSystem_GetFactory();
  492. }
  493. /*
  494. ============
  495. FileTime
  496. returns -1 if not present
  497. ============
  498. */
  499. int FileTime (char *path)
  500. {
  501. struct stat buf;
  502. if (stat (path,&buf) == -1)
  503. return -1;
  504. return buf.st_mtime;
  505. }
  506. /*
  507. ==============
  508. COM_Parse
  509. Parse a token out of a string
  510. ==============
  511. */
  512. char *COM_Parse (char *data)
  513. {
  514. return (char*)ParseFile( data, com_token, NULL );
  515. }
  516. /*
  517. =============================================================================
  518. MISC FUNCTIONS
  519. =============================================================================
  520. */
  521. /*
  522. =================
  523. CheckParm
  524. Checks for the given parameter in the program's command line arguments
  525. Returns the argument number (1 to argc-1) or 0 if not present
  526. =================
  527. */
  528. int CheckParm (char *check)
  529. {
  530. int i;
  531. for (i = 1;i<myargc;i++)
  532. {
  533. if ( !Q_strcasecmp(check, myargv[i]) )
  534. return i;
  535. }
  536. return 0;
  537. }
  538. /*
  539. ================
  540. Q_filelength
  541. ================
  542. */
  543. int Q_filelength (FileHandle_t f)
  544. {
  545. return g_pFileSystem->Size( f );
  546. }
  547. FileHandle_t SafeOpenWrite ( const char *filename )
  548. {
  549. FileHandle_t f = g_pFileSystem->Open(filename, "wb");
  550. if (!f)
  551. {
  552. //Error ("Error opening %s: %s",filename,strerror(errno));
  553. // BUGBUG: No way to get equivalent of errno from IFileSystem!
  554. Error ("Error opening %s! (Check for write enable)\n",filename);
  555. }
  556. return f;
  557. }
  558. #define MAX_CMDLIB_BASE_PATHS 10
  559. static char g_pBasePaths[MAX_CMDLIB_BASE_PATHS][MAX_PATH];
  560. static int g_NumBasePaths = 0;
  561. void CmdLib_AddBasePath( const char *pPath )
  562. {
  563. // printf( "CmdLib_AddBasePath( \"%s\" )\n", pPath );
  564. if( g_NumBasePaths < MAX_CMDLIB_BASE_PATHS )
  565. {
  566. Q_strncpy( g_pBasePaths[g_NumBasePaths], pPath, MAX_PATH );
  567. Q_FixSlashes( g_pBasePaths[g_NumBasePaths] );
  568. g_NumBasePaths++;
  569. }
  570. else
  571. {
  572. Assert( 0 );
  573. }
  574. }
  575. bool CmdLib_HasBasePath( const char *pFileName_, int &pathLength )
  576. {
  577. char *pFileName = ( char * )_alloca( strlen( pFileName_ ) + 1 );
  578. strcpy( pFileName, pFileName_ );
  579. Q_FixSlashes( pFileName );
  580. pathLength = 0;
  581. int i;
  582. for( i = 0; i < g_NumBasePaths; i++ )
  583. {
  584. // see if we can rip the base off of the filename.
  585. if( Q_strncasecmp( g_pBasePaths[i], pFileName, strlen( g_pBasePaths[i] ) ) == 0 )
  586. {
  587. pathLength = strlen( g_pBasePaths[i] );
  588. return true;
  589. }
  590. }
  591. return false;
  592. }
  593. int CmdLib_GetNumBasePaths( void )
  594. {
  595. return g_NumBasePaths;
  596. }
  597. const char *CmdLib_GetBasePath( int i )
  598. {
  599. Assert( i >= 0 && i < g_NumBasePaths );
  600. return g_pBasePaths[i];
  601. }
  602. //-----------------------------------------------------------------------------
  603. // Like ExpandPath but expands the path for each base path like SafeOpenRead
  604. //-----------------------------------------------------------------------------
  605. int CmdLib_ExpandWithBasePaths( CUtlVector< CUtlString > &expandedPathList, const char *pszPath )
  606. {
  607. int nPathLength = 0;
  608. pszPath = ExpandPath( const_cast< char * >( pszPath ) ); // Kind of redundant but it's how CmdLib_HasBasePath needs things
  609. if ( CmdLib_HasBasePath( pszPath, nPathLength ) )
  610. {
  611. pszPath = pszPath + nPathLength;
  612. for ( int i = 0; i < CmdLib_GetNumBasePaths(); ++i )
  613. {
  614. CUtlString &expandedPath = expandedPathList[ expandedPathList.AddToTail( CmdLib_GetBasePath( i ) ) ];
  615. expandedPath += pszPath;
  616. }
  617. }
  618. else
  619. {
  620. expandedPathList.AddToTail( pszPath );
  621. }
  622. return expandedPathList.Count();
  623. }
  624. FileHandle_t SafeOpenRead( const char *filename )
  625. {
  626. int pathLength;
  627. FileHandle_t f = 0;
  628. if( CmdLib_HasBasePath( filename, pathLength ) )
  629. {
  630. filename = filename + pathLength;
  631. int i;
  632. for( i = 0; i < g_NumBasePaths; i++ )
  633. {
  634. char tmp[MAX_PATH];
  635. strcpy( tmp, g_pBasePaths[i] );
  636. strcat( tmp, filename );
  637. f = g_pFileSystem->Open( tmp, "rb" );
  638. if( f )
  639. {
  640. return f;
  641. }
  642. }
  643. Error ("Error opening %s\n",filename );
  644. return f;
  645. }
  646. else
  647. {
  648. f = g_pFileSystem->Open( filename, "rb" );
  649. if ( !f )
  650. Error ("Error opening %s",filename );
  651. return f;
  652. }
  653. }
  654. void SafeRead( FileHandle_t f, void *buffer, int count)
  655. {
  656. if ( g_pFileSystem->Read (buffer, count, f) != (size_t)count)
  657. Error ("File read failure");
  658. }
  659. void SafeWrite ( FileHandle_t f, void *buffer, int count)
  660. {
  661. if (g_pFileSystem->Write (buffer, count, f) != (size_t)count)
  662. Error ("File write failure");
  663. }
  664. /*
  665. ==============
  666. FileExists
  667. ==============
  668. */
  669. qboolean FileExists ( const char *filename )
  670. {
  671. FileHandle_t hFile = g_pFileSystem->Open( filename, "rb" );
  672. if ( hFile == FILESYSTEM_INVALID_HANDLE )
  673. {
  674. return false;
  675. }
  676. else
  677. {
  678. g_pFileSystem->Close( hFile );
  679. return true;
  680. }
  681. }
  682. /*
  683. ==============
  684. LoadFile
  685. ==============
  686. */
  687. int LoadFile ( const char *filename, void **bufferptr )
  688. {
  689. int length = 0;
  690. void *buffer;
  691. FileHandle_t f = SafeOpenRead (filename);
  692. if ( FILESYSTEM_INVALID_HANDLE != f )
  693. {
  694. length = Q_filelength (f);
  695. buffer = malloc (length+1);
  696. ((char *)buffer)[length] = 0;
  697. SafeRead (f, buffer, length);
  698. g_pFileSystem->Close (f);
  699. *bufferptr = buffer;
  700. }
  701. else
  702. {
  703. *bufferptr = NULL;
  704. }
  705. return length;
  706. }
  707. /*
  708. ==============
  709. SaveFile
  710. ==============
  711. */
  712. void SaveFile ( const char *filename, void *buffer, int count )
  713. {
  714. FileHandle_t f = SafeOpenWrite (filename);
  715. SafeWrite (f, buffer, count);
  716. g_pFileSystem->Close (f);
  717. }
  718. /*
  719. ====================
  720. Extract file parts
  721. ====================
  722. */
  723. // FIXME: should include the slash, otherwise
  724. // backing to an empty path will be wrong when appending a slash
  725. /*
  726. ==============
  727. ParseNum / ParseHex
  728. ==============
  729. */
  730. int ParseHex (char *hex)
  731. {
  732. char *str;
  733. int num;
  734. num = 0;
  735. str = hex;
  736. while (*str)
  737. {
  738. num <<= 4;
  739. if (*str >= '0' && *str <= '9')
  740. num += *str-'0';
  741. else if (*str >= 'a' && *str <= 'f')
  742. num += 10 + *str-'a';
  743. else if (*str >= 'A' && *str <= 'F')
  744. num += 10 + *str-'A';
  745. else
  746. Error ("Bad hex number: %s",hex);
  747. str++;
  748. }
  749. return num;
  750. }
  751. int ParseNum (char *str)
  752. {
  753. if (str[0] == '$')
  754. return ParseHex (str+1);
  755. if (str[0] == '0' && str[1] == 'x')
  756. return ParseHex (str+2);
  757. return atol (str);
  758. }
  759. /*
  760. ============
  761. CreatePath
  762. ============
  763. */
  764. void CreatePath (char *path)
  765. {
  766. char *ofs, c;
  767. // strip the drive
  768. if (path[1] == ':')
  769. path += 2;
  770. for (ofs = path+1 ; *ofs ; ofs++)
  771. {
  772. c = *ofs;
  773. if (c == '/' || c == '\\')
  774. { // create the directory
  775. *ofs = 0;
  776. Q_mkdir (path);
  777. *ofs = c;
  778. }
  779. }
  780. }
  781. //-----------------------------------------------------------------------------
  782. // Creates a path, path may already exist
  783. //-----------------------------------------------------------------------------
  784. #if defined( _WIN32 ) || defined( WIN32 )
  785. void SafeCreatePath( char *path )
  786. {
  787. char *ptr;
  788. // skip past the drive path, but don't strip
  789. if ( path[1] == ':' )
  790. {
  791. ptr = strchr( path, '\\' );
  792. }
  793. else
  794. {
  795. ptr = path;
  796. }
  797. while ( ptr )
  798. {
  799. ptr = strchr( ptr+1, '\\' );
  800. if ( ptr )
  801. {
  802. *ptr = '\0';
  803. _mkdir( path );
  804. *ptr = '\\';
  805. }
  806. }
  807. }
  808. #endif
  809. /*
  810. ============
  811. QCopyFile
  812. Used to archive source files
  813. ============
  814. */
  815. void QCopyFile (char *from, char *to)
  816. {
  817. void *buffer;
  818. int length;
  819. length = LoadFile (from, &buffer);
  820. CreatePath (to);
  821. SaveFile (to, buffer, length);
  822. free (buffer);
  823. }