//========= Copyright Valve Corporation, All rights reserved. ============//
//
//	SYS_UTILS.C
//
//=====================================================================================//
#include "vxconsole.h"

CHAR g_szRegistryPrefix[256];

//-----------------------------------------------------------------------------
//	Sys_SetRegistryPrefix
//
//-----------------------------------------------------------------------------
void Sys_SetRegistryPrefix( const CHAR *pPrefix )
{
	_snprintf_s( g_szRegistryPrefix, sizeof( g_szRegistryPrefix ), _TRUNCATE, pPrefix );
}

//-----------------------------------------------------------------------------
//	Sys_SplitRegistryKey
//
//-----------------------------------------------------------------------------
static BOOL Sys_SplitRegistryKey( const CHAR *key, CHAR *key0, int key0Len, CHAR *key1, int key1Len )
{
	if ( !key )
	{
		return false;
	}
	
	int len = (int)strlen( key );
	if ( !len )
	{
		return false;
	}

	int Start = -1;
	for ( int i=len-1; i>=0; i-- )
	{
		if ( key[i] == '\\' )
			break;
		else
			Start=i;
	}

	if ( Start == -1 )
		return false;
	
	_snprintf_s( key0, Start, _TRUNCATE, key );
	_snprintf_s( key1, ( len-Start )+1, _TRUNCATE, key+Start );

	return true;
}

//-----------------------------------------------------------------------------
//	Sys_SetRegistryString
//
//-----------------------------------------------------------------------------
BOOL Sys_SetRegistryString( const CHAR *keyName, const CHAR *value )
{
	HKEY	hKey;
	CHAR	key0[256];
	CHAR	key1[256];
	CHAR	keyBuff[256];
	CHAR	*key;

	strcpy_s( keyBuff, sizeof( keyBuff ), g_szRegistryPrefix );
	strcat_s( keyBuff, sizeof( keyBuff ), keyName );
	key = keyBuff;
	
	HKEY hSlot = HKEY_CURRENT_USER;
	if ( !strncmp( key, "HKEY_LOCAL_MACHINE", 18 ) )
	{
		hSlot = HKEY_LOCAL_MACHINE;
		key += 19;
	}
	else if ( !strncmp( key, "HKEY_CURRENT_USER", 17 ) )
	{
		hSlot = HKEY_CURRENT_USER;
		key += 18;
	}

	if ( !Sys_SplitRegistryKey( key, key0, sizeof( key0 ), key1, sizeof( key1 ) ) )
		return false;

	if ( RegCreateKeyEx( hSlot,key0,NULL,NULL,REG_OPTION_NON_VOLATILE, value ? KEY_WRITE : KEY_ALL_ACCESS,NULL,&hKey,NULL )!=ERROR_SUCCESS )
		return false;

	if ( RegSetValueEx( hKey, key1, NULL, REG_SZ, ( UCHAR* )value, (int)strlen( value ) + 1 ) != ERROR_SUCCESS )
	{
		RegCloseKey( hKey );
		return false;
	}

	// success
	RegCloseKey( hKey );
	return true;
}

//-----------------------------------------------------------------------------
//	Sys_GetRegistryString
//
//-----------------------------------------------------------------------------
BOOL Sys_GetRegistryString( const CHAR *keyName, CHAR *value, const CHAR* defValue, int valueLen )
{
	HKEY	hKey;
	CHAR	key0[256];
	CHAR	key1[256];
	CHAR	keyBuff[256];
	CHAR	*key;

	strcpy_s( keyBuff, sizeof( keyBuff ), g_szRegistryPrefix );
	strcat_s( keyBuff, sizeof( keyBuff ), keyName );
	key = keyBuff;

	if ( defValue )
	{
		_snprintf_s( value, valueLen, _TRUNCATE, defValue );
	}

	HKEY hSlot = HKEY_CURRENT_USER;
	if ( !strncmp( key, "HKEY_LOCAL_MACHINE", 18 ) )
	{
		hSlot = HKEY_LOCAL_MACHINE;
		key += 19;
	}
	else if ( !strncmp( key, "HKEY_CURRENT_USER", 17 ) )
	{
		hSlot = HKEY_CURRENT_USER;
		key += 18;
	}

	if ( !Sys_SplitRegistryKey( key,key0,256,key1,256 ) )
		return false;

	if ( RegOpenKeyEx( hSlot,key0,NULL,KEY_READ,&hKey )!=ERROR_SUCCESS )
		return false;

	unsigned long len=valueLen;
	if ( RegQueryValueEx( hKey,key1,NULL,NULL,( UCHAR* )value,&len )!=ERROR_SUCCESS )
	{		
		RegCloseKey( hKey );
		return false;
	}

	// success
	RegCloseKey( hKey );
	return true;
}

