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.
1062 lines
21 KiB
1062 lines
21 KiB
//========= Copyright © 1996-2005, 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
|
|
|
|
// set these before calling CheckParm
|
|
int myargc;
|
|
char **myargv;
|
|
|
|
int newdirs = 0;
|
|
|
|
char com_token[ 1024 ];
|
|
|
|
qboolean archive;
|
|
char archivedir[ 1024 ];
|
|
|
|
|
|
|
|
CUtlLinkedList<CleanupFn, unsigned short> g_CleanupFunctions;
|
|
|
|
bool g_bStopOnExit = false;
|
|
|
|
#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
|
|
|
|
bool g_bSuppressPrintfOutput = false;
|
|
|
|
void CCmdLibStandardLoggingListener::Log( const LoggingContext_t *pContext, const tchar *pMessage )
|
|
{
|
|
if ( ( pContext->m_Flags & LCF_DO_NOT_ECHO ) != 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
WORD oldColor;
|
|
Color spewColor = pContext->m_Color;
|
|
if ( spewColor == UNSPECIFIED_LOGGING_COLOR )
|
|
{
|
|
switch ( pContext->m_Severity )
|
|
{
|
|
case LS_MESSAGE:
|
|
spewColor = Color( 255, 255, 255, 0 );
|
|
break;
|
|
|
|
case LS_WARNING:
|
|
spewColor = Color( 255, 255, 0, 255 );
|
|
break;
|
|
|
|
case LS_ERROR:
|
|
case LS_ASSERT:
|
|
spewColor = Color( 255, 0, 0, 255 );
|
|
break;
|
|
}
|
|
}
|
|
oldColor = SetConsoleTextColor( spewColor.r(), spewColor.g(), spewColor.b(), spewColor.a() );
|
|
|
|
#ifdef MPI
|
|
if ( pContext->m_Severity == LS_ASSERT )
|
|
{
|
|
// 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( pMessage, 0, NULL, true );
|
|
exit( 0 );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if ( !g_bSuppressPrintfOutput || pContext->m_Severity == LS_ERROR )
|
|
{
|
|
printf( "%s", pMessage );
|
|
}
|
|
|
|
OutputDebugString( pMessage );
|
|
|
|
if ( pContext->m_Severity == LS_ERROR )
|
|
{
|
|
if ( !g_bSuppressPrintfOutput )
|
|
{
|
|
printf( "\n" );
|
|
}
|
|
OutputDebugString( "\n" );
|
|
}
|
|
|
|
RestoreConsoleTextColor( oldColor );
|
|
}
|
|
|
|
CCmdLibFileLoggingListener::CCmdLibFileLoggingListener() : m_pLogFile( FILESYSTEM_INVALID_HANDLE ) { }
|
|
|
|
void CCmdLibFileLoggingListener::Log( const LoggingContext_t *pContext, const tchar *pMessage )
|
|
{
|
|
if( m_pLogFile != FILESYSTEM_INVALID_HANDLE && ( pContext->m_Flags & LCF_CONSOLE_ONLY ) == 0 )
|
|
{
|
|
CmdLib_FPrintf( m_pLogFile, "%s", pMessage );
|
|
g_pFileSystem->Flush( m_pLogFile );
|
|
}
|
|
}
|
|
|
|
void CCmdLibFileLoggingListener::Open( char const *pFilename )
|
|
{
|
|
Assert( m_pLogFile == FILESYSTEM_INVALID_HANDLE );
|
|
m_pLogFile = g_pFileSystem->Open( pFilename, "a" );
|
|
|
|
Assert( m_pLogFile != FILESYSTEM_INVALID_HANDLE );
|
|
if ( !m_pLogFile )
|
|
{
|
|
Error( "Can't create LogFile:\"%s\"\n", pFilename );
|
|
}
|
|
|
|
CmdLib_FPrintf( m_pLogFile, "\n\n\n" );
|
|
}
|
|
|
|
|
|
void CCmdLibFileLoggingListener::Close()
|
|
{
|
|
if ( g_pFileSystem && m_pLogFile != FILESYSTEM_INVALID_HANDLE )
|
|
{
|
|
g_pFileSystem->Close( m_pLogFile );
|
|
m_pLogFile = FILESYSTEM_INVALID_HANDLE;
|
|
}
|
|
}
|
|
|
|
CCmdLibStandardLoggingListener g_CmdLibOutputLoggingListener;
|
|
CCmdLibFileLoggingListener g_CmdLibFileLoggingListener;
|
|
bool g_bInstalledSpewFunction = false;
|
|
|
|
void InstallSpewFunction()
|
|
{
|
|
Assert( !g_bInstalledSpewFunction );
|
|
if ( !g_bInstalledSpewFunction )
|
|
{
|
|
g_bInstalledSpewFunction = true;
|
|
setvbuf( stdout, NULL, _IONBF, 0 );
|
|
setvbuf( stderr, NULL, _IONBF, 0 );
|
|
|
|
LoggingSystem_PushLoggingState();
|
|
LoggingSystem_RegisterLoggingListener( &g_CmdLibOutputLoggingListener );
|
|
LoggingSystem_RegisterLoggingListener( &g_CmdLibFileLoggingListener );
|
|
GetInitialColors();
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
void InstallAllocationFunctions()
|
|
{
|
|
_set_new_mode( 1 ); // so if malloc() fails, we exit.
|
|
_set_new_handler( CmdLib_NewHandler );
|
|
}
|
|
#endif
|
|
|
|
void CmdLib_AtCleanup( CleanupFn pFn )
|
|
{
|
|
g_CleanupFunctions.AddToTail( pFn );
|
|
}
|
|
|
|
|
|
void CmdLib_Cleanup()
|
|
{
|
|
if ( g_bInstalledSpewFunction )
|
|
{
|
|
LoggingSystem_PopLoggingState();
|
|
}
|
|
|
|
g_CmdLibFileLoggingListener.Close();
|
|
|
|
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
|
|
}
|
|
|
|
|
|
|
|
#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
|
|
{
|
|
V_sprintf_safe( 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( char *format, ... )
|
|
{
|
|
if ( !verbose )
|
|
return;
|
|
|
|
va_list argptr;
|
|
va_start( argptr, format );
|
|
|
|
char str[ 2048 ];
|
|
V_vsprintf_safe( 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
|
|
getwd( out );
|
|
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 ) );
|
|
V_strcat_safe( full, path, COPY_ALL_CHARACTERS );
|
|
}
|
|
else
|
|
{
|
|
V_strcpy_safe( full, path );
|
|
}
|
|
return full;
|
|
}
|
|
|
|
|
|
char *ExpandPath (char *path)
|
|
{
|
|
static char full[ 1024 ];
|
|
if ( path[ 0 ] == '/' || path[ 0 ] == '\\' || path[ 1 ] == ':')
|
|
return path;
|
|
V_sprintf_safe( full, "%s%s", qdir, path );
|
|
return full;
|
|
}
|
|
|
|
|
|
|
|
char *copystring(const char *s)
|
|
{
|
|
char *b;
|
|
b = ( char * )malloc( strlen( s ) + 1 );
|
|
V_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;
|
|
|
|
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 20
|
|
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 )
|
|
{
|
|
V_strcpy_safe( g_pBasePaths[ g_NumBasePaths ], pPath );
|
|
Q_FixSlashes( g_pBasePaths[ g_NumBasePaths ] );
|
|
g_NumBasePaths++;
|
|
}
|
|
else
|
|
{
|
|
Assert( 0 );
|
|
}
|
|
}
|
|
|
|
|
|
void CmdLib_AddNewSearchPath( const char *pPath )
|
|
{
|
|
static int g_nAdditionalDirectoryCount = 0;
|
|
static char s_addedRelativeDirs[ MAX_CMDLIB_BASE_PATHS ][ MAX_PATH ];
|
|
static int s_originalNumBasePaths = 0;
|
|
static char s_originalBasePaths[ MAX_CMDLIB_BASE_PATHS ][ MAX_PATH ];
|
|
|
|
if ( g_nAdditionalDirectoryCount == 0 ) // first call:
|
|
{
|
|
// remember all original paths
|
|
s_originalNumBasePaths = g_NumBasePaths;
|
|
for ( int nOriginalBasePath = 0; nOriginalBasePath < s_originalNumBasePaths; nOriginalBasePath++ )
|
|
{
|
|
V_strcpy_safe( s_originalBasePaths[nOriginalBasePath], g_pBasePaths[nOriginalBasePath] );
|
|
}
|
|
}
|
|
|
|
if( g_nAdditionalDirectoryCount < MAX_CMDLIB_BASE_PATHS )
|
|
{
|
|
V_strcpy_safe( s_addedRelativeDirs[g_nAdditionalDirectoryCount], pPath );
|
|
Q_FixSlashes( s_addedRelativeDirs[g_nAdditionalDirectoryCount] );
|
|
g_nAdditionalDirectoryCount++;
|
|
}
|
|
else
|
|
{
|
|
Assert( 0 );
|
|
}
|
|
|
|
//update the original base paths with the new number
|
|
// we'll produce a cross-product of the set of original paths and the new relative paths
|
|
g_NumBasePaths = s_originalNumBasePaths + ( s_originalNumBasePaths * g_nAdditionalDirectoryCount );
|
|
if ( g_NumBasePaths > MAX_CMDLIB_BASE_PATHS )
|
|
{
|
|
Error( "You have too many search paths, let SteveK know about this\n");
|
|
}
|
|
|
|
//make an array of all the new search directories and copy it back into g_pBasePaths
|
|
int nGeneratedPaths = 0;
|
|
for ( int nOriginalBasePath = 0; nOriginalBasePath < s_originalNumBasePaths; nOriginalBasePath++ )
|
|
{
|
|
for ( int nAdditionalDir = 0; nAdditionalDir < g_nAdditionalDirectoryCount; nAdditionalDir++ )
|
|
{
|
|
//add in the new search dirs here
|
|
V_strcpy_safe( g_pBasePaths[ nGeneratedPaths ], s_originalBasePaths[ nOriginalBasePath ] );
|
|
V_strcat_safe( g_pBasePaths[ nGeneratedPaths ], s_addedRelativeDirs[ nAdditionalDir ], sizeof( s_addedRelativeDirs[ nAdditionalDir ] ) );
|
|
nGeneratedPaths++;
|
|
}
|
|
//we still need the original base path, but insert it after the newly added ones
|
|
V_strcpy_safe( g_pBasePaths[ nGeneratedPaths ], s_originalBasePaths[ nOriginalBasePath ] );
|
|
nGeneratedPaths++;
|
|
}
|
|
}
|
|
|
|
|
|
bool CmdLib_HasBasePath( const char *pFileName_, int &pathLength )
|
|
{
|
|
char *pFileName = ( char * )stackalloc( strlen( pFileName_ ) + 1 );
|
|
V_strcpy( pFileName, pFileName_ );
|
|
Q_FixSlashes( pFileName );
|
|
pathLength = 0;
|
|
for( int i = 0; i < g_NumBasePaths; i++ )
|
|
{
|
|
// see if we can rip the base off of the filename.
|
|
if( Q_strncasecmp( g_pBasePaths[ i ], pFileName, Q_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 ];
|
|
}
|
|
|
|
FileHandle_t SafeOpenRead( const char *filename )
|
|
{
|
|
int pathLength;
|
|
FileHandle_t f = 0;
|
|
if( CmdLib_HasBasePath( filename, pathLength ) )
|
|
{
|
|
filename = filename + pathLength;
|
|
char tmp[ MAX_PATH ];
|
|
for( int i = 0; i < g_NumBasePaths; i++ )
|
|
{
|
|
V_strcpy_safe( tmp, g_pBasePaths[ i ] );
|
|
V_strcat_safe( 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 );
|
|
}
|
|
|
|
|
|
|