1187 lines
25 KiB
C++
1187 lines
25 KiB
C++
//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//
|
|
//=============================================================================//
|
|
|
|
#include "host.h"
|
|
#include <ctype.h>
|
|
#include "draw.h"
|
|
#include "zone.h"
|
|
#include "sys.h"
|
|
#include <edict.h>
|
|
#include <coordsize.h>
|
|
#include <characterset.h>
|
|
#include <bitbuf.h>
|
|
#include "common.h"
|
|
#include "traceinit.h"
|
|
#include <filesystem.h>
|
|
#include "filesystem_engine.h"
|
|
#include <convar.h>
|
|
#include "gl_matsysiface.h"
|
|
#include "filesystem_init.h"
|
|
#include <materialsystem/imaterialsystemhardwareconfig.h>
|
|
#include <tier0/icommandline.h>
|
|
#include <vstdlib/random.h>
|
|
#include "sys_dll.h"
|
|
#include "datacache/idatacache.h"
|
|
#include "tier1/keyvalues.h"
|
|
#ifndef DEDICATED
|
|
#include "vgui_baseui_interface.h"
|
|
#include "vgui/ISystem.h"
|
|
#include "vgui_controls/Controls.h"
|
|
#endif
|
|
#include "matchmaking/imatchframework.h"
|
|
#include "tier2/tier2.h"
|
|
#include "cl_steamauth.h"
|
|
#ifdef _X360
|
|
#include "xbox/xbox_launch.h"
|
|
#elif defined(_PS3)
|
|
#include "tls_ps3.h"
|
|
#include "ps3_pathinfo.h"
|
|
#endif
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
// Things in other C files.
|
|
#define MAX_LOG_DIRECTORIES 10000
|
|
|
|
bool com_ignorecolons = false;
|
|
|
|
// wordbreak parsing set
|
|
static characterset_t g_BreakSet, g_BreakSetIncludingColons;
|
|
|
|
#define COM_TOKEN_MAX_LENGTH 1024
|
|
char com_token[COM_TOKEN_MAX_LENGTH];
|
|
unsigned char com_character;
|
|
|
|
/*
|
|
All of Quake's data access is through a hierarchical file system, but the contents of
|
|
the file system can be transparently merged from several sources.
|
|
|
|
The "base directory" is the path to the directory holding the quake.exe and all
|
|
game directories. The sys_* files pass this to host_init in engineparms->basedir.
|
|
This can be overridden with the "-basedir" command line parm to allow code
|
|
debugging in a different directory. The base directory is
|
|
only used during filesystem initialization.
|
|
|
|
The "game directory" is the first tree on the search path and directory
|
|
that all generated files (savegames, screenshots, demos, config files) will
|
|
be saved to. This can be overridden with the "-game" command line parameter.
|
|
The game directory can never be changed while quake is executing.
|
|
This is a precacution against having a malicious server instruct clients
|
|
to write files over areas they shouldn't.
|
|
|
|
The "cache directory" is only used during development to save network bandwidth,
|
|
especially over ISDN / T1 lines. If there is a cache directory
|
|
specified, when a file is found by the normal search path, it will be mirrored
|
|
into the cache directory, then opened there.
|
|
|
|
FIXME:
|
|
The file "parms.txt" will be read out of the game directory and appended to the
|
|
current command line arguments to allow different games to initialize startup
|
|
parms differently. This could be used to add a "-sspeed 22050" for the high
|
|
quality sound edition. Because they are added at the end, they will not override
|
|
an explicit setting on the original command line.
|
|
*/
|
|
|
|
/*
|
|
==============================
|
|
COM_ExplainDisconnection
|
|
|
|
==============================
|
|
*/
|
|
void COM_ExplainDisconnection( bool bPrint, const char *fmt, ... )
|
|
{
|
|
va_list argptr;
|
|
char szString[1024];
|
|
|
|
va_start ( argptr, fmt );
|
|
Q_vsnprintf( szString, sizeof( szString ), fmt,argptr );
|
|
va_end ( argptr );
|
|
|
|
if ( !IsX360() )
|
|
{
|
|
Q_strncpy( gszDisconnectReason, szString, 256 );
|
|
gfExtendedError = true;
|
|
}
|
|
|
|
if ( bPrint )
|
|
{
|
|
ConMsg( "%s\n", gszDisconnectReason );
|
|
}
|
|
|
|
char const *szNotificationReason = szString;
|
|
if ( char const *szActualDisconnectReason = StringAfterPrefix( szString, "Disconnect: " ) )
|
|
szNotificationReason = szActualDisconnectReason;
|
|
|
|
g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
|
|
"OnEngineDisconnectReason", "reason", szNotificationReason ) );
|
|
}
|
|
|
|
/*
|
|
==============================
|
|
COM_ExtendedExplainDisconnection
|
|
|
|
==============================
|
|
*/
|
|
void COM_ExtendedExplainDisconnection( bool bPrint, const char *fmt, ... )
|
|
{
|
|
if ( !IsX360() )
|
|
{
|
|
va_list argptr;
|
|
char string[1024];
|
|
|
|
va_start (argptr, fmt);
|
|
Q_vsnprintf(string, sizeof( string ), fmt,argptr);
|
|
va_end (argptr);
|
|
|
|
Q_strncpy( gszExtendedDisconnectReason, string, 256 );
|
|
}
|
|
|
|
if ( bPrint )
|
|
{
|
|
ConMsg( "%s\n", gszExtendedDisconnectReason );
|
|
}
|
|
|
|
g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
|
|
"OnEngineDisconnectReason", "reason", "" ) );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
COM_ReadChar
|
|
|
|
Reads a single code point, turning unsupported
|
|
UTF-8 characters into question marks
|
|
==============
|
|
*/
|
|
|
|
const char *COM_ReadChar(const char *data)
|
|
{
|
|
if ( !data || !*data )
|
|
return NULL;
|
|
|
|
com_character = *data++;
|
|
return data;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
COM_Parse
|
|
|
|
Parse a token out of a string
|
|
==============
|
|
*/
|
|
const char *COM_Parse (const char *data)
|
|
{
|
|
unsigned char c;
|
|
int len;
|
|
characterset_t *breaks;
|
|
|
|
breaks = &g_BreakSetIncludingColons;
|
|
if ( com_ignorecolons )
|
|
breaks = &g_BreakSet;
|
|
|
|
len = 0;
|
|
com_token[0] = 0;
|
|
|
|
if (!data)
|
|
return NULL;
|
|
|
|
// skip whitespace
|
|
skipwhite:
|
|
while ( (c = *data) <= ' ')
|
|
{
|
|
if (c == 0)
|
|
return NULL; // end of file;
|
|
data++;
|
|
}
|
|
|
|
// skip // comments
|
|
if (c=='/' && data[1] == '/')
|
|
{
|
|
while (*data && *data != '\n')
|
|
data++;
|
|
goto skipwhite;
|
|
}
|
|
|
|
|
|
// handle quoted strings specially
|
|
if (c == '\"')
|
|
{
|
|
data++;
|
|
while (1)
|
|
{
|
|
c = *data++;
|
|
if (c=='\"' || !c)
|
|
{
|
|
com_token[len] = 0;
|
|
return data;
|
|
}
|
|
com_token[len] = c;
|
|
len++;
|
|
}
|
|
}
|
|
|
|
// parse single characters
|
|
if ( IN_CHARACTERSET( *breaks, c ) )
|
|
{
|
|
com_token[len] = c;
|
|
len++;
|
|
com_token[len] = 0;
|
|
return data+1;
|
|
}
|
|
|
|
// parse a regular word
|
|
do
|
|
{
|
|
com_token[len] = c;
|
|
data++;
|
|
len++;
|
|
c = *data;
|
|
if ( IN_CHARACTERSET( *breaks, c ) )
|
|
break;
|
|
} while (c>32);
|
|
|
|
com_token[len] = 0;
|
|
return data;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
COM_AddNoise
|
|
|
|
Changes n random bits in a data block
|
|
==============
|
|
*/
|
|
void COM_AddNoise( unsigned char *data, int length, int number )
|
|
{
|
|
for ( int i = 0; i < number; i++ )
|
|
{
|
|
int randomByte = RandomInt( 0, length-1 );
|
|
int randomBit = RandomInt( 0, 7 );
|
|
|
|
// get original data
|
|
unsigned char dataByte = data[randomByte];
|
|
|
|
// flip bit
|
|
if ( dataByte & randomBit )
|
|
{
|
|
dataByte &= ~randomBit;
|
|
}
|
|
else
|
|
{
|
|
dataByte |= randomBit;
|
|
}
|
|
|
|
// write back
|
|
data[randomByte] = dataByte;
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
|
|
COM_Parse_Line
|
|
|
|
Parse a line out of a string
|
|
==============
|
|
*/
|
|
|
|
#pragma warning( disable : 4706 )
|
|
|
|
const char *COM_ParseLine (const char *data)
|
|
{
|
|
int len = 0;
|
|
|
|
while( data = COM_ReadChar( data ) )
|
|
{
|
|
if ( com_character < ' ' && com_character != '\t' )
|
|
break;
|
|
|
|
com_token[len++] = com_character;
|
|
|
|
if ( len == COM_TOKEN_MAX_LENGTH-1 )
|
|
break;
|
|
}
|
|
|
|
com_token[len] = 0;
|
|
|
|
if ( len == COM_TOKEN_MAX_LENGTH-1 )
|
|
{
|
|
// if we stopped the line because it was too long, then
|
|
// skip to the actual line end
|
|
|
|
while( data = COM_ReadChar( data ) )
|
|
{
|
|
if ( com_character < ' ' && com_character != '\t' )
|
|
break;
|
|
}
|
|
}
|
|
|
|
// now eat control chars at the end of the line
|
|
const char *nextChar;
|
|
|
|
while( nextChar = COM_ReadChar( data ) )
|
|
{
|
|
if ( com_character >= ' ' )
|
|
return data;
|
|
else
|
|
data = nextChar;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#pragma warning( default : 4706 )
|
|
|
|
/*
|
|
==============
|
|
COM_TokenWaiting
|
|
|
|
Returns 1 if additional data is waiting to be processed on this line
|
|
==============
|
|
*/
|
|
int COM_TokenWaiting( const char *buffer )
|
|
{
|
|
const char *p;
|
|
|
|
p = buffer;
|
|
while ( *p && *p!='\n')
|
|
{
|
|
if ( !V_isspace( *p ) || V_isalnum( *p ) )
|
|
return 1;
|
|
|
|
p++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
============
|
|
va
|
|
|
|
does a varargs printf into a temp buffer, so I don't need to have
|
|
varargs versions of all text functions.
|
|
============
|
|
*/
|
|
char *va( const char *format, ... )
|
|
{
|
|
va_list argptr;
|
|
static char string[8][512];
|
|
static int curstring = 0;
|
|
|
|
curstring = ( curstring + 1 ) % 8;
|
|
|
|
va_start (argptr, format);
|
|
Q_vsnprintf( string[curstring], sizeof( string[curstring] ), format, argptr );
|
|
va_end (argptr);
|
|
|
|
return string[curstring];
|
|
}
|
|
|
|
/*
|
|
============
|
|
vstr
|
|
|
|
prints a vector into a temporary string
|
|
bufffer.
|
|
============
|
|
*/
|
|
const char *vstr(Vector& v)
|
|
{
|
|
static int idx = 0;
|
|
static char string[16][1024];
|
|
|
|
idx++;
|
|
idx &= 15;
|
|
|
|
Q_snprintf(string[idx], sizeof( string[idx] ), "%.2f %.2f %.2f", v[0], v[1], v[2]);
|
|
return string[idx];
|
|
}
|
|
|
|
char com_basedir[MAX_OSPATH];
|
|
char com_gamedir[MAX_OSPATH];
|
|
|
|
/*
|
|
==================
|
|
CL_CheckGameDirectory
|
|
|
|
Client side game directory change.
|
|
==================
|
|
*/
|
|
bool COM_CheckGameDirectory( const char *gamedir )
|
|
{
|
|
// Switch game directories if needed, or abort if it's not good.
|
|
char szGD[ MAX_OSPATH ];
|
|
|
|
if ( !gamedir || !gamedir[0] )
|
|
{
|
|
ConMsg( "Server didn't specify a gamedir, assuming no change\n" );
|
|
return true;
|
|
}
|
|
|
|
// Rip out the current gamedir.
|
|
Q_FileBase( com_gamedir, szGD, sizeof( szGD ) );
|
|
|
|
if ( Q_stricmp( szGD, gamedir ) )
|
|
{
|
|
// We know szGD and gamedir aren't the same, but as long as one is 'portal2' and the other is 'portal_sixense2', go ahead and connect
|
|
bool sixense_vs_not_sixense = false;
|
|
if( (!Q_stricmp( szGD, "portal2" ) || !Q_stricmp( szGD, "portal2_sixense" )) && (!Q_stricmp( gamedir, "portal2" ) || !Q_stricmp( gamedir, "portal2_sixense" )) )
|
|
{
|
|
sixense_vs_not_sixense = true;
|
|
}
|
|
|
|
if( !sixense_vs_not_sixense )
|
|
{
|
|
// Changing game directories without restarting is not permitted any more
|
|
ConMsg( "COM_CheckGameDirectory: game directories don't match (%s / %s)\n", szGD, gamedir );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Finds the file in the search path.
|
|
// Input : *filename -
|
|
// *file -
|
|
// Output : int
|
|
//-----------------------------------------------------------------------------
|
|
int COM_FindFile( const char *filename, FileHandle_t *file )
|
|
{
|
|
Assert( file );
|
|
int filesize = -1;
|
|
*file = g_pFileSystem->Open( filename, "rb" );
|
|
if ( *file )
|
|
{
|
|
filesize = g_pFileSystem->Size( *file );
|
|
}
|
|
return filesize;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *filename -
|
|
// *file -
|
|
// Output : int
|
|
//-----------------------------------------------------------------------------
|
|
int COM_OpenFile( const char *filename, FileHandle_t *file )
|
|
{
|
|
return COM_FindFile( (char *)filename, file );
|
|
}
|
|
|
|
/*
|
|
============
|
|
COM_WriteFile
|
|
|
|
The filename will be prefixed by the current game directory
|
|
============
|
|
*/
|
|
void COM_WriteFile (const char *filename, void *data, int len)
|
|
{
|
|
FileHandle_t handle;
|
|
|
|
int nameLen = strlen( filename ) + 2;
|
|
char *pName = ( char * )stackalloc( nameLen );
|
|
|
|
Q_snprintf( pName, nameLen, "%s", filename);
|
|
|
|
Q_FixSlashes( pName );
|
|
COM_CreatePath( pName );
|
|
|
|
handle = g_pFileSystem->Open( pName, "wb" );
|
|
if ( !handle )
|
|
{
|
|
Warning ("COM_WriteFile: failed on %s\n", pName);
|
|
return;
|
|
}
|
|
|
|
g_pFileSystem->Write( data, len, handle );
|
|
g_pFileSystem->Close( handle );
|
|
}
|
|
|
|
/*
|
|
============
|
|
COM_CreatePath
|
|
|
|
Only used for CopyFile
|
|
============
|
|
*/
|
|
void COM_CreatePath (const char *path)
|
|
{
|
|
char temppath[512];
|
|
Q_strncpy( temppath, path, sizeof(temppath) );
|
|
|
|
for (char *ofs = temppath+1 ; *ofs ; ofs++)
|
|
{
|
|
if (*ofs == '/' || *ofs == '\\')
|
|
{ // create the directory
|
|
char old = *ofs;
|
|
*ofs = 0;
|
|
Sys_mkdir (temppath);
|
|
*ofs = old;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===========
|
|
COM_CopyFile
|
|
|
|
Copies a file over from the net to the local cache, creating any directories
|
|
needed. This is for the convenience of developers using ISDN from home.
|
|
===========
|
|
*/
|
|
bool COM_CopyFile ( const char *netpath, const char *cachepath)
|
|
{
|
|
if ( IsX360() )
|
|
return false;
|
|
|
|
int remaining, count;
|
|
char buf[4096];
|
|
FileHandle_t in, out;
|
|
|
|
in = g_pFileSystem->Open( netpath, "rb" );
|
|
|
|
Assert( in );
|
|
|
|
if ( in == FILESYSTEM_INVALID_HANDLE )
|
|
return false;
|
|
|
|
// create directories up to the cache file
|
|
COM_CreatePath (cachepath);
|
|
|
|
out = g_pFileSystem->Open( cachepath, "wb", "MOD" );
|
|
|
|
Assert( out );
|
|
|
|
if ( out == FILESYSTEM_INVALID_HANDLE )
|
|
{
|
|
g_pFileSystem->Close( in );
|
|
return false;
|
|
}
|
|
|
|
remaining = g_pFileSystem->Size( in );
|
|
while ( remaining > 0 )
|
|
{
|
|
if (remaining < sizeof(buf))
|
|
{
|
|
count = remaining;
|
|
}
|
|
else
|
|
{
|
|
count = sizeof(buf);
|
|
}
|
|
g_pFileSystem->Read( buf, count, in );
|
|
g_pFileSystem->Write( buf, count, out );
|
|
remaining -= count;
|
|
}
|
|
|
|
g_pFileSystem->Close( in );
|
|
g_pFileSystem->Close( out );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
===========
|
|
COM_ExpandFilename
|
|
|
|
Finds the file in the search path, copies over the name with the full path name.
|
|
This doesn't search in the pak file.
|
|
===========
|
|
*/
|
|
int COM_ExpandFilename( char *filename, int maxlength )
|
|
{
|
|
char expanded[MAX_OSPATH];
|
|
|
|
if ( g_pFileSystem->GetLocalPath( filename, expanded, sizeof(expanded) ) != NULL )
|
|
{
|
|
Q_strncpy( filename, expanded, maxlength );
|
|
return 1;
|
|
}
|
|
|
|
if ( filename && filename[0] != '*' )
|
|
{
|
|
Warning ("COM_ExpandFilename: can't find %s\n", filename);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
===========
|
|
COM_FileSize
|
|
|
|
Returns the size of the file only.
|
|
===========
|
|
*/
|
|
int COM_FileSize (const char *filename)
|
|
{
|
|
return g_pFileSystem->Size(filename);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Close file handle
|
|
// Input : hFile -
|
|
//-----------------------------------------------------------------------------
|
|
void COM_CloseFile( FileHandle_t hFile )
|
|
{
|
|
g_pFileSystem->Close( hFile );
|
|
}
|
|
|
|
|
|
/*
|
|
============
|
|
COM_LoadFile
|
|
|
|
Filename are reletive to the quake directory.
|
|
Allways appends a 0 byte.
|
|
============
|
|
*/
|
|
cache_user_t *loadcache;
|
|
byte *loadbuf;
|
|
int loadsize;
|
|
byte *COM_LoadFile (const char *path, int usehunk, int *pLength)
|
|
{
|
|
FileHandle_t hFile;
|
|
byte *buf = NULL;
|
|
char base[128];
|
|
int len;
|
|
|
|
if (pLength)
|
|
{
|
|
*pLength = 0;
|
|
}
|
|
|
|
// look for it in the filesystem or pack files
|
|
len = COM_OpenFile( path, &hFile );
|
|
if ( !hFile )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// Extract the filename base name for hunk tag
|
|
Q_FileBase( path, base, sizeof( base ) );
|
|
|
|
unsigned bufSize = len + 1;
|
|
if ( IsX360() )
|
|
{
|
|
bufSize = g_pFileSystem->GetOptimalReadSize( hFile, bufSize ); // align to sector
|
|
}
|
|
|
|
switch ( usehunk )
|
|
{
|
|
case 1:
|
|
buf = (byte *)Hunk_AllocName (bufSize, base);
|
|
break;
|
|
case 2:
|
|
AssertMsg( 0, "Temp alloc no longer supported\n" );
|
|
break;
|
|
case 3:
|
|
AssertMsg( 0, "Cache alloc no longer supported\n" );
|
|
break;
|
|
case 4:
|
|
{
|
|
if (len+1 > loadsize)
|
|
buf = (byte *)malloc(bufSize);
|
|
else
|
|
buf = loadbuf;
|
|
}
|
|
break;
|
|
case 5:
|
|
buf = (byte *)malloc(bufSize); // YWB: FIXME, this is evil.
|
|
break;
|
|
default:
|
|
Sys_Error ("COM_LoadFile: bad usehunk");
|
|
}
|
|
|
|
if ( !buf )
|
|
{
|
|
Sys_Error ("COM_LoadFile: not enough space for %s", path);
|
|
COM_CloseFile(hFile); // exit here to prevent fault on oom (kdb)
|
|
return NULL;
|
|
}
|
|
|
|
g_pFileSystem->ReadEx( buf, bufSize, len, hFile );
|
|
COM_CloseFile( hFile );
|
|
|
|
((byte *)buf)[ len ] = 0;
|
|
|
|
if ( pLength )
|
|
{
|
|
*pLength = len;
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
COM_CopyFileChunk
|
|
|
|
===============
|
|
*/
|
|
void COM_CopyFileChunk( FileHandle_t dst, FileHandle_t src, int nSize )
|
|
{
|
|
int copysize = nSize;
|
|
char copybuf[COM_COPY_CHUNK_SIZE];
|
|
|
|
while (copysize > COM_COPY_CHUNK_SIZE)
|
|
{
|
|
g_pFileSystem->Read ( copybuf, COM_COPY_CHUNK_SIZE, src );
|
|
g_pFileSystem->Write( copybuf, COM_COPY_CHUNK_SIZE, dst );
|
|
copysize -= COM_COPY_CHUNK_SIZE;
|
|
}
|
|
|
|
g_pFileSystem->Read ( copybuf, copysize, src );
|
|
g_pFileSystem->Write( copybuf, copysize, dst );
|
|
|
|
g_pFileSystem->Flush ( src );
|
|
g_pFileSystem->Flush ( dst );
|
|
}
|
|
|
|
// uses malloc if larger than bufsize
|
|
byte *COM_LoadStackFile (const char *path, void *buffer, int bufsize, int& filesize )
|
|
{
|
|
byte *buf;
|
|
loadbuf = (byte *)buffer;
|
|
loadsize = bufsize;
|
|
buf = COM_LoadFile (path, 4, &filesize );
|
|
return buf;
|
|
}
|
|
|
|
void COM_ShutdownFileSystem( void )
|
|
{
|
|
}
|
|
|
|
/*
|
|
================
|
|
COM_Shutdown
|
|
|
|
Remove the searchpaths
|
|
================
|
|
*/
|
|
void COM_Shutdown( void )
|
|
{
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: allocates memory and copys source text
|
|
// Input : *in -
|
|
// Output : char *CopyString
|
|
//-----------------------------------------------------------------------------
|
|
char *COM_StringCopy(const char *in)
|
|
{
|
|
int len = Q_strlen(in)+1;
|
|
char *out = (char *)new char[ len ];
|
|
Q_strncpy (out, in, len );
|
|
return out;
|
|
}
|
|
|
|
void COM_StringFree(const char *in)
|
|
{
|
|
delete [] in;
|
|
}
|
|
|
|
|
|
void COM_SetupLogDir( const char *mapname )
|
|
{
|
|
#if defined( _CERT )
|
|
return;
|
|
#endif
|
|
|
|
char gameDir[MAX_OSPATH];
|
|
COM_GetGameDir( gameDir, sizeof( gameDir ) );
|
|
|
|
// Blat out the all directories in the LOGDIR path
|
|
g_pFileSystem->RemoveSearchPath( NULL, "LOGDIR" );
|
|
|
|
// set the log directory
|
|
if ( mapname && CommandLine()->FindParm("-uselogdir") )
|
|
{
|
|
int i;
|
|
char sRelativeLogDir[MAX_PATH];
|
|
for ( i = 0; i < MAX_LOG_DIRECTORIES; i++ )
|
|
{
|
|
Q_snprintf( sRelativeLogDir, sizeof( sRelativeLogDir ), "logs/%s/%04i", mapname, i );
|
|
if ( !g_pFileSystem->IsDirectory( sRelativeLogDir, "GAME" ) )
|
|
break;
|
|
}
|
|
|
|
// Loop at max
|
|
if ( i == MAX_LOG_DIRECTORIES )
|
|
{
|
|
i = 0;
|
|
Q_snprintf( sRelativeLogDir, sizeof( sRelativeLogDir ), "logs/%s/%04i", mapname, i );
|
|
}
|
|
|
|
// Make sure the directories we need exist.
|
|
g_pFileSystem->CreateDirHierarchy( sRelativeLogDir, "GAME" );
|
|
|
|
{
|
|
static bool pathsetup = false;
|
|
|
|
if ( !pathsetup )
|
|
{
|
|
pathsetup = true;
|
|
|
|
// Set the search path
|
|
char sLogDir[MAX_PATH];
|
|
Q_snprintf( sLogDir, sizeof( sLogDir ), "%s/%s", gameDir, sRelativeLogDir );
|
|
g_pFileSystem->AddSearchPath( sLogDir, "LOGDIR" );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Default to the base game directory for logs.
|
|
g_pFileSystem->AddSearchPath( gameDir, "LOGDIR" );
|
|
}
|
|
|
|
// no reason to have this as part of search path fallthrough
|
|
// callers must explicitly request
|
|
g_pFileSystem->MarkPathIDByRequestOnly( "LOGDIR", true );
|
|
}
|
|
|
|
/*
|
|
================
|
|
COM_GetModDirectory - return the final directory in the game dir (i.e "cstrike", "hl2", rather than c:\blah\cstrike )
|
|
================
|
|
*/
|
|
const char *COM_GetModDirectory()
|
|
{
|
|
static char modDir[MAX_PATH];
|
|
if ( Q_strlen( modDir ) == 0 )
|
|
{
|
|
const char *gamedir = CommandLine()->ParmValue("-game", CommandLine()->ParmValue( "-defaultgamedir", "hl2" ) );
|
|
Q_strncpy( modDir, gamedir, sizeof(modDir) );
|
|
if ( strchr( modDir, '/' ) || strchr( modDir, '\\' ) )
|
|
{
|
|
Q_StripLastDir( modDir, sizeof(modDir) );
|
|
int dirlen = Q_strlen( modDir );
|
|
Q_strncpy( modDir, gamedir + dirlen, sizeof(modDir) - dirlen );
|
|
}
|
|
}
|
|
|
|
return modDir;
|
|
}
|
|
|
|
/*
|
|
================
|
|
COM_InitFilesystem
|
|
================
|
|
*/
|
|
void COM_InitFilesystem( const char *pFullModPath )
|
|
{
|
|
CFSSearchPathsInit initInfo;
|
|
|
|
#ifndef DEDICATED
|
|
char language[128];
|
|
|
|
// Fallback to English
|
|
V_strncpy( language, "english", sizeof( language ) );
|
|
|
|
if ( IsPC() )
|
|
{
|
|
#if !defined( NO_STEAM )
|
|
if ( CommandLine()->CheckParm( "-language" ) )
|
|
{
|
|
Q_strncpy( language, CommandLine()->ParmValue( "-language", "english"), sizeof( language ) );
|
|
}
|
|
else
|
|
{
|
|
// get Steam client language
|
|
memset( language, 0, sizeof( language ) );
|
|
if ( Steam3Client().SteamApps() )
|
|
{
|
|
// just follow the language steam wants you to be
|
|
const char *lang = Steam3Client().SteamApps()->GetCurrentGameLanguage();
|
|
if ( lang && Q_strlen(lang) )
|
|
{
|
|
Q_strncpy( language, lang, sizeof(language) );
|
|
}
|
|
else
|
|
Q_strncpy( language, "english", sizeof(language) );
|
|
}
|
|
else
|
|
{
|
|
Q_strncpy( language, "english", sizeof(language) );
|
|
}
|
|
}
|
|
#endif // NO_STEAM
|
|
}
|
|
|
|
#if defined( _GAMECONSOLE )
|
|
if ( XBX_IsAudioLocalized() )
|
|
{
|
|
// allow non-english audio localization for gameconsole configured language
|
|
V_strncpy( language, XBX_GetLanguageString(), sizeof( language ) );
|
|
}
|
|
#endif
|
|
|
|
if ( ( Q_strlen( language ) > 0 ) && ( Q_stricmp( language, "english" ) != 0 ) )
|
|
{
|
|
initInfo.m_pLanguage = language;
|
|
}
|
|
#endif
|
|
|
|
initInfo.m_pFileSystem = g_pFileSystem;
|
|
#if !defined(_PS3)
|
|
initInfo.m_pDirectoryName = pFullModPath;
|
|
#else
|
|
char ps3NeedsAbsoluteModPath[256];
|
|
|
|
#ifdef HDD_BOOT
|
|
snprintf( ps3NeedsAbsoluteModPath, 256, "%s", pFullModPath );
|
|
#else
|
|
snprintf( ps3NeedsAbsoluteModPath, 256, "%s/%s", g_pPS3PathInfo->GameImagePath(), pFullModPath );
|
|
#endif
|
|
|
|
initInfo.m_pDirectoryName = ps3NeedsAbsoluteModPath;
|
|
#endif
|
|
if ( !initInfo.m_pDirectoryName )
|
|
{
|
|
initInfo.m_pDirectoryName = GetCurrentGame();
|
|
}
|
|
|
|
// Load gameinfo.txt and setup all the search paths, just like the tools do.
|
|
FileSystem_LoadSearchPaths( initInfo );
|
|
|
|
// The mod path becomes com_gamedir.
|
|
Q_MakeAbsolutePath( com_gamedir, sizeof( com_gamedir ), initInfo.m_ModPath );
|
|
|
|
// Set com_basedir.
|
|
Q_strncpy ( com_basedir, GetBaseDirectory(), sizeof( com_basedir ) ); // the "root" directory where hl2.exe is
|
|
Q_strlower( com_basedir );
|
|
Q_FixSlashes( com_basedir );
|
|
|
|
#ifndef DEDICATED
|
|
EngineVGui()->SetVGUIDirectories();
|
|
#endif
|
|
|
|
// Set LOGDIR to be something reasonable
|
|
COM_SetupLogDir( NULL );
|
|
}
|
|
|
|
const char *COM_DXLevelToString( int dxlevel )
|
|
{
|
|
bool bHalfPrecision = false;
|
|
|
|
const char *pShaderDLLName = g_pMaterialSystemHardwareConfig->GetShaderDLLName();
|
|
if( pShaderDLLName && Q_stristr( pShaderDLLName, "nvfx" ) )
|
|
{
|
|
bHalfPrecision = true;
|
|
}
|
|
|
|
switch( dxlevel )
|
|
{
|
|
case 60:
|
|
return "gamemode - 6.0";
|
|
case 70:
|
|
return "gamemode - 7.0";
|
|
case 80:
|
|
return "gamemode - 8.0";
|
|
case 81:
|
|
return "gamemode - 8.1";
|
|
case 82:
|
|
if( bHalfPrecision )
|
|
{
|
|
return "gamemode - 8.1 with some 9.0 (half-precision)";
|
|
}
|
|
else
|
|
{
|
|
return "gamemode - 8.1 with some 9.0 (full-precision)";
|
|
}
|
|
case 90:
|
|
if( bHalfPrecision )
|
|
{
|
|
return "gamemode - 9.0 (half-precision)";
|
|
}
|
|
else
|
|
{
|
|
return "gamemode - 9.0 (full-precision)";
|
|
}
|
|
case 92:
|
|
return "9.0 Shader Model 2.0b";
|
|
case 95:
|
|
return "9.0 Shader Model 3.0";
|
|
case 98:
|
|
return "XBox 360";
|
|
case 100:
|
|
return "10.0 Shader Model 4.0";
|
|
default:
|
|
return "gamemode";
|
|
}
|
|
}
|
|
|
|
const char *COM_FormatSeconds( int seconds )
|
|
{
|
|
static char string[64];
|
|
|
|
int hours = 0;
|
|
int minutes = seconds / 60;
|
|
|
|
if ( minutes > 0 )
|
|
{
|
|
seconds -= (minutes * 60);
|
|
hours = minutes / 60;
|
|
|
|
if ( hours > 0 )
|
|
{
|
|
minutes -= (hours * 60);
|
|
}
|
|
}
|
|
|
|
if ( hours > 0 )
|
|
{
|
|
Q_snprintf( string, sizeof(string), "%2i:%02i:%02i", hours, minutes, seconds );
|
|
}
|
|
else
|
|
{
|
|
Q_snprintf( string, sizeof(string), "%02i:%02i", minutes, seconds );
|
|
}
|
|
|
|
return string;
|
|
}
|
|
|
|
// Non-VarArgs version
|
|
void COM_LogString( char const *pchFile, char const *pchString )
|
|
{
|
|
if ( !g_pFileSystem )
|
|
{
|
|
Assert( 0 );
|
|
return;
|
|
}
|
|
|
|
FileHandle_t fp;
|
|
const char *pfilename;
|
|
|
|
if ( !pchFile )
|
|
{
|
|
pfilename = "hllog.txt";
|
|
}
|
|
else
|
|
{
|
|
pfilename = pchFile;
|
|
}
|
|
|
|
fp = g_pFileSystem->Open( pfilename, "a+t");
|
|
if (fp)
|
|
{
|
|
g_pFileSystem->FPrintf(fp, "%s", pchString);
|
|
g_pFileSystem->Close(fp);
|
|
}
|
|
}
|
|
|
|
void COM_Log( const char *pszFile, const char *fmt, ...)
|
|
{
|
|
if ( !g_pFileSystem )
|
|
{
|
|
Assert( 0 );
|
|
return;
|
|
}
|
|
|
|
va_list argptr;
|
|
char string[8192];
|
|
|
|
va_start (argptr,fmt);
|
|
Q_vsnprintf(string, sizeof( string ), fmt,argptr);
|
|
va_end (argptr);
|
|
|
|
COM_LogString( pszFile, string );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *filename1 -
|
|
// *filename2 -
|
|
// *iCompare -
|
|
// Output : int
|
|
//-----------------------------------------------------------------------------
|
|
int COM_CompareFileTime(const char *filename1, const char *filename2, int *iCompare)
|
|
{
|
|
int bRet = 0;
|
|
if ( iCompare )
|
|
{
|
|
*iCompare = 0;
|
|
}
|
|
|
|
if (filename1 && filename2)
|
|
{
|
|
long ft1 = g_pFileSystem->GetFileTime( filename1 );
|
|
long ft2 = g_pFileSystem->GetFileTime( filename2 );
|
|
|
|
if ( iCompare )
|
|
{
|
|
*iCompare = Sys_CompareFileTime( ft1, ft2 );
|
|
}
|
|
bRet = 1;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *szGameDir -
|
|
//-----------------------------------------------------------------------------
|
|
void COM_GetGameDir(char *szGameDir, int maxlen)
|
|
{
|
|
if (!szGameDir) return;
|
|
Q_strncpy(szGameDir, com_gamedir, maxlen );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Parse a token from a file stream
|
|
// Input : *data -
|
|
// *token -
|
|
// Output : char
|
|
//-----------------------------------------------------------------------------
|
|
const char *COM_ParseFile(const char *data, char *token, int maxtoken )
|
|
{
|
|
const char *return_data = COM_Parse(data);
|
|
Q_strncpy(token, com_token, maxtoken);
|
|
return return_data;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : void COM_Init
|
|
//-----------------------------------------------------------------------------
|
|
void COM_Init ( void )
|
|
{
|
|
CharacterSetBuild( &g_BreakSet, "{}()'" );
|
|
CharacterSetBuild( &g_BreakSetIncludingColons, "{}()':" );
|
|
}
|
|
|
|
bool COM_IsValidPath( const char *pszFilename )
|
|
{
|
|
if ( !pszFilename )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( Q_strlen( pszFilename ) <= 0 ||
|
|
Q_strstr( pszFilename, "\\\\" ) || // to protect network paths
|
|
Q_strstr( pszFilename, ":" ) || // to protect absolute paths
|
|
Q_strstr( pszFilename, ".." ) || // to protect relative paths
|
|
Q_strstr( pszFilename, "\n" ) || // CFileSystem_Stdio::FS_fopen doesn't allow this
|
|
Q_strstr( pszFilename, "\r" ) ) // CFileSystem_Stdio::FS_fopen doesn't allow this
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|