//-----------------------------------------------------------------------------
//	Sys_SetRegistryInteger
//
//-----------------------------------------------------------------------------
BOOL Sys_SetRegistryInteger( const CHAR *keyName, int value )
{
	HKEY	hKey;
	CHAR	key0[256];
	CHAR	key1[256];
	CHAR	keyBuff[256];
	CHAR	*key;

	strcpy_s( keyBuff, sizeof( keyBuff ), g_szRegistryPrefix );
	strcat_s( keyBuff, sizeof( keyBuff ), keyName );
	key = keyBuff;

	HKEY hSlot = HKEY_CURRENT_USER;
	if ( !strncmp( key, "HKEY_LOCAL_MACHINE", 18 ) )
	{
		hSlot = HKEY_LOCAL_MACHINE;
		key += 19;
	}
	else if ( !strncmp( key, "HKEY_CURRENT_USER", 17 ) )
	{
		hSlot = HKEY_CURRENT_USER;
		key += 18;
	}

	if ( !Sys_SplitRegistryKey( key,key0,256,key1,256 ) )
		return false;

	if ( RegCreateKeyEx( hSlot, key0, NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL )!=ERROR_SUCCESS )
		return false;
		
	if ( RegSetValueEx( hKey, key1, NULL, REG_DWORD, ( UCHAR* )&value, 4 )!=ERROR_SUCCESS )
	{
		RegCloseKey( hKey );
		return false;
	}

	// success
	RegCloseKey( hKey );
	return true;
}

//-----------------------------------------------------------------------------
//	Sys_GetRegistryInteger
//
//-----------------------------------------------------------------------------
BOOL Sys_GetRegistryInteger( const CHAR *keyName, int defValue, int &value )
{
	HKEY	hKey;
	CHAR	key0[256];
	CHAR	key1[256];
	CHAR	keyBuff[256];
	CHAR	*key;

	strcpy_s( keyBuff, sizeof( keyBuff ), g_szRegistryPrefix );
	strcat_s( keyBuff, sizeof( keyBuff ), keyName );
	key = keyBuff;

	value = defValue;

	HKEY hSlot = HKEY_CURRENT_USER;
	if ( !strncmp( key, "HKEY_LOCAL_MACHINE", 18 ) )
	{
		hSlot = HKEY_LOCAL_MACHINE;
		key += 19;
	}
	else if ( !strncmp( key, "HKEY_CURRENT_USER", 17 ) )
	{
		hSlot = HKEY_CURRENT_USER;
		key += 18;
	}

	if ( !Sys_SplitRegistryKey( key, key0, 256, key1, 256 ) )
		return false;

	if ( RegOpenKeyEx( hSlot, key0, NULL, KEY_READ, &hKey ) != ERROR_SUCCESS )
		return false;

	unsigned long len=4;
	if ( RegQueryValueEx( hKey, key1, NULL, NULL, ( UCHAR* )&value, &len ) != ERROR_SUCCESS )
	{		
		RegCloseKey( hKey );
		return false;
	}

	// success
	RegCloseKey( hKey );
	return true;
}

//-----------------------------------------------------------------------------
//	Sys_MessageBox
//
//-----------------------------------------------------------------------------
void Sys_MessageBox( const CHAR* title, const CHAR* format, ... )
{
	CHAR	msg[2048];
	va_list	argptr;

	va_start( argptr, format );
	vsprintf_s( msg, sizeof( msg ), format, argptr );
	va_end( argptr );
	
	MessageBox( NULL, msg, title, MB_OK|MB_TASKMODAL|MB_TOPMOST );
}

