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
1009 lines
19 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
// |
|
//=============================================================================// |
|
// ----------------------- |
|
// cmdlib.c |
|
// ----------------------- |
|
#include "tier0/platform.h" |
|
#ifdef IS_WINDOWS_PC |
|
#include <windows.h> |
|
#endif |
|
#include "cmdlib.h" |
|
#include <sys/types.h> |
|
#include <sys/stat.h> |
|
#include "tier1/strtools.h" |
|
#ifdef _WIN32 |
|
#include <conio.h> |
|
#endif |
|
#include "utlvector.h" |
|
#include "filesystem_helpers.h" |
|
#include "utllinkedlist.h" |
|
#include "tier0/icommandline.h" |
|
#include "KeyValues.h" |
|
#include "filesystem_tools.h" |
|
|
|
#if defined( MPI ) |
|
|
|
#include "vmpi.h" |
|
#include "vmpi_tools_shared.h" |
|
|
|
#endif |
|
|
|
|
|
#if defined( _WIN32 ) || defined( WIN32 ) |
|
#include <direct.h> |
|
#endif |
|
|
|
#if defined( _X360 ) |
|
#include "xbox/xbox_win32stubs.h" |
|
#endif |
|
|
|
#include "tier0/memdbgon.h" |
|
|
|
// set these before calling CheckParm |
|
int myargc; |
|
char **myargv; |
|
|
|
char com_token[1024]; |
|
|
|
qboolean archive; |
|
char archivedir[1024]; |
|
|
|
FileHandle_t g_pLogFile = 0; |
|
|
|
CUtlLinkedList<CleanupFn, unsigned short> g_CleanupFunctions; |
|
CUtlLinkedList<SpewHookFn, unsigned short> g_ExtraSpewHooks; |
|
|
|
bool g_bStopOnExit = false; |
|
void (*g_ExtraSpewHook)(const char*) = NULL; |
|
|
|
#if defined( _WIN32 ) || defined( WIN32 ) |
|
|
|
void CmdLib_FPrintf( FileHandle_t hFile, const char *pFormat, ... ) |
|
{ |
|
static CUtlVector<char> buf; |
|
if ( buf.Count() == 0 ) |
|
buf.SetCount( 1024 ); |
|
|
|
va_list marker; |
|
va_start( marker, pFormat ); |
|
|
|
while ( 1 ) |
|
{ |
|
int ret = Q_vsnprintf( buf.Base(), buf.Count(), pFormat, marker ); |
|
if ( ret >= 0 ) |
|
{ |
|
// Write the string. |
|
g_pFileSystem->Write( buf.Base(), ret, hFile ); |
|
|
|
break; |
|
} |
|
else |
|
{ |
|
// Make the buffer larger. |
|
int newSize = buf.Count() * 2; |
|
buf.SetCount( newSize ); |
|
if ( buf.Count() != newSize ) |
|
{ |
|
Error( "CmdLib_FPrintf: can't allocate space for text." ); |
|
} |
|
} |
|
} |
|
|
|
va_end( marker ); |
|
} |
|
|
|
char* CmdLib_FGets( char *pOut, int outSize, FileHandle_t hFile ) |
|
{ |
|
int iCur=0; |
|
for ( ; iCur < (outSize-1); iCur++ ) |
|
{ |
|
char c; |
|
if ( !g_pFileSystem->Read( &c, 1, hFile ) ) |
|
{ |
|
if ( iCur == 0 ) |
|
return NULL; |
|
else |
|
break; |
|
} |
|
|
|
pOut[iCur] = c; |
|
if ( c == '\n' ) |
|
break; |
|
|
|
if ( c == EOF ) |
|
{ |
|
if ( iCur == 0 ) |
|
return NULL; |
|
else |
|
break; |
|
} |
|
} |
|
|
|
pOut[iCur] = 0; |
|
return pOut; |
|
} |
|
|
|
#if !defined( _X360 ) |
|
#include <wincon.h> |
|
#endif |
|
|
|
// This pauses before exiting if they use -StopOnExit. Useful for debugging. |
|
class CExitStopper |
|
{ |
|
public: |
|
~CExitStopper() |
|
{ |
|
if ( g_bStopOnExit ) |
|
{ |
|
Warning( "\nPress any key to quit.\n" ); |
|
getch(); |
|
} |
|
} |
|
} g_ExitStopper; |
|
|
|
|
|
static unsigned short g_InitialColor = 0xFFFF; |
|
static unsigned short g_LastColor = 0xFFFF; |
|
static unsigned short g_BadColor = 0xFFFF; |
|
static WORD g_BackgroundFlags = 0xFFFF; |
|
static void GetInitialColors( ) |
|
{ |
|
#if !defined( _X360 ) |
|
// Get the old background attributes. |
|
CONSOLE_SCREEN_BUFFER_INFO oldInfo; |
|
GetConsoleScreenBufferInfo( GetStdHandle( STD_OUTPUT_HANDLE ), &oldInfo ); |
|
g_InitialColor = g_LastColor = oldInfo.wAttributes & (FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY); |
|
g_BackgroundFlags = oldInfo.wAttributes & (BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE|BACKGROUND_INTENSITY); |
|
|
|
g_BadColor = 0; |
|
if (g_BackgroundFlags & BACKGROUND_RED) |
|
g_BadColor |= FOREGROUND_RED; |
|
if (g_BackgroundFlags & BACKGROUND_GREEN) |
|
g_BadColor |= FOREGROUND_GREEN; |
|
if (g_BackgroundFlags & BACKGROUND_BLUE) |
|
g_BadColor |= FOREGROUND_BLUE; |
|
if (g_BackgroundFlags & BACKGROUND_INTENSITY) |
|
g_BadColor |= FOREGROUND_INTENSITY; |
|
#endif |
|
} |
|
|
|
WORD SetConsoleTextColor( int red, int green, int blue, int intensity ) |
|
{ |
|
WORD ret = g_LastColor; |
|
#if !defined( _X360 ) |
|
|
|
g_LastColor = 0; |
|
if( red ) g_LastColor |= FOREGROUND_RED; |
|
if( green ) g_LastColor |= FOREGROUND_GREEN; |
|
if( blue ) g_LastColor |= FOREGROUND_BLUE; |
|
if( intensity ) g_LastColor |= FOREGROUND_INTENSITY; |
|
|
|
// Just use the initial color if there's a match... |
|
if (g_LastColor == g_BadColor) |
|
g_LastColor = g_InitialColor; |
|
|
|
SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), g_LastColor | g_BackgroundFlags ); |
|
#endif |
|
return ret; |
|
} |
|
|
|
void RestoreConsoleTextColor( WORD color ) |
|
{ |
|
#if !defined( _X360 ) |
|
SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), color | g_BackgroundFlags ); |
|
g_LastColor = color; |
|
#endif |
|
} |
|
|
|
|
|
#if defined( CMDLIB_NODBGLIB ) |
|
|
|
// This can go away when everything is in bin. |
|
void Error( char const *pMsg, ... ) |
|
{ |
|
va_list marker; |
|
va_start( marker, pMsg ); |
|
vprintf( pMsg, marker ); |
|
va_end( marker ); |
|
|
|
exit( -1 ); |
|
} |
|
|
|
#else |
|
|
|
CRITICAL_SECTION g_SpewCS; |
|
bool g_bSpewCSInitted = false; |
|
bool g_bSuppressPrintfOutput = false; |
|
|
|
SpewRetval_t CmdLib_SpewOutputFunc( SpewType_t type, char const *pMsg ) |
|
{ |
|
// Hopefully two threads won't call this simultaneously right at the start! |
|
if ( !g_bSpewCSInitted ) |
|
{ |
|
InitializeCriticalSection( &g_SpewCS ); |
|
g_bSpewCSInitted = true; |
|
} |
|
|
|
WORD old; |
|
SpewRetval_t retVal; |
|
|
|
EnterCriticalSection( &g_SpewCS ); |
|
{ |
|
if (( type == SPEW_MESSAGE ) || (type == SPEW_LOG )) |
|
{ |
|
Color c = *GetSpewOutputColor(); |
|
if ( c.r() != 255 || c.g() != 255 || c.b() != 255 ) |
|
{ |
|
// custom color |
|
old = SetConsoleTextColor( c.r(), c.g(), c.b(), c.a() ); |
|
} |
|
else |
|
{ |
|
old = SetConsoleTextColor( 1, 1, 1, 0 ); |
|
} |
|
retVal = SPEW_CONTINUE; |
|
} |
|
else if( type == SPEW_WARNING ) |
|
{ |
|
old = SetConsoleTextColor( 1, 1, 0, 1 ); |
|
retVal = SPEW_CONTINUE; |
|
} |
|
else if( type == SPEW_ASSERT ) |
|
{ |
|
old = SetConsoleTextColor( 1, 0, 0, 1 ); |
|
retVal = SPEW_DEBUGGER; |
|
|
|
#ifdef MPI |
|
// VMPI workers don't want to bring up dialogs and suchlike. |
|
// They need to have a special function installed to handle |
|
// the exceptions and write the minidumps. |
|
// Install the function after VMPI_Init with a call: |
|
// SetupToolsMinidumpHandler( VMPI_ExceptionFilter ); |
|
if ( g_bUseMPI && !g_bMPIMaster && !Plat_IsInDebugSession() ) |
|
{ |
|
// Generating an exception and letting the |
|
// installed handler handle it |
|
::RaiseException |
|
( |
|
0, // dwExceptionCode |
|
EXCEPTION_NONCONTINUABLE, // dwExceptionFlags |
|
0, // nNumberOfArguments, |
|
NULL // const ULONG_PTR* lpArguments |
|
); |
|
|
|
// Never get here (non-continuable exception) |
|
|
|
VMPI_HandleCrash( pMsg, NULL, true ); |
|
exit( 0 ); |
|
} |
|
#endif |
|
} |
|
else if( type == SPEW_ERROR ) |
|
{ |
|
old = SetConsoleTextColor( 1, 0, 0, 1 ); |
|
retVal = SPEW_ABORT; // doesn't matter.. we exit below so we can return an errorlevel (which dbg.dll doesn't do). |
|
} |
|
else |
|
{ |
|
old = SetConsoleTextColor( 1, 1, 1, 1 ); |
|
retVal = SPEW_CONTINUE; |
|
} |
|
|
|
if ( !g_bSuppressPrintfOutput || type == SPEW_ERROR ) |
|
printf( "%s", pMsg ); |
|
|
|
OutputDebugString( pMsg ); |
|
|
|
if ( type == SPEW_ERROR ) |
|
{ |
|
printf( "\n" ); |
|
OutputDebugString( "\n" ); |
|
} |
|
|
|
if( g_pLogFile ) |
|
{ |
|
CmdLib_FPrintf( g_pLogFile, "%s", pMsg ); |
|
g_pFileSystem->Flush( g_pLogFile ); |
|
} |
|
|
|
// Dispatch to other spew hooks. |
|
FOR_EACH_LL( g_ExtraSpewHooks, i ) |
|
g_ExtraSpewHooks[i]( pMsg ); |
|
|
|
RestoreConsoleTextColor( old ); |
|
} |
|
LeaveCriticalSection( &g_SpewCS ); |
|
|
|
if ( type == SPEW_ERROR ) |
|
{ |
|
CmdLib_Exit( 1 ); |
|
} |
|
|
|
return retVal; |
|
} |
|
|
|
|
|
void InstallSpewFunction() |
|
{ |
|
setvbuf( stdout, NULL, _IONBF, 0 ); |
|
setvbuf( stderr, NULL, _IONBF, 0 ); |
|
|
|
SpewOutputFunc( CmdLib_SpewOutputFunc ); |
|
GetInitialColors(); |
|
} |
|
|
|
|
|
void InstallExtraSpewHook( SpewHookFn pFn ) |
|
{ |
|
g_ExtraSpewHooks.AddToTail( pFn ); |
|
} |
|
|
|
#if 0 |
|
void CmdLib_AllocError( unsigned long size ) |
|
{ |
|
Error( "Error trying to allocate %d bytes.\n", size ); |
|
} |
|
|
|
|
|
int CmdLib_NewHandler( size_t size ) |
|
{ |
|
CmdLib_AllocError( size ); |
|
return 0; |
|
} |
|
#endif |
|
|
|
void InstallAllocationFunctions() |
|
{ |
|
// _set_new_mode( 1 ); // so if malloc() fails, we exit. |
|
// _set_new_handler( CmdLib_NewHandler ); |
|
} |
|
|
|
void SetSpewFunctionLogFile( char const *pFilename ) |
|
{ |
|
Assert( (!g_pLogFile) ); |
|
g_pLogFile = g_pFileSystem->Open( pFilename, "a" ); |
|
|
|
Assert( g_pLogFile ); |
|
if (!g_pLogFile) |
|
Error("Can't create LogFile:\"%s\"\n", pFilename ); |
|
|
|
CmdLib_FPrintf( g_pLogFile, "\n\n\n" ); |
|
} |
|
|
|
|
|
void CloseSpewFunctionLogFile() |
|
{ |
|
if ( g_pFileSystem && g_pLogFile ) |
|
{ |
|
g_pFileSystem->Close( g_pLogFile ); |
|
g_pLogFile = FILESYSTEM_INVALID_HANDLE; |
|
} |
|
} |
|
|
|
|
|
void CmdLib_AtCleanup( CleanupFn pFn ) |
|
{ |
|
g_CleanupFunctions.AddToTail( pFn ); |
|
} |
|
|
|
|
|
void CmdLib_Cleanup() |
|
{ |
|
CloseSpewFunctionLogFile(); |
|
|
|
CmdLib_TermFileSystem(); |
|
|
|
FOR_EACH_LL( g_CleanupFunctions, i ) |
|
g_CleanupFunctions[i](); |
|
|
|
#if defined( MPI ) |
|
// Unfortunately, when you call exit(), even if you have things registered with atexit(), |
|
// threads go into a seemingly undefined state where GetExitCodeThread gives STILL_ACTIVE |
|
// and WaitForSingleObject will stall forever on the thread. Because of this, we must cleanup |
|
// everything that uses threads before exiting. |
|
VMPI_Finalize(); |
|
#endif |
|
} |
|
|
|
|
|
void CmdLib_Exit( int exitCode ) |
|
{ |
|
TerminateProcess( GetCurrentProcess(), 1 ); |
|
} |
|
|
|
|
|
|
|
#endif |
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
/* |
|
=================== |
|
ExpandWildcards |
|
|
|
Mimic unix command line expansion |
|
=================== |
|
*/ |
|
#define MAX_EX_ARGC 1024 |
|
int ex_argc; |
|
char *ex_argv[MAX_EX_ARGC]; |
|
#if defined( _WIN32 ) && !defined( _X360 ) |
|
#include "io.h" |
|
void ExpandWildcards (int *argc, char ***argv) |
|
{ |
|
struct _finddata_t fileinfo; |
|
int handle; |
|
int i; |
|
char filename[1024]; |
|
char filebase[1024]; |
|
char *path; |
|
|
|
ex_argc = 0; |
|
for (i=0 ; i<*argc ; i++) |
|
{ |
|
path = (*argv)[i]; |
|
if ( path[0] == '-' |
|
|| ( !strstr(path, "*") && !strstr(path, "?") ) ) |
|
{ |
|
ex_argv[ex_argc++] = path; |
|
continue; |
|
} |
|
|
|
handle = _findfirst (path, &fileinfo); |
|
if (handle == -1) |
|
return; |
|
|
|
Q_ExtractFilePath (path, filebase, sizeof( filebase )); |
|
|
|
do |
|
{ |
|
sprintf (filename, "%s%s", filebase, fileinfo.name); |
|
ex_argv[ex_argc++] = copystring (filename); |
|
} while (_findnext( handle, &fileinfo ) != -1); |
|
|
|
_findclose (handle); |
|
} |
|
|
|
*argc = ex_argc; |
|
*argv = ex_argv; |
|
} |
|
#else |
|
void ExpandWildcards (int *argc, char ***argv) |
|
{ |
|
} |
|
#endif |
|
|
|
|
|
// only printf if in verbose mode |
|
qboolean verbose = false; |
|
void qprintf (const char *format, ...) |
|
{ |
|
if (!verbose) |
|
return; |
|
|
|
va_list argptr; |
|
va_start (argptr,format); |
|
|
|
char str[2048]; |
|
Q_vsnprintf( str, sizeof(str), format, argptr ); |
|
|
|
#if defined( CMDLIB_NODBGLIB ) |
|
printf( "%s", str ); |
|
#else |
|
Msg( "%s", str ); |
|
#endif |
|
|
|
va_end (argptr); |
|
} |
|
|
|
|
|
// ---------------------------------------------------------------------------------------------------- // |
|
// Helpers. |
|
// ---------------------------------------------------------------------------------------------------- // |
|
|
|
static void CmdLib_getwd( char *out, int outSize ) |
|
{ |
|
#if defined( _WIN32 ) || defined( WIN32 ) |
|
_getcwd( out, outSize ); |
|
Q_strncat( out, "\\", outSize, COPY_ALL_CHARACTERS ); |
|
#else |
|
getcwd(out, outSize); |
|
strcat(out, "/"); |
|
#endif |
|
Q_FixSlashes( out ); |
|
} |
|
|
|
char *ExpandArg (char *path) |
|
{ |
|
static char full[1024]; |
|
|
|
if (path[0] != '/' && path[0] != '\\' && path[1] != ':') |
|
{ |
|
CmdLib_getwd (full, sizeof( full )); |
|
Q_strncat (full, path, sizeof( full ), COPY_ALL_CHARACTERS); |
|
} |
|
else |
|
Q_strncpy (full, path, sizeof( full )); |
|
return full; |
|
} |
|
|
|
|
|
char *ExpandPath (char *path) |
|
{ |
|
static char full[1024]; |
|
if (path[0] == '/' || path[0] == '\\' || path[1] == ':') |
|
return path; |
|
sprintf (full, "%s%s", qdir, path); |
|
return full; |
|
} |
|
|
|
|
|
|
|
char *copystring(const char *s) |
|
{ |
|
char *b; |
|
b = (char *)malloc(strlen(s)+1); |
|
strcpy (b, s); |
|
return b; |
|
} |
|
|
|
|
|
void GetHourMinuteSeconds( int nInputSeconds, int &nHours, int &nMinutes, int &nSeconds ) |
|
{ |
|
} |
|
|
|
|
|
void GetHourMinuteSecondsString( int nInputSeconds, char *pOut, int outLen ) |
|
{ |
|
int nMinutes = nInputSeconds / 60; |
|
int nSeconds = nInputSeconds - nMinutes * 60; |
|
int nHours = nMinutes / 60; |
|
nMinutes -= nHours * 60; |
|
|
|
const char *extra[2] = { "", "s" }; |
|
|
|
if ( nHours > 0 ) |
|
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] ); |
|
else if ( nMinutes > 0 ) |
|
Q_snprintf( pOut, outLen, "%d minute%s, %d second%s", nMinutes, extra[nMinutes != 1], nSeconds, extra[nSeconds != 1] ); |
|
else |
|
Q_snprintf( pOut, outLen, "%d second%s", nSeconds, extra[nSeconds != 1] ); |
|
} |
|
|
|
|
|
void Q_mkdir (char *path) |
|
{ |
|
#if defined( _WIN32 ) || defined( WIN32 ) |
|
if (_mkdir (path) != -1) |
|
return; |
|
#else |
|
if (mkdir (path, 0777) != -1) |
|
return; |
|
#endif |
|
// if (errno != EEXIST) |
|
Error ("mkdir failed %s\n", path ); |
|
} |
|
|
|
void CmdLib_InitFileSystem( const char *pFilename, int maxMemoryUsage ) |
|
{ |
|
FileSystem_Init( pFilename, maxMemoryUsage ); |
|
if ( !g_pFileSystem ) |
|
Error( "CmdLib_InitFileSystem failed." ); |
|
} |
|
|
|
void CmdLib_TermFileSystem() |
|
{ |
|
FileSystem_Term(); |
|
} |
|
|
|
CreateInterfaceFn CmdLib_GetFileSystemFactory() |
|
{ |
|
return FileSystem_GetFactory(); |
|
} |
|
|
|
|
|
/* |
|
============ |
|
FileTime |
|
|
|
returns -1 if not present |
|
============ |
|
*/ |
|
int FileTime (char *path) |
|
{ |
|
struct stat buf; |
|
|
|
if (stat (path,&buf) == -1) |
|
return -1; |
|
|
|
return buf.st_mtime; |
|
} |
|
|
|
|
|
|
|
/* |
|
============== |
|
COM_Parse |
|
|
|
Parse a token out of a string |
|
============== |
|
*/ |
|
char *COM_Parse (char *data) |
|
{ |
|
return (char*)ParseFile( data, com_token, NULL ); |
|
} |
|
|
|
|
|
/* |
|
============================================================================= |
|
|
|
MISC FUNCTIONS |
|
|
|
============================================================================= |
|
*/ |
|
|
|
|
|
/* |
|
================= |
|
CheckParm |
|
|
|
Checks for the given parameter in the program's command line arguments |
|
Returns the argument number (1 to argc-1) or 0 if not present |
|
================= |
|
*/ |
|
int CheckParm (char *check) |
|
{ |
|
int i; |
|
|
|
for (i = 1;i<myargc;i++) |
|
{ |
|
if ( !Q_strcasecmp(check, myargv[i]) ) |
|
return i; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
|
|
|
|
/* |
|
================ |
|
Q_filelength |
|
================ |
|
*/ |
|
int Q_filelength (FileHandle_t f) |
|
{ |
|
return g_pFileSystem->Size( f ); |
|
} |
|
|
|
|
|
FileHandle_t SafeOpenWrite ( const char *filename ) |
|
{ |
|
FileHandle_t f = g_pFileSystem->Open(filename, "wb"); |
|
|
|
if (!f) |
|
{ |
|
//Error ("Error opening %s: %s",filename,strerror(errno)); |
|
// BUGBUG: No way to get equivalent of errno from IFileSystem! |
|
Error ("Error opening %s! (Check for write enable)\n",filename); |
|
} |
|
|
|
return f; |
|
} |
|
|
|
#define MAX_CMDLIB_BASE_PATHS 10 |
|
static char g_pBasePaths[MAX_CMDLIB_BASE_PATHS][MAX_PATH]; |
|
static int g_NumBasePaths = 0; |
|
|
|
void CmdLib_AddBasePath( const char *pPath ) |
|
{ |
|
// printf( "CmdLib_AddBasePath( \"%s\" )\n", pPath ); |
|
if( g_NumBasePaths < MAX_CMDLIB_BASE_PATHS ) |
|
{ |
|
Q_strncpy( g_pBasePaths[g_NumBasePaths], pPath, MAX_PATH ); |
|
Q_FixSlashes( g_pBasePaths[g_NumBasePaths] ); |
|
g_NumBasePaths++; |
|
} |
|
else |
|
{ |
|
Assert( 0 ); |
|
} |
|
} |
|
|
|
bool CmdLib_HasBasePath( const char *pFileName_, int &pathLength ) |
|
{ |
|
char *pFileName = ( char * )_alloca( strlen( pFileName_ ) + 1 ); |
|
strcpy( pFileName, pFileName_ ); |
|
Q_FixSlashes( pFileName ); |
|
pathLength = 0; |
|
int i; |
|
for( i = 0; i < g_NumBasePaths; i++ ) |
|
{ |
|
// see if we can rip the base off of the filename. |
|
if( Q_strncasecmp( g_pBasePaths[i], pFileName, strlen( g_pBasePaths[i] ) ) == 0 ) |
|
{ |
|
pathLength = strlen( g_pBasePaths[i] ); |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
int CmdLib_GetNumBasePaths( void ) |
|
{ |
|
return g_NumBasePaths; |
|
} |
|
|
|
const char *CmdLib_GetBasePath( int i ) |
|
{ |
|
Assert( i >= 0 && i < g_NumBasePaths ); |
|
return g_pBasePaths[i]; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Like ExpandPath but expands the path for each base path like SafeOpenRead |
|
//----------------------------------------------------------------------------- |
|
int CmdLib_ExpandWithBasePaths( CUtlVector< CUtlString > &expandedPathList, const char *pszPath ) |
|
{ |
|
int nPathLength = 0; |
|
|
|
pszPath = ExpandPath( const_cast< char * >( pszPath ) ); // Kind of redundant but it's how CmdLib_HasBasePath needs things |
|
|
|
if ( CmdLib_HasBasePath( pszPath, nPathLength ) ) |
|
{ |
|
pszPath = pszPath + nPathLength; |
|
for ( int i = 0; i < CmdLib_GetNumBasePaths(); ++i ) |
|
{ |
|
CUtlString &expandedPath = expandedPathList[ expandedPathList.AddToTail( CmdLib_GetBasePath( i ) ) ]; |
|
expandedPath += pszPath; |
|
} |
|
} |
|
else |
|
{ |
|
expandedPathList.AddToTail( pszPath ); |
|
} |
|
|
|
return expandedPathList.Count(); |
|
} |
|
|
|
|
|
FileHandle_t SafeOpenRead( const char *filename ) |
|
{ |
|
int pathLength; |
|
FileHandle_t f = 0; |
|
if( CmdLib_HasBasePath( filename, pathLength ) ) |
|
{ |
|
filename = filename + pathLength; |
|
int i; |
|
for( i = 0; i < g_NumBasePaths; i++ ) |
|
{ |
|
char tmp[MAX_PATH]; |
|
strcpy( tmp, g_pBasePaths[i] ); |
|
strcat( tmp, filename ); |
|
f = g_pFileSystem->Open( tmp, "rb" ); |
|
if( f ) |
|
{ |
|
return f; |
|
} |
|
} |
|
Error ("Error opening %s\n",filename ); |
|
return f; |
|
} |
|
else |
|
{ |
|
f = g_pFileSystem->Open( filename, "rb" ); |
|
if ( !f ) |
|
Error ("Error opening %s",filename ); |
|
|
|
return f; |
|
} |
|
} |
|
|
|
void SafeRead( FileHandle_t f, void *buffer, int count) |
|
{ |
|
if ( g_pFileSystem->Read (buffer, count, f) != (size_t)count) |
|
Error ("File read failure"); |
|
} |
|
|
|
|
|
void SafeWrite ( FileHandle_t f, void *buffer, int count) |
|
{ |
|
if (g_pFileSystem->Write (buffer, count, f) != (size_t)count) |
|
Error ("File write failure"); |
|
} |
|
|
|
|
|
/* |
|
============== |
|
FileExists |
|
============== |
|
*/ |
|
qboolean FileExists ( const char *filename ) |
|
{ |
|
FileHandle_t hFile = g_pFileSystem->Open( filename, "rb" ); |
|
if ( hFile == FILESYSTEM_INVALID_HANDLE ) |
|
{ |
|
return false; |
|
} |
|
else |
|
{ |
|
g_pFileSystem->Close( hFile ); |
|
return true; |
|
} |
|
} |
|
|
|
/* |
|
============== |
|
LoadFile |
|
============== |
|
*/ |
|
int LoadFile ( const char *filename, void **bufferptr ) |
|
{ |
|
int length = 0; |
|
void *buffer; |
|
|
|
FileHandle_t f = SafeOpenRead (filename); |
|
if ( FILESYSTEM_INVALID_HANDLE != f ) |
|
{ |
|
length = Q_filelength (f); |
|
buffer = malloc (length+1); |
|
((char *)buffer)[length] = 0; |
|
SafeRead (f, buffer, length); |
|
g_pFileSystem->Close (f); |
|
*bufferptr = buffer; |
|
} |
|
else |
|
{ |
|
*bufferptr = NULL; |
|
} |
|
return length; |
|
} |
|
|
|
|
|
|
|
/* |
|
============== |
|
SaveFile |
|
============== |
|
*/ |
|
void SaveFile ( const char *filename, void *buffer, int count ) |
|
{ |
|
FileHandle_t f = SafeOpenWrite (filename); |
|
SafeWrite (f, buffer, count); |
|
g_pFileSystem->Close (f); |
|
} |
|
|
|
/* |
|
==================== |
|
Extract file parts |
|
==================== |
|
*/ |
|
// FIXME: should include the slash, otherwise |
|
// backing to an empty path will be wrong when appending a slash |
|
|
|
|
|
|
|
/* |
|
============== |
|
ParseNum / ParseHex |
|
============== |
|
*/ |
|
int ParseHex (char *hex) |
|
{ |
|
char *str; |
|
int num; |
|
|
|
num = 0; |
|
str = hex; |
|
|
|
while (*str) |
|
{ |
|
num <<= 4; |
|
if (*str >= '0' && *str <= '9') |
|
num += *str-'0'; |
|
else if (*str >= 'a' && *str <= 'f') |
|
num += 10 + *str-'a'; |
|
else if (*str >= 'A' && *str <= 'F') |
|
num += 10 + *str-'A'; |
|
else |
|
Error ("Bad hex number: %s",hex); |
|
str++; |
|
} |
|
|
|
return num; |
|
} |
|
|
|
|
|
int ParseNum (char *str) |
|
{ |
|
if (str[0] == '$') |
|
return ParseHex (str+1); |
|
if (str[0] == '0' && str[1] == 'x') |
|
return ParseHex (str+2); |
|
return atol (str); |
|
} |
|
|
|
/* |
|
============ |
|
CreatePath |
|
============ |
|
*/ |
|
void CreatePath (char *path) |
|
{ |
|
char *ofs, c; |
|
|
|
// strip the drive |
|
if (path[1] == ':') |
|
path += 2; |
|
|
|
for (ofs = path+1 ; *ofs ; ofs++) |
|
{ |
|
c = *ofs; |
|
if (c == '/' || c == '\\') |
|
{ // create the directory |
|
*ofs = 0; |
|
Q_mkdir (path); |
|
*ofs = c; |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Creates a path, path may already exist |
|
//----------------------------------------------------------------------------- |
|
#if defined( _WIN32 ) || defined( WIN32 ) |
|
void SafeCreatePath( char *path ) |
|
{ |
|
char *ptr; |
|
|
|
// skip past the drive path, but don't strip |
|
if ( path[1] == ':' ) |
|
{ |
|
ptr = strchr( path, '\\' ); |
|
} |
|
else |
|
{ |
|
ptr = path; |
|
} |
|
while ( ptr ) |
|
{ |
|
ptr = strchr( ptr+1, '\\' ); |
|
if ( ptr ) |
|
{ |
|
*ptr = '\0'; |
|
_mkdir( path ); |
|
*ptr = '\\'; |
|
} |
|
} |
|
} |
|
#endif |
|
|
|
/* |
|
============ |
|
QCopyFile |
|
|
|
Used to archive source files |
|
============ |
|
*/ |
|
void QCopyFile (char *from, char *to) |
|
{ |
|
void *buffer; |
|
int length; |
|
|
|
length = LoadFile (from, &buffer); |
|
CreatePath (to); |
|
SaveFile (to, buffer, length); |
|
free (buffer); |
|
} |
|
|
|
|
|
|
|
|