1186 lines
22 KiB
1186 lines
22 KiB
/* |
|
common.c - misc functions used by dlls' |
|
Copyright (C) 2008 Uncle Mike |
|
|
|
This program is free software: you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation, either version 3 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
*/ |
|
|
|
#if defined( ALLOCA_H ) |
|
#include ALLOCA_H |
|
#endif |
|
#include "common.h" |
|
#include "studio.h" |
|
#include "xash3d_mathlib.h" |
|
#include "const.h" |
|
#include "client.h" |
|
#include "library.h" |
|
#include "sequence.h" |
|
|
|
static const char *file_exts[] = |
|
{ |
|
"cfg", |
|
"lst", |
|
"exe", |
|
"vbs", |
|
"com", |
|
"bat", |
|
"dll", |
|
"ini", |
|
"log", |
|
"sys", |
|
}; |
|
|
|
#ifdef _DEBUG |
|
void DBG_AssertFunction( qboolean fExpr, const char* szExpr, const char* szFile, int szLine, const char* szMessage ) |
|
{ |
|
if( fExpr ) return; |
|
|
|
if( szMessage != NULL ) |
|
Con_DPrintf( S_ERROR "ASSERT FAILED:\n %s \n(%s@%d)\n%s\n", szExpr, szFile, szLine, szMessage ); |
|
else Con_DPrintf( S_ERROR "ASSERT FAILED:\n %s \n(%s@%d)\n", szExpr, szFile, szLine ); |
|
} |
|
#endif // DEBUG |
|
|
|
static int idum = 0; |
|
|
|
#define MAX_RANDOM_RANGE 0x7FFFFFFFUL |
|
#define IA 16807 |
|
#define IM 2147483647 |
|
#define IQ 127773 |
|
#define IR 2836 |
|
#define NTAB 32 |
|
#define EPS 1.2e-7 |
|
#define NDIV (1 + (IM - 1) / NTAB) |
|
#define AM (1.0 / IM) |
|
#define RNMX (1.0 - EPS) |
|
|
|
static int lran1( void ) |
|
{ |
|
static int iy = 0; |
|
static int iv[NTAB]; |
|
int j; |
|
int k; |
|
|
|
if( idum <= 0 || !iy ) |
|
{ |
|
if( -(idum) < 1 ) idum = 1; |
|
else idum = -(idum); |
|
|
|
for( j = NTAB + 7; j >= 0; j-- ) |
|
{ |
|
k = (idum) / IQ; |
|
idum = IA * (idum - k * IQ) - IR * k; |
|
if( idum < 0 ) idum += IM; |
|
if( j < NTAB ) iv[j] = idum; |
|
} |
|
|
|
iy = iv[0]; |
|
} |
|
|
|
k = (idum) / IQ; |
|
idum = IA * (idum - k * IQ) - IR * k; |
|
if( idum < 0 ) idum += IM; |
|
j = iy / NDIV; |
|
iy = iv[j]; |
|
iv[j] = idum; |
|
|
|
return iy; |
|
} |
|
|
|
// fran1 -- return a random floating-point number on the interval [0,1] |
|
static float fran1( void ) |
|
{ |
|
float temp = (float)AM * lran1(); |
|
if( temp > RNMX ) |
|
return (float)RNMX; |
|
return temp; |
|
} |
|
|
|
void COM_SetRandomSeed( int lSeed ) |
|
{ |
|
if( lSeed ) idum = lSeed; |
|
else idum = -time( NULL ); |
|
|
|
if( 1000 < idum ) |
|
idum = -idum; |
|
else if( -1000 < idum ) |
|
idum -= 22261048; |
|
} |
|
|
|
float GAME_EXPORT COM_RandomFloat( float flLow, float flHigh ) |
|
{ |
|
float fl; |
|
|
|
if( idum == 0 ) COM_SetRandomSeed( 0 ); |
|
|
|
fl = fran1(); // float in [0,1] |
|
return (fl * (flHigh - flLow)) + flLow; // float in [low, high) |
|
} |
|
|
|
int GAME_EXPORT COM_RandomLong( int lLow, int lHigh ) |
|
{ |
|
dword maxAcceptable; |
|
dword n, x = lHigh - lLow + 1; |
|
|
|
if( idum == 0 ) COM_SetRandomSeed( 0 ); |
|
|
|
if( x <= 0 || MAX_RANDOM_RANGE < x - 1 ) |
|
return lLow; |
|
|
|
// The following maps a uniform distribution on the interval [0, MAX_RANDOM_RANGE] |
|
// to a smaller, client-specified range of [0,x-1] in a way that doesn't bias |
|
// the uniform distribution unfavorably. Even for a worst case x, the loop is |
|
// guaranteed to be taken no more than half the time, so for that worst case x, |
|
// the average number of times through the loop is 2. For cases where x is |
|
// much smaller than MAX_RANDOM_RANGE, the average number of times through the |
|
// loop is very close to 1. |
|
maxAcceptable = MAX_RANDOM_RANGE - ((MAX_RANDOM_RANGE + 1) % x ); |
|
do |
|
{ |
|
n = lran1(); |
|
} while( n > maxAcceptable ); |
|
|
|
return lLow + (n % x); |
|
} |
|
|
|
/* |
|
=============================================================================== |
|
|
|
LZSS Compression |
|
|
|
=============================================================================== |
|
*/ |
|
#define LZSS_ID (('S'<<24)|('S'<<16)|('Z'<<8)|('L')) |
|
#define LZSS_LOOKSHIFT 4 |
|
#define LZSS_WINDOW_SIZE 4096 |
|
#define LZSS_LOOKAHEAD BIT( LZSS_LOOKSHIFT ) |
|
|
|
|
|
typedef struct |
|
{ |
|
unsigned int id; |
|
unsigned int size; |
|
} lzss_header_t; |
|
|
|
// expected to be sixteen bytes |
|
typedef struct lzss_node_s |
|
{ |
|
const byte *data; |
|
struct lzss_node_s *prev; |
|
struct lzss_node_s *next; |
|
char pad[4]; |
|
} lzss_node_t; |
|
|
|
typedef struct |
|
{ |
|
lzss_node_t *start; |
|
lzss_node_t *end; |
|
} lzss_list_t; |
|
|
|
typedef struct |
|
{ |
|
lzss_list_t *hash_table; |
|
lzss_node_t *hash_node; |
|
int window_size; |
|
} lzss_state_t; |
|
|
|
qboolean LZSS_IsCompressed( const byte *source ) |
|
{ |
|
lzss_header_t *phdr = (lzss_header_t *)source; |
|
|
|
if( phdr && phdr->id == LZSS_ID ) |
|
return true; |
|
return false; |
|
} |
|
|
|
uint LZSS_GetActualSize( const byte *source ) |
|
{ |
|
lzss_header_t *phdr = (lzss_header_t *)source; |
|
|
|
if( phdr && phdr->id == LZSS_ID ) |
|
return phdr->size; |
|
return 0; |
|
} |
|
|
|
static void LZSS_BuildHash( lzss_state_t *state, const byte *source ) |
|
{ |
|
lzss_list_t *list; |
|
lzss_node_t *node; |
|
unsigned int targetindex = (uint)source & ( state->window_size - 1 ); |
|
|
|
node = &state->hash_node[targetindex]; |
|
|
|
if( node->data ) |
|
{ |
|
list = &state->hash_table[*node->data]; |
|
if( node->prev ) |
|
{ |
|
list->end = node->prev; |
|
node->prev->next = NULL; |
|
} |
|
else |
|
{ |
|
list->start = NULL; |
|
list->end = NULL; |
|
} |
|
} |
|
|
|
list = &state->hash_table[*source]; |
|
node->data = source; |
|
node->prev = NULL; |
|
node->next = list->start; |
|
if( list->start ) |
|
list->start->prev = node; |
|
else list->end = node; |
|
list->start = node; |
|
} |
|
|
|
byte *LZSS_CompressNoAlloc( lzss_state_t *state, byte *pInput, int input_length, byte *pOutputBuf, uint *pOutputSize ) |
|
{ |
|
byte *pStart = pOutputBuf; // allocate the output buffer, compressed buffer is expected to be less, caller will free |
|
byte *pEnd = pStart + input_length - sizeof( lzss_header_t ) - 8; // prevent compression failure |
|
lzss_header_t *header = (lzss_header_t *)pStart; |
|
byte *pOutput = pStart + sizeof( lzss_header_t ); |
|
const byte *pEncodedPosition = NULL; |
|
byte *pLookAhead = pInput; |
|
byte *pWindow = pInput; |
|
int i, putCmdByte = 0; |
|
byte *pCmdByte = NULL; |
|
|
|
if( input_length <= sizeof( lzss_header_t ) + 8 ) |
|
return NULL; |
|
|
|
// set LZSS header |
|
header->id = LZSS_ID; |
|
header->size = input_length; |
|
|
|
// create the compression work buffers, small enough (~64K) for stack |
|
state->hash_table = (lzss_list_t *)alloca( 256 * sizeof( lzss_list_t )); |
|
memset( state->hash_table, 0, 256 * sizeof( lzss_list_t )); |
|
state->hash_node = (lzss_node_t *)alloca( state->window_size * sizeof( lzss_node_t )); |
|
memset( state->hash_node, 0, state->window_size * sizeof( lzss_node_t )); |
|
|
|
while( input_length > 0 ) |
|
{ |
|
int lookAheadLength = input_length < LZSS_LOOKAHEAD ? input_length : LZSS_LOOKAHEAD; |
|
lzss_node_t *hash = state->hash_table[pLookAhead[0]].start; |
|
int encoded_length = 0; |
|
|
|
pWindow = pLookAhead - state->window_size; |
|
|
|
if( pWindow < pInput ) |
|
pWindow = pInput; |
|
|
|
if( !putCmdByte ) |
|
{ |
|
pCmdByte = pOutput++; |
|
*pCmdByte = 0; |
|
} |
|
|
|
putCmdByte = ( putCmdByte + 1 ) & 0x07; |
|
|
|
while( hash != NULL ) |
|
{ |
|
int length = lookAheadLength; |
|
int match_length = 0; |
|
|
|
while( length-- && hash->data[match_length] == pLookAhead[match_length] ) |
|
match_length++; |
|
|
|
if( match_length > encoded_length ) |
|
{ |
|
encoded_length = match_length; |
|
pEncodedPosition = hash->data; |
|
} |
|
|
|
if( match_length == lookAheadLength ) |
|
break; |
|
|
|
hash = hash->next; |
|
} |
|
|
|
if ( encoded_length >= 3 ) |
|
{ |
|
*pCmdByte = (*pCmdByte >> 1) | 0x80; |
|
*pOutput++ = (( pLookAhead - pEncodedPosition - 1 ) >> LZSS_LOOKSHIFT ); |
|
*pOutput++ = (( pLookAhead - pEncodedPosition - 1 ) << LZSS_LOOKSHIFT ) | ( encoded_length - 1 ); |
|
} |
|
else |
|
{ |
|
*pCmdByte = ( *pCmdByte >> 1 ); |
|
*pOutput++ = *pLookAhead; |
|
encoded_length = 1; |
|
} |
|
|
|
for( i = 0; i < encoded_length; i++ ) |
|
{ |
|
LZSS_BuildHash( state, pLookAhead++ ); |
|
} |
|
|
|
input_length -= encoded_length; |
|
|
|
if( pOutput >= pEnd ) |
|
{ |
|
// compression is worse, abandon |
|
return NULL; |
|
} |
|
} |
|
|
|
if( input_length != 0 ) |
|
{ |
|
// unexpected failure |
|
Assert( 0 ); |
|
return NULL; |
|
} |
|
|
|
if( !putCmdByte ) |
|
{ |
|
pCmdByte = pOutput++; |
|
*pCmdByte = 0x01; |
|
} |
|
else |
|
{ |
|
*pCmdByte = (( *pCmdByte >> 1 ) | 0x80 ) >> ( 7 - putCmdByte ); |
|
} |
|
|
|
// put two ints at end of buffer |
|
*pOutput++ = 0; |
|
*pOutput++ = 0; |
|
|
|
if( pOutputSize ) |
|
*pOutputSize = pOutput - pStart; |
|
|
|
return pStart; |
|
} |
|
|
|
byte *LZSS_Compress( byte *pInput, int inputLength, uint *pOutputSize ) |
|
{ |
|
byte *pStart = (byte *)malloc( inputLength ); |
|
byte *pFinal = NULL; |
|
lzss_state_t state; |
|
|
|
memset( &state, 0, sizeof( state )); |
|
state.window_size = LZSS_WINDOW_SIZE; |
|
|
|
pFinal = LZSS_CompressNoAlloc( &state, pInput, inputLength, pStart, pOutputSize ); |
|
|
|
if( !pFinal ) |
|
{ |
|
free( pStart ); |
|
return NULL; |
|
} |
|
|
|
return pStart; |
|
} |
|
|
|
uint LZSS_Decompress( const byte *pInput, byte *pOutput ) |
|
{ |
|
uint totalBytes = 0; |
|
int getCmdByte = 0; |
|
int cmdByte = 0; |
|
uint actualSize = LZSS_GetActualSize( pInput ); |
|
|
|
if( !actualSize ) |
|
return 0; |
|
|
|
pInput += sizeof( lzss_header_t ); |
|
|
|
while( 1 ) |
|
{ |
|
if( !getCmdByte ) |
|
cmdByte = *pInput++; |
|
getCmdByte = ( getCmdByte + 1 ) & 0x07; |
|
|
|
if( cmdByte & 0x01 ) |
|
{ |
|
int position = *pInput++ << LZSS_LOOKSHIFT; |
|
int i, count; |
|
byte *pSource; |
|
|
|
position |= ( *pInput >> LZSS_LOOKSHIFT ); |
|
count = ( *pInput++ & 0x0F ) + 1; |
|
|
|
if( count == 1 ) |
|
break; |
|
|
|
pSource = pOutput - position - 1; |
|
for( i = 0; i < count; i++ ) |
|
*pOutput++ = *pSource++; |
|
totalBytes += count; |
|
} |
|
else |
|
{ |
|
*pOutput++ = *pInput++; |
|
totalBytes++; |
|
} |
|
cmdByte = cmdByte >> 1; |
|
} |
|
|
|
if( totalBytes != actualSize ) |
|
{ |
|
Assert( 0 ); |
|
return 0; |
|
} |
|
return totalBytes; |
|
} |
|
|
|
/* |
|
============== |
|
COM_IsWhiteSpace |
|
|
|
interpret symbol as whitespace |
|
============== |
|
*/ |
|
|
|
static int COM_IsWhiteSpace( char space ) |
|
{ |
|
if( space == ' ' || space == '\t' || space == '\r' || space == '\n' ) |
|
return 1; |
|
return 0; |
|
} |
|
|
|
/* |
|
================ |
|
COM_ParseVector |
|
|
|
================ |
|
*/ |
|
qboolean COM_ParseVector( char **pfile, float *v, size_t size ) |
|
{ |
|
string token; |
|
qboolean bracket = false; |
|
char *saved; |
|
uint i; |
|
|
|
if( v == NULL || size == 0 ) |
|
return false; |
|
|
|
memset( v, 0, sizeof( *v ) * size ); |
|
|
|
if( size == 1 ) |
|
{ |
|
*pfile = COM_ParseFile( *pfile, token, sizeof( token )); |
|
v[0] = Q_atof( token ); |
|
return true; |
|
} |
|
|
|
saved = *pfile; |
|
|
|
if(( *pfile = COM_ParseFile( *pfile, token, sizeof( token ))) == NULL ) |
|
return false; |
|
|
|
if( token[0] == '(' ) |
|
bracket = true; |
|
else *pfile = saved; // restore token to right get it again |
|
|
|
for( i = 0; i < size; i++ ) |
|
{ |
|
*pfile = COM_ParseFile( *pfile, token, sizeof( token )); |
|
v[i] = Q_atof( token ); |
|
} |
|
|
|
if( !bracket ) return true; // done |
|
|
|
if(( *pfile = COM_ParseFile( *pfile, token, sizeof( token ))) == NULL ) |
|
return false; |
|
|
|
if( token[0] == ')' ) |
|
return true; |
|
return false; |
|
} |
|
|
|
/* |
|
============= |
|
COM_FileSize |
|
|
|
============= |
|
*/ |
|
int GAME_EXPORT COM_FileSize( const char *filename ) |
|
{ |
|
return FS_FileSize( filename, false ); |
|
} |
|
|
|
/* |
|
============= |
|
COM_AddAppDirectoryToSearchPath |
|
|
|
============= |
|
*/ |
|
void GAME_EXPORT COM_AddAppDirectoryToSearchPath( const char *pszBaseDir, const char *appName ) |
|
{ |
|
FS_AddGameHierarchy( pszBaseDir, FS_NOWRITE_PATH ); |
|
} |
|
|
|
/* |
|
=========== |
|
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 GAME_EXPORT COM_ExpandFilename( const char *fileName, char *nameOutBuffer, int nameOutBufferSize ) |
|
{ |
|
const char *path; |
|
char result[MAX_SYSPATH]; |
|
|
|
if( !COM_CheckString( fileName ) || !nameOutBuffer || nameOutBufferSize <= 0 ) |
|
return 0; |
|
|
|
// filename examples: |
|
// media\sierra.avi - D:\Xash3D\valve\media\sierra.avi |
|
// models\barney.mdl - D:\Xash3D\bshift\models\barney.mdl |
|
if(( path = FS_GetDiskPath( fileName, false )) != NULL ) |
|
{ |
|
Q_sprintf( result, "%s/%s", host.rootdir, path ); |
|
|
|
// check for enough room |
|
if( Q_strlen( result ) > nameOutBufferSize ) |
|
return 0; |
|
|
|
Q_strncpy( nameOutBuffer, result, nameOutBufferSize ); |
|
return 1; |
|
} |
|
return 0; |
|
} |
|
|
|
/* |
|
============= |
|
COM_TrimSpace |
|
|
|
trims all whitespace from the front |
|
and end of a string |
|
============= |
|
*/ |
|
void COM_TrimSpace( const char *source, char *dest ) |
|
{ |
|
int start, end, length; |
|
|
|
start = 0; |
|
end = Q_strlen( source ); |
|
|
|
while( source[start] && COM_IsWhiteSpace( source[start] )) |
|
start++; |
|
end--; |
|
|
|
while( end > 0 && COM_IsWhiteSpace( source[end] )) |
|
end--; |
|
end++; |
|
|
|
length = end - start; |
|
|
|
if( length > 0 ) |
|
memcpy( dest, source + start, length ); |
|
else length = 0; |
|
|
|
// terminate the dest string |
|
dest[length] = 0; |
|
} |
|
|
|
/* |
|
================== |
|
COM_Nibble |
|
|
|
Returns the 4 bit nibble for a hex character |
|
================== |
|
*/ |
|
byte COM_Nibble( char c ) |
|
{ |
|
if(( c >= '0' ) && ( c <= '9' )) |
|
{ |
|
return (byte)(c - '0'); |
|
} |
|
|
|
if(( c >= 'A' ) && ( c <= 'F' )) |
|
{ |
|
return (byte)(c - 'A' + 0x0a); |
|
} |
|
|
|
if(( c >= 'a' ) && ( c <= 'f' )) |
|
{ |
|
return (byte)(c - 'a' + 0x0a); |
|
} |
|
|
|
return '0'; |
|
} |
|
|
|
/* |
|
================== |
|
COM_HexConvert |
|
|
|
Converts pszInput Hex string to nInputLength/2 binary |
|
================== |
|
*/ |
|
void COM_HexConvert( const char *pszInput, int nInputLength, byte *pOutput ) |
|
{ |
|
const char *pIn; |
|
byte *p = pOutput; |
|
int i; |
|
|
|
|
|
for( i = 0; i < nInputLength; i += 2 ) |
|
{ |
|
pIn = &pszInput[i]; |
|
*p = COM_Nibble( pIn[0] ) << 4 | COM_Nibble( pIn[1] ); |
|
p++; |
|
} |
|
} |
|
|
|
/* |
|
============= |
|
COM_MemFgets |
|
|
|
============= |
|
*/ |
|
char *COM_MemFgets( byte *pMemFile, int fileSize, int *filePos, char *pBuffer, int bufferSize ) |
|
{ |
|
int i, last, stop; |
|
|
|
if( !pMemFile || !pBuffer || !filePos ) |
|
return NULL; |
|
|
|
if( *filePos >= fileSize ) |
|
return NULL; |
|
|
|
i = *filePos; |
|
last = fileSize; |
|
|
|
// fgets always NULL terminates, so only read bufferSize-1 characters |
|
if( last - *filePos > ( bufferSize - 1 )) |
|
last = *filePos + ( bufferSize - 1); |
|
|
|
stop = 0; |
|
|
|
// stop at the next newline (inclusive) or end of buffer |
|
while( i < last && !stop ) |
|
{ |
|
if( pMemFile[i] == '\n' ) |
|
stop = 1; |
|
i++; |
|
} |
|
|
|
// if we actually advanced the pointer, copy it over |
|
if( i != *filePos ) |
|
{ |
|
// we read in size bytes |
|
int size = i - *filePos; |
|
|
|
// copy it out |
|
memcpy( pBuffer, pMemFile + *filePos, size ); |
|
|
|
// If the buffer isn't full, terminate (this is always true) |
|
if( size < bufferSize ) pBuffer[size] = 0; |
|
|
|
// update file pointer |
|
*filePos = i; |
|
return pBuffer; |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
/* |
|
==================== |
|
Cache_Check |
|
|
|
consistency check |
|
==================== |
|
*/ |
|
void *Cache_Check( poolhandle_t mempool, cache_user_t *c ) |
|
{ |
|
if( !c->data ) |
|
return NULL; |
|
|
|
if( !Mem_IsAllocatedExt( mempool, c->data )) |
|
return NULL; |
|
|
|
return c->data; |
|
} |
|
|
|
/* |
|
============= |
|
COM_LoadFileForMe |
|
|
|
============= |
|
*/ |
|
byte* GAME_EXPORT COM_LoadFileForMe( const char *filename, int *pLength ) |
|
{ |
|
string name; |
|
byte *file, *pfile; |
|
fs_offset_t iLength; |
|
|
|
if( !COM_CheckString( filename )) |
|
{ |
|
if( pLength ) |
|
*pLength = 0; |
|
return NULL; |
|
} |
|
|
|
Q_strncpy( name, filename, sizeof( name )); |
|
COM_FixSlashes( name ); |
|
|
|
pfile = FS_LoadFile( name, &iLength, false ); |
|
if( pLength ) *pLength = (int)iLength; |
|
|
|
if( pfile ) |
|
{ |
|
file = malloc( iLength + 1 ); |
|
if( file != NULL ) |
|
{ |
|
memcpy( file, pfile, iLength ); |
|
file[iLength] = '\0'; |
|
} |
|
Mem_Free( pfile ); |
|
pfile = file; |
|
} |
|
|
|
return pfile; |
|
} |
|
|
|
/* |
|
============= |
|
COM_LoadFile |
|
|
|
============= |
|
*/ |
|
byte *COM_LoadFile( const char *filename, int usehunk, int *pLength ) |
|
{ |
|
return COM_LoadFileForMe( filename, pLength ); |
|
} |
|
|
|
/* |
|
============= |
|
COM_SaveFile |
|
|
|
============= |
|
*/ |
|
int GAME_EXPORT COM_SaveFile( const char *filename, const void *data, int len ) |
|
{ |
|
// check for empty filename |
|
if( !COM_CheckString( filename )) |
|
return false; |
|
|
|
// check for null data |
|
if( !data || len <= 0 ) |
|
return false; |
|
|
|
return FS_WriteFile( filename, data, len ); |
|
} |
|
|
|
/* |
|
============= |
|
COM_FreeFile |
|
|
|
============= |
|
*/ |
|
void GAME_EXPORT COM_FreeFile( void *buffer ) |
|
{ |
|
free( buffer ); |
|
} |
|
|
|
/* |
|
============= |
|
COM_NormalizeAngles |
|
|
|
============= |
|
*/ |
|
void COM_NormalizeAngles( vec3_t angles ) |
|
{ |
|
int i; |
|
|
|
for( i = 0; i < 3; i++ ) |
|
{ |
|
if( angles[i] > 180.0f ) |
|
angles[i] -= 360.0f; |
|
else if( angles[i] < -180.0f ) |
|
angles[i] += 360.0f; |
|
} |
|
} |
|
|
|
/* |
|
============= |
|
pfnGetModelType |
|
|
|
============= |
|
*/ |
|
int GAME_EXPORT pfnGetModelType( model_t *mod ) |
|
{ |
|
if( !mod ) return mod_bad; |
|
return mod->type; |
|
} |
|
|
|
/* |
|
============= |
|
pfnGetModelBounds |
|
|
|
============= |
|
*/ |
|
void GAME_EXPORT pfnGetModelBounds( model_t *mod, float *mins, float *maxs ) |
|
{ |
|
if( mod ) |
|
{ |
|
if( mins ) VectorCopy( mod->mins, mins ); |
|
if( maxs ) VectorCopy( mod->maxs, maxs ); |
|
} |
|
else |
|
{ |
|
if( mins ) VectorClear( mins ); |
|
if( maxs ) VectorClear( maxs ); |
|
} |
|
} |
|
|
|
/* |
|
============= |
|
pfnCvar_RegisterServerVariable |
|
|
|
standard path to register game variable |
|
============= |
|
*/ |
|
void GAME_EXPORT pfnCvar_RegisterServerVariable( cvar_t *variable ) |
|
{ |
|
if( variable != NULL ) |
|
SetBits( variable->flags, FCVAR_EXTDLL ); |
|
Cvar_RegisterVariable( (convar_t *)variable ); |
|
} |
|
|
|
/* |
|
============= |
|
pfnCvar_RegisterEngineVariable |
|
|
|
use with precaution: this cvar will NOT unlinked |
|
after game.dll is unloaded |
|
============= |
|
*/ |
|
void GAME_EXPORT pfnCvar_RegisterEngineVariable( cvar_t *variable ) |
|
{ |
|
Cvar_RegisterVariable( (convar_t *)variable ); |
|
} |
|
|
|
/* |
|
============= |
|
pfnCvar_RegisterVariable |
|
|
|
============= |
|
*/ |
|
cvar_t *pfnCvar_RegisterClientVariable( const char *szName, const char *szValue, int flags ) |
|
{ |
|
// a1ba: try to mitigate outdated client.dll vulnerabilities |
|
if( !Q_stricmp( szName, "motdfile" )) |
|
flags |= FCVAR_PRIVILEGED; |
|
|
|
if( FBitSet( flags, FCVAR_GLCONFIG )) |
|
return (cvar_t *)Cvar_Get( szName, szValue, flags, va( CVAR_GLCONFIG_DESCRIPTION, szName )); |
|
return (cvar_t *)Cvar_Get( szName, szValue, flags|FCVAR_CLIENTDLL, Cvar_BuildAutoDescription( flags|FCVAR_CLIENTDLL )); |
|
} |
|
|
|
/* |
|
============= |
|
pfnCvar_RegisterVariable |
|
|
|
============= |
|
*/ |
|
cvar_t *pfnCvar_RegisterGameUIVariable( const char *szName, const char *szValue, int flags ) |
|
{ |
|
if( FBitSet( flags, FCVAR_GLCONFIG )) |
|
return (cvar_t *)Cvar_Get( szName, szValue, flags, va( CVAR_GLCONFIG_DESCRIPTION, szName )); |
|
return (cvar_t *)Cvar_Get( szName, szValue, flags|FCVAR_GAMEUIDLL, Cvar_BuildAutoDescription( flags|FCVAR_GAMEUIDLL )); |
|
} |
|
|
|
/* |
|
============= |
|
pfnCVarGetPointer |
|
|
|
can return NULL |
|
============= |
|
*/ |
|
cvar_t *pfnCVarGetPointer( const char *szVarName ) |
|
{ |
|
return (cvar_t *)Cvar_FindVar( szVarName ); |
|
} |
|
|
|
/* |
|
============= |
|
pfnCVarDirectSet |
|
|
|
allow to set cvar directly |
|
============= |
|
*/ |
|
void GAME_EXPORT pfnCVarDirectSet( cvar_t *var, const char *szValue ) |
|
{ |
|
Cvar_DirectSet( (convar_t *)var, szValue ); |
|
} |
|
|
|
/* |
|
============= |
|
COM_CompareFileTime |
|
|
|
============= |
|
*/ |
|
int GAME_EXPORT COM_CompareFileTime( const char *filename1, const char *filename2, int *iCompare ) |
|
{ |
|
int bRet = 0; |
|
|
|
*iCompare = 0; |
|
|
|
if( filename1 && filename2 ) |
|
{ |
|
int ft1 = FS_FileTime( filename1, false ); |
|
int ft2 = FS_FileTime( filename2, false ); |
|
|
|
// one of files is missing |
|
if( ft1 == -1 || ft2 == -1 ) |
|
return bRet; |
|
|
|
*iCompare = Host_CompareFileTime( ft1, ft2 ); |
|
bRet = 1; |
|
} |
|
|
|
return bRet; |
|
} |
|
|
|
/* |
|
============= |
|
COM_CheckParm |
|
|
|
============= |
|
*/ |
|
int GAME_EXPORT COM_CheckParm( char *parm, char **ppnext ) |
|
{ |
|
int i = Sys_CheckParm( parm ); |
|
|
|
if( ppnext ) |
|
{ |
|
if( i != 0 && i < host.argc - 1 ) |
|
*ppnext = (char *)host.argv[i + 1]; |
|
else *ppnext = NULL; |
|
} |
|
|
|
return i; |
|
} |
|
|
|
/* |
|
============= |
|
pfnTime |
|
|
|
============= |
|
*/ |
|
float GAME_EXPORT pfnTime( void ) |
|
{ |
|
return (float)Sys_DoubleTime(); |
|
} |
|
|
|
/* |
|
============= |
|
pfnGetGameDir |
|
|
|
============= |
|
*/ |
|
void GAME_EXPORT pfnGetGameDir( char *szGetGameDir ) |
|
{ |
|
if( !szGetGameDir ) return; |
|
Q_strcpy( szGetGameDir, GI->gamefolder ); |
|
} |
|
|
|
qboolean COM_IsSafeFileToDownload( const char *filename ) |
|
{ |
|
char lwrfilename[4096]; |
|
const char *first, *last; |
|
const char *ext; |
|
int i; |
|
|
|
if( !COM_CheckString( filename )) |
|
return false; |
|
|
|
if( !Q_strncmp( filename, "!MD5", 4 )) |
|
return true; |
|
|
|
Q_strnlwr( filename, lwrfilename, sizeof( lwrfilename )); |
|
|
|
if( Q_strpbrk( lwrfilename, "\\:~" ) || Q_strstr( lwrfilename, ".." ) ) |
|
return false; |
|
|
|
if( lwrfilename[0] == '/' ) |
|
return false; |
|
|
|
first = Q_strchr( lwrfilename, '.' ); |
|
last = Q_strrchr( lwrfilename, '.' ); |
|
|
|
if( first == NULL || last == NULL ) |
|
return false; |
|
|
|
if( first != last ) |
|
return false; |
|
|
|
if( Q_strlen( first ) != 4 ) |
|
return false; |
|
|
|
ext = COM_FileExtension( lwrfilename ); |
|
|
|
for( i = 0; i < ARRAYSIZE( file_exts ); i++ ) |
|
{ |
|
if( !Q_stricmp( ext, file_exts[i] )) |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
char *_copystring( poolhandle_t mempool, const char *s, const char *filename, int fileline ) |
|
{ |
|
char *b; |
|
|
|
if( !s ) return NULL; |
|
if( !mempool ) mempool = host.mempool; |
|
|
|
b = _Mem_Alloc( mempool, Q_strlen( s ) + 1, false, filename, fileline ); |
|
Q_strcpy( b, s ); |
|
|
|
return b; |
|
} |
|
|
|
/* |
|
====================== |
|
|
|
COMMON EXPORT STUBS |
|
|
|
====================== |
|
*/ |
|
|
|
|
|
/* |
|
============= |
|
pfnSequenceGet |
|
|
|
used by CS:CZ |
|
============= |
|
*/ |
|
void *GAME_EXPORT pfnSequenceGet( const char *fileName, const char *entryName ) |
|
{ |
|
Msg( "Sequence_Get: file %s, entry %s\n", fileName, entryName ); |
|
|
|
|
|
return Sequence_Get( fileName, entryName ); |
|
} |
|
|
|
/* |
|
============= |
|
pfnSequencePickSentence |
|
|
|
used by CS:CZ |
|
============= |
|
*/ |
|
void *GAME_EXPORT pfnSequencePickSentence( const char *groupName, int pickMethod, int *picked ) |
|
{ |
|
Msg( "Sequence_PickSentence: group %s, pickMethod %i\n", groupName, pickMethod ); |
|
|
|
return Sequence_PickSentence( groupName, pickMethod, picked ); |
|
|
|
} |
|
|
|
/* |
|
============= |
|
pfnIsCareerMatch |
|
|
|
used by CS:CZ (client stub) |
|
============= |
|
*/ |
|
int GAME_EXPORT GAME_EXPORT pfnIsCareerMatch( void ) |
|
{ |
|
return 0; |
|
} |
|
|
|
/* |
|
============= |
|
pfnRegisterTutorMessageShown |
|
|
|
only exists in PlayStation version |
|
============= |
|
*/ |
|
void GAME_EXPORT pfnRegisterTutorMessageShown( int mid ) |
|
{ |
|
} |
|
|
|
/* |
|
============= |
|
pfnGetTimesTutorMessageShown |
|
|
|
only exists in PlayStation version |
|
============= |
|
*/ |
|
int GAME_EXPORT pfnGetTimesTutorMessageShown( int mid ) |
|
{ |
|
return 0; |
|
} |
|
|
|
/* |
|
============= |
|
pfnProcessTutorMessageDecayBuffer |
|
|
|
only exists in PlayStation version |
|
============= |
|
*/ |
|
void GAME_EXPORT pfnProcessTutorMessageDecayBuffer( int *buffer, int bufferLength ) |
|
{ |
|
} |
|
|
|
/* |
|
============= |
|
pfnConstructTutorMessageDecayBuffer |
|
|
|
only exists in PlayStation version |
|
============= |
|
*/ |
|
void GAME_EXPORT pfnConstructTutorMessageDecayBuffer( int *buffer, int bufferLength ) |
|
{ |
|
} |
|
|
|
/* |
|
============= |
|
pfnResetTutorMessageDecayData |
|
|
|
only exists in PlayStation version |
|
============= |
|
*/ |
|
void GAME_EXPORT pfnResetTutorMessageDecayData( void ) |
|
{ |
|
} |
|
|
|
#if XASH_ENGINE_TESTS |
|
|
|
#include "tests.h" |
|
|
|
void Test_RunCommon( void ) |
|
{ |
|
char *file = (char *)"q asdf \"qwerty\" \"f \\\"f\" meowmeow\n// comment \"stuff ignored\"\nbark"; |
|
int len; |
|
char buf[5]; |
|
|
|
Msg( "Checking COM_ParseFile...\n" ); |
|
|
|
file = COM_ParseFileSafe( file, buf, sizeof( buf ), 0, &len, NULL ); |
|
TASSERT( !Q_strcmp( buf, "q" ) && len == 1); |
|
|
|
file = COM_ParseFileSafe( file, buf, sizeof( buf ), 0, &len, NULL ); |
|
TASSERT( !Q_strcmp( buf, "asdf" ) && len == 4); |
|
|
|
file = COM_ParseFileSafe( file, buf, sizeof( buf ), 0, &len, NULL ); |
|
TASSERT( !Q_strcmp( buf, "qwer" ) && len == -1); |
|
|
|
file = COM_ParseFileSafe( file, buf, sizeof( buf ), 0, &len, NULL ); |
|
TASSERT( !Q_strcmp( buf, "f \"f" ) && len == 4); |
|
|
|
file = COM_ParseFileSafe( file, buf, sizeof( buf ), 0, &len, NULL ); |
|
TASSERT( !Q_strcmp( buf, "meow" ) && len == -1); |
|
|
|
file = COM_ParseFileSafe( file, buf, sizeof( buf ), 0, &len, NULL ); |
|
TASSERT( !Q_strcmp( buf, "bark" ) && len == 4); |
|
} |
|
#endif
|
|
|