//-----------------------------------------------------------------------------
//	Sys_CopyString
//
//-----------------------------------------------------------------------------
CHAR* Sys_CopyString( const CHAR* str )
{
	int len = (int)strlen( str );
	CHAR *ptr = ( CHAR* )Sys_Alloc( len+1 );
	memcpy( ptr,str,len+1 );

	return ( ptr );
}

//-----------------------------------------------------------------------------
//	Sys_Alloc
//
//-----------------------------------------------------------------------------
void* Sys_Alloc( int size )
{
	int*	ptr;

	if ( !size )
	{
		Sys_Error( "Sys_Alloc(): zero size" );
	}

	size = AlignValue( size, 4 );

	// allocate fixed zero init block
	ptr = ( int* )malloc( size );
	if ( !ptr )
	{
		Sys_Error( "Sys_Alloc(): %d bytes not available",size );
	}

	V_memset( ptr, 0, size );

	return ptr;	
}

//-----------------------------------------------------------------------------
//	Sys_Free
//
//-----------------------------------------------------------------------------
void Sys_Free( void* ptr )
{
	if ( !ptr )
	{
		// already freed - easier for me, not really an error
		return;
	}

	free( ptr );
}

//-----------------------------------------------------------------------------
//	Sys_Error
//
//-----------------------------------------------------------------------------
void Sys_Error( const CHAR* format, ... )
{
	va_list		argptr;
	CHAR		msg[MAX_SYSPRINTMSG];
		
	va_start( argptr, format );
	vsprintf_s( msg, sizeof( msg ), format, argptr );
	va_end( argptr );

	MessageBox( NULL, msg, "FATAL ERROR", MB_OK|MB_ICONHAND );
}

//-----------------------------------------------------------------------------
//	Sys_LoadFile
//
//-----------------------------------------------------------------------------
int Sys_LoadFile( const CHAR* filename, void** bufferptr, bool bText )
{
	int		handle;
	long	length;
	CHAR*	buffer;

	*bufferptr = NULL;

	if ( !Sys_Exists( filename ) )
	{
		return ( -1 );
	}
		
	int openFlags = bText ? _O_TEXT : _O_BINARY;
	_sopen_s( &handle, filename, _O_RDONLY|openFlags, _SH_DENYWR, _S_IREAD );
	if ( handle == -1 )
	{
		char szError[MAX_PATH];
		strerror_s( szError, sizeof( szError ), errno );
		Sys_Error( "Sys_LoadFile(): Error opening %s: %s", filename, szError );
	}

	// allocate a buffer with an auto null terminator
	length = Sys_FileLength( handle );
	buffer = ( CHAR* )Sys_Alloc( length+1 );

	int numBytesRead = _read( handle, buffer, length );
	_close( handle );

	if ( bText )
	{
		length = numBytesRead;
	}
	else if ( length != numBytesRead )
	{
		Sys_Error( "Sys_LoadFile(): read failure" );
	}
	
	// for parsing
	buffer[length] = '\0';

	*bufferptr = ( void* )buffer;

	return ( length );
}

//-----------------------------------------------------------------------------
//	Sys_SaveFile
//
//-----------------------------------------------------------------------------
bool Sys_SaveFile( const CHAR* filename, void* buffer, long count, bool bText )
{
	int	handle;
	int	status;
	
	int openFlags = bText ? _O_TEXT : _O_BINARY;
	_sopen_s( &handle, filename, _O_RDWR|_O_CREAT|_O_TRUNC|openFlags, _SH_DENYNO, _S_IREAD|_S_IWRITE );
	if ( handle == -1 )
	{
		char szError[MAX_PATH];
		strerror_s( szError, sizeof( szError ), errno );
		Sys_Error( "Sys_SaveFile(): Error opening %s: %s", filename, szError );
		return false;
	}

	status = _write( handle, buffer, count );
	if ( status != count )
	{
		Sys_Error( "Sys_SaveFile(): write failure %d, errno=%d", status, errno );
		return false;
	}

	_close( handle );
	return true;
}

//-----------------------------------------------------------------------------
//	Sys_FileLength
//
//-----------------------------------------------------------------------------
long Sys_FileLength( const CHAR* filename, bool bText )
{
	long	length;

	if ( filename )
	{
		int handle;
		int openFlags = bText ? _O_TEXT : _O_BINARY;
		_sopen_s( &handle, filename, _O_RDONLY|openFlags, _SH_DENYWR, _S_IREAD );
		if ( handle == -1 )
		{
			// file does not exist
			return -1;
		}

		length = _lseek( handle, 0, SEEK_END );
		_close( handle );
	}
	else
	{
		return -1;
	}

	return length;
}

//-----------------------------------------------------------------------------
//	Sys_FileLength
//
//-----------------------------------------------------------------------------
long Sys_FileLength( int handle )
{
	long	pos;
	long	length;

	if ( handle != -1 )
	{
		pos = _lseek( handle, 0, SEEK_CUR );
		length = _lseek( handle, 0, SEEK_END );
		_lseek( handle, pos, SEEK_SET );
	}
	else
	{
		return -1;
	}

	return length;
}

//-----------------------------------------------------------------------------
//	Sys_NormalizePath
//
//-----------------------------------------------------------------------------
void Sys_NormalizePath( CHAR* path, bool forceToLower )
{
	int	i;
	int	srclen;

	srclen = (int)strlen( path );
	for ( i=0; i<srclen; i++ )
	{
		if ( path[i] == '/' )
			path[i] = '\\';
		else if ( forceToLower && ( path[i] >= 'A' && path[i] <= 'Z' ) )
			path[i] = path[i] - 'A' + 'a';
	}
}

//-----------------------------------------------------------------------------
//	Sys_AddFileSeperator
//
//-----------------------------------------------------------------------------
void Sys_AddFileSeperator( CHAR* path, int pathLen )
{
	int	srclen;

	if ( !path[0] )
	{
		strcpy_s( path, pathLen, ".\\" );
		return;
	}

	srclen = (int)strlen( path );
	if ( path[srclen-1] == '\\' )
	{
		return;
	}

	strcat_s( path, pathLen, "\\" );
}

//-----------------------------------------------------------------------------
//	Sys_StripFilename
//
//	Removes filename from path.
//-----------------------------------------------------------------------------
void Sys_StripFilename( const CHAR* inpath, CHAR* outpath, int outPathLen )
{
	int	length;

	strcpy_s( outpath, outPathLen, inpath );

	length = (int)strlen( outpath )-1;
	while ( ( length > 0 ) && ( outpath[length] != '\\' ) && ( outpath[length] != '/' ) && ( outpath[length] != ':' ) )
		length--;

	// leave possible seperator
	if ( !length )
		outpath[0] = '\0';
	else		
		outpath[length+1] = '\0';
}

//-----------------------------------------------------------------------------
//	Sys_StripExtension
//
//	Removes extension from path.
//-----------------------------------------------------------------------------
void Sys_StripExtension( const CHAR* inpath, CHAR* outpath, int outPathLen )
{
	int	length;

	strcpy_s( outpath, outPathLen, inpath );

	length = (int)strlen( outpath )-1;
	while ( length > 0 && outpath[length] != '.' )
	{
		length--;
	}

	if ( length && outpath[length] == '.' )
	{
		outpath[length] = '\0';
	}
}

//-----------------------------------------------------------------------------
//	Sys_StripPath
//
//	Removes path from full path.
//-----------------------------------------------------------------------------
void Sys_StripPath( const CHAR* inpath, CHAR* outpath, int outPathLen )
{
	const CHAR*	src;

	src = inpath + strlen( inpath );
	while ( ( src != inpath ) && ( *( src-1 ) != '\\' ) && ( *( src-1 ) != '/' ) && ( *( src-1 ) != ':' ) )
	{
		src--;
	}

	strcpy_s( outpath, outPathLen, src );
}

//-----------------------------------------------------------------------------
//	Sys_GetExtension
//
//	Gets any extension from the full path.
//-----------------------------------------------------------------------------
void Sys_GetExtension( const CHAR* inpath, CHAR* outpath, int outPathLen )
{
	const CHAR*	src;

	src = inpath + strlen( inpath ) - 1;

	// back up until a . or the start
	while ( src != inpath && *( src-1 ) != '.' )
	{
		src--;
	}

	if ( src == inpath )
	{
		*outpath = '\0';	// no extension
		return;
	}

	strcpy_s( outpath, outPathLen, src );
}

//-----------------------------------------------------------------------------
//	Sys_AddExtension
//
//	Adds extension to end of path.
//-----------------------------------------------------------------------------
void Sys_AddExtension( const CHAR* extension, CHAR* outpath, int outPathLen )
{
	CHAR*	src;
	
	if ( outpath[0] )
	{
		src = outpath + strlen( outpath ) - 1;
		while ( ( src != outpath ) && ( *src != '\\' ) && ( *src != '/' ) )
		{
			if ( *src == '.' )
				return;
			src--;
		}
	}

	strcat_s( outpath, outPathLen, extension );
}

//-----------------------------------------------------------------------------
//	Sys_TempFilename
//
//	Make a temporary filename at specified path.
//-----------------------------------------------------------------------------
void Sys_TempFilename( CHAR* temppath, int tempPathLen )
{
	CHAR*	ptr;
	
	ptr = _tempnam( "c:\\", "tmp" );
	strcpy_s( temppath, tempPathLen, ptr );
	free( ptr );
}

//-----------------------------------------------------------------------------
//	Sys_Exists
//
//	Returns TRUE if file exists.
//-----------------------------------------------------------------------------
BOOL Sys_Exists( const CHAR* filename )
{
	FILE*	test;

	fopen_s( &test, filename, "rb" );
	if ( test == NULL )
	{
		return false;
	}

	fclose( test );

	return true;
}

//-----------------------------------------------------------------------------
//	Sys_SkipWhitespace
//
//-----------------------------------------------------------------------------
CHAR* Sys_SkipWhitespace( CHAR *data, BOOL *hasNewLines, int* numlines ) 
{
	int c;

	while( ( c = *data ) <= ' ' ) 
	{
		if ( c == '\n' ) 
		{
			if ( numlines )
				( *numlines )++;

			if ( hasNewLines )
				*hasNewLines = true;
		}
		else if ( !c )
			return ( NULL );

		data++;
	}

	return ( data );
}

//-----------------------------------------------------------------------------
//	Sys_GetToken
//
//-----------------------------------------------------------------------------
CHAR* Sys_GetToken( CHAR** dataptr, BOOL allowLineBreaks, int* numlines )
{
	CHAR		c;
	int			len;
	BOOL		hasNewLines;
	CHAR*		data;
	static CHAR	token[MAX_SYSTOKENCHARS];

	if ( numlines )
		*numlines = 0;

	c           = 0;
	data        = *dataptr;
	len         = 0;
	token[0]    = 0;
	hasNewLines = false;

	// make sure incoming data is valid
	if ( !data )
	{
		*dataptr = NULL;
		return ( token );
	}

	for ( ;; )
	{
		// skip whitespace
		data = Sys_SkipWhitespace( data,&hasNewLines,numlines );
		if ( !data )
		{
			*dataptr = NULL;
			return ( token );
		}
		
		if ( hasNewLines && !allowLineBreaks )
		{
			*dataptr = data;
			return ( token );
		}

		c = *data;

		// skip double slash comments
		if ( c == '/' && data[1] == '/' )
		{
			data += 2;
			while ( *data && *data != '\n' )
				data++;
		}
		// skip /* */ comments
		else if ( c =='/' && data[1] == '*' ) 
		{
			data += 2;
			while ( *data && ( *data != '*' || data[1] != '/' ) ) 
				data++;

			if ( *data ) 
				data += 2;
		}
		else
			break;
	}

	// handle quoted strings
	if ( c == '\"' )
	{
		data++;
		for ( ;; )
		{
			c = *data++;
			if ( c == '\"' || !c )
			{
				token[len] = 0;
				*dataptr = ( CHAR* )data;
				return ( token );
			}
			if ( len < MAX_SYSTOKENCHARS )
				token[len++] = c;
		}
	}

	// parse a regular word
	do
	{
		if ( len < MAX_SYSTOKENCHARS )
			token[len++] = c;

		data++;
		c = *data;
		if ( c == '\n' && numlines )
			( *numlines )++;
	} while ( c > ' ' );

	if ( len >= MAX_SYSTOKENCHARS ) 
		len = 0;

	token[len] = '\0';
	*dataptr   = ( CHAR* ) data;
	
	return ( token );
}

//-----------------------------------------------------------------------------
//	Sys_SkipBracedSection
//
//	The next token should be an open brace.
//	Skips until a matching close brace is found.
//	Internal brace depths are properly skipped.
//-----------------------------------------------------------------------------
void Sys_SkipBracedSection( CHAR** dataptr, int* numlines ) 
{
	CHAR*	token;
	int	depth;

	depth = 0;
	do 
	{
		token = Sys_GetToken( dataptr, true, numlines );
		if ( token[1] == '\0' ) 
		{
			if ( token[0] == '{' )
				depth++;
			else if ( token[0] == '}' )
				depth--;
		}
	}
	while( depth && *dataptr );
}

//-----------------------------------------------------------------------------
//	Sys_SkipRestOfLine
//
//-----------------------------------------------------------------------------
void Sys_SkipRestOfLine( CHAR** dataptr, int* numlines ) 
{
	CHAR*	p;
	int	c;

	p = *dataptr;
	while ( ( c = *p++ ) != '\0' ) 
	{
		if ( c == '\n' ) 
		{
			if ( numlines )
				( *numlines )++;
			break;
		}
	}
	*dataptr = p;
}

//-----------------------------------------------------------------------------
//	Sys_FileTime
//
//	Returns a file's last write time
//-----------------------------------------------------------------------------
BOOL Sys_FileTime( CHAR* filename, FILETIME* time )
{
	HANDLE		hFile; 
 
	hFile = CreateFile( 
				filename,
                GENERIC_READ,
                FILE_SHARE_READ,
                NULL,
                OPEN_EXISTING,
                FILE_ATTRIBUTE_NORMAL, 
                NULL ); 
 
	if ( hFile == INVALID_HANDLE_VALUE ) 
		return ( false );

    // Retrieve the file times for the file.
    if ( !GetFileTime( hFile, NULL, NULL, time ) )
	{
		CloseHandle( hFile );
		return ( false );
	}

	CloseHandle( hFile );
    return ( true );
}

//-----------------------------------------------------------------------------
//	Sys_GetSystemTime
//
//	Current time marker in milliseconds
//-----------------------------------------------------------------------------
DWORD Sys_GetSystemTime( void )
{
	LARGE_INTEGER	qwTime;
	LARGE_INTEGER	qwTicksPerSec;
	float			msecsPerTick;

	// Get the frequency of the timer
	QueryPerformanceFrequency( &qwTicksPerSec );
	msecsPerTick = 1000.0f/( FLOAT )qwTicksPerSec.QuadPart;

	QueryPerformanceCounter( &qwTime );
	return ( ( DWORD )( msecsPerTick * ( FLOAT )qwTime.QuadPart ) );
}

//-----------------------------------------------------------------------------
//	Sys_ColorScale
//
//-----------------------------------------------------------------------------
COLORREF Sys_ColorScale( COLORREF color, float scale )
{
	int r;
	int	g;
	int	b;

	r = color & 0xFF;
	g = ( color >> 8 ) & 0xFF;
	b = ( color >> 16 ) & 0xFF;

	r = ( int )( ( float )r * scale );
	g = ( int )( ( float )g * scale );
	b = ( int )( ( float )b * scale );

	if ( r > 255 )
		r = 255;
	if ( g > 255 )
		g = 255;
	if ( b > 255 )
		b = 255;

	color = RGB( r, g, b );
	return ( color );
}

//-----------------------------------------------------------------------------
//	Sys_IsWildcardMatch
//
//	See if a string matches a wildcard specification that uses * or ?
//-----------------------------------------------------------------------------
bool Sys_IsWildcardMatch( const CHAR *wildcardString, const CHAR *stringToCheck, bool caseSensitive )
{
	CHAR wcChar;
	CHAR strChar;

	if ( !_stricmp( wildcardString, "*.*" ) || !_stricmp( wildcardString, "*" ) )
	{
		// matches everything
		return true;
	}

	// use the starMatchesZero variable to determine whether an asterisk
	// matches zero or more characters ( TRUE ) or one or more characters
	// ( FALSE )
	bool starMatchesZero = true;

	for ( ;; )
	{
		strChar = *stringToCheck;
		if ( !strChar )
		{
			break;
		}

		wcChar = *wildcardString;
		if ( !wcChar )
		{
			break;
		}

		// we only want to advance the pointers if we successfully assigned
		// both of our char variables, so we'll do it here rather than in the
		// loop condition itself
		*stringToCheck++;
		*wildcardString++;

		// if this isn't a case-sensitive match, make both chars uppercase
		// ( thanks to David John Fielder ( Konan ) at http://innuendo.ev.ca
		// for pointing out an error here in the original code )
		if ( !caseSensitive )
		{
			wcChar = (CHAR)toupper( wcChar );
			strChar = (CHAR)toupper( strChar );
		}

		// check the wcChar against our wildcard list
		switch ( wcChar )
		{
			// an asterisk matches zero or more characters
			case '*' :
				// do a recursive call against the rest of the string,
				// until we've either found a match or the string has
				// ended
				if ( starMatchesZero )
					*stringToCheck--;

				while ( *stringToCheck )
				{
					if ( Sys_IsWildcardMatch( wildcardString, stringToCheck++, caseSensitive ) )
						return true;
				}

				break;

			// a question mark matches any single character
			case '?' :
				break;

			// if we fell through, we want an exact match
			default :
				if ( wcChar != strChar )
					return false;
				break;
		}
	}

	// if we have any asterisks left at the end of the wildcard string, we can
	// advance past them if starMatchesZero is TRUE ( so "blah*" will match "blah" )
	while ( ( *wildcardString ) && ( starMatchesZero ) )
	{
		if ( *wildcardString == '*' )
			wildcardString++;
		else
			break;
	}
	
	// if we got to the end but there's still stuff left in either of our strings,
	// return false; otherwise, we have a match
	if ( ( *stringToCheck ) || ( *wildcardString ) )
		return false;
	else
		return true;
}

//-----------------------------------------------------------------------------
//	Sys_NumberToCommaString
//
//	Add commas to number
//-----------------------------------------------------------------------------
char *Sys_NumberToCommaString( __int64 number, char *buffer, int bufferSize )
{
	char	temp[256];
	char	temp2[256];
	int		inLen;
	char	*inPtr;
	char	*outPtr;
	int		i;

	sprintf_s( temp, sizeof( temp ), "%I64d", number );

	// build string backwards
	inLen  = (int)strlen( temp );
	inPtr  = temp+inLen-1;
	outPtr = temp2;
	while ( inLen > 0 )
	{
		for ( i=0; i<3 && inLen > 0; i++, inLen-- )
		{
			*outPtr++ = *inPtr--;
		}
		if ( inLen > 0 )
			*outPtr++ = ',';
	}
	*outPtr++ = '\0';

	// reverse string
	inLen  = (int)strlen( temp2 );
	inPtr  = temp2;
	outPtr = temp;
	for ( i=inLen-1; i>=0; i-- )
	{
		*outPtr++ = inPtr[i];
	}
	*outPtr++ = '\0';

	_snprintf_s( buffer, bufferSize, _TRUNCATE, temp );

	return buffer;
}

//-----------------------------------------------------------------------------
//	Sys_CreatePath
//
//-----------------------------------------------------------------------------
void Sys_CreatePath( const char* pInPath )
{
	char*	ptr;
	char	dirPath[MAX_PATH];

	// prime and skip to first seperator
	strcpy_s( dirPath, sizeof( dirPath ), pInPath );

	if ( dirPath[0] == '\\' && dirPath[1] == '\\' )
	{
		ptr = strchr( dirPath+1, '\\' );
	}
	else
	{
		ptr = strchr( dirPath, '\\' );
	}

	while ( ptr )
	{		
		ptr = strchr( ptr+1, '\\' );
		if ( ptr )
		{
			*ptr = '\0';
			CreateDirectory( dirPath, NULL );
			*ptr = '\\';
		}
	}
}