diff options
Diffstat (limited to 'source/common.c')
-rw-r--r-- | source/common.c | 1673 |
1 files changed, 1673 insertions, 0 deletions
diff --git a/source/common.c b/source/common.c new file mode 100644 index 0000000..6ee5ab0 --- /dev/null +++ b/source/common.c @@ -0,0 +1,1673 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +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 2 +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. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// common.c -- misc functions used in client and server +#include "com_local.h" +#include <setjmp.h> +#ifdef USE_ZLIB +#include <zlib.h> +#endif + +commonAPI_t com; + +static jmp_buf abortframe; // an ERR_DROP occured, exit the entire frame + +static char com_errorMsg[MAXPRINTMSG]; + +cvar_t *host_speeds; +cvar_t *developer; +cvar_t *timescale; +cvar_t *fixedtime; +cvar_t *dedicated; + +cvar_t *logfile_active; // 1 = create new, 2 = append to existing +cvar_t *logfile_flush; // 1 = flush after each print +cvar_t *logfile_name; +cvar_t *logfile_prefix; + +cvar_t *sv_running; +cvar_t *sv_paused; +cvar_t *cl_running; +cvar_t *cl_paused; +cvar_t *com_timedemo; +cvar_t *com_sleep; +cvar_t *com_date_format; +cvar_t *com_time_format; +cvar_t *com_debug_break; + +fileHandle_t com_logFile; +qboolean com_logNewline; +uint32 com_framenum; +uint32 com_eventTime; +uint32 com_localTime; +qboolean com_initialized; + +// host_speeds times +int time_before_game; +int time_after_game; +int time_before_ref; +int time_after_ref; + +void Con_Init( void ); +void Prompt_Init( void ); +void SCR_EndLoadingPlaque( void ); + +/* +============================================================================ + +CLIENT / SERVER interactions + +============================================================================ +*/ + +static int rd_target; +static char *rd_buffer; +static int rd_buffersize; +static void (*rd_flush)(int target, char *buffer); + +void Com_BeginRedirect (int target, char *buffer, int buffersize, void (*flush)) +{ + if (!target || !buffer || !buffersize || !flush) + return; + rd_target = target; + rd_buffer = buffer; + rd_buffersize = buffersize; + rd_flush = flush; + + *rd_buffer = 0; +} + +void Com_AbortRedirect (void) +{ + rd_target = 0; + rd_buffer = NULL; + rd_buffersize = 0; + rd_flush = NULL; +} + +void Com_EndRedirect (void) +{ + rd_flush(rd_target, rd_buffer); + + rd_target = 0; + rd_buffer = NULL; + rd_buffersize = 0; + rd_flush = NULL; +} + +static void LogFile_Close( void ) { + if( !com_logFile ) { + return; + } + + Com_Printf( "Closing %s\n", FS_GetFileName( com_logFile ) ); + + FS_FCloseFile( com_logFile ); + com_logFile = 0; +} + +static void LogFile_Open( void ) { + uint32 mode; + + mode = logfile_active->integer > 1 ? FS_MODE_APPEND : FS_MODE_WRITE; + + if( logfile_flush->integer ) { + mode |= FS_FLUSH_SYNC; + } + + FS_FOpenFile( logfile_name->string, &com_logFile, mode ); + + if( !com_logFile ) { + Com_WPrintf( "Couldn't open %s\n", logfile_name->string ); + Cvar_SetInteger( "logfile", 0 ); + return; + } + + com_logNewline = qtrue; + Com_Printf( "Logging console to %s\n", logfile_name->string ); +} + +static void logfile_active_changed( cvar_t *self ) { + if( !self->integer ) { + LogFile_Close(); + } else { + LogFile_Open(); + } +} + +static void logfile_param_changed( cvar_t *self ) { + if( logfile_active->integer ) { + LogFile_Close(); + LogFile_Open(); + } +} + +static void LogFile_Output( const char *string ) { + char text[MAXPRINTMSG]; + char timebuf[MAX_QPATH]; + char *p, *maxp; + int length; + time_t clock; + struct tm *tm; + int c; + + if( logfile_prefix->string[0] ) { + time( &clock ); + tm = localtime( &clock ); + length = strftime( timebuf, sizeof( timebuf ), + logfile_prefix->string, tm ); + } else { + length = 0; + } + + p = text; + maxp = text + sizeof( text ) - 1; + while( *string ) { + if( Q_IsColorString( string ) ) { + string += 2; + continue; + } + if( com_logNewline ) { + if( length && p + length < maxp ) { + strcpy( p, timebuf ); + p += length; + } + com_logNewline = qfalse; + } + + if( p == maxp ) { + break; + } + + c = *string++; + c &= 127; + if( c == '\n' ) { + com_logNewline = qtrue; + } + + *p++ = c; + } + *p = 0; + + length = p - text; + FS_Write( text, length, com_logFile ); +} + +/* +============= +Com_Printf + +Both client and server can use this, and it will output +to the apropriate place. +============= +*/ +void Com_Printf( const char *fmt, ... ) { + va_list argptr; + char msg[MAXPRINTMSG]; + static int recursive; + int length; + + if( recursive == 2 ) { + return; + } + + recursive++; + + va_start( argptr, fmt ); + length = Q_vsnprintf( msg, sizeof( msg ), fmt, argptr ); + va_end( argptr ); + + if( rd_target ) { + if( length + strlen( rd_buffer ) > rd_buffersize - 1 ) { + rd_flush( rd_target, rd_buffer ); + *rd_buffer = 0; + } + Q_strcat( rd_buffer, rd_buffersize, msg ); + } else { + Con_Print( msg ); + + // also echo to debugging console + Sys_ConsoleOutput( msg ); + + // logfile + if( com_logFile ) { + LogFile_Output( msg ); + } + } + + recursive--; +} + + +/* +================ +Com_DPrintf + +A Com_Printf that only shows up if the "developer" cvar is set +================ +*/ +void Com_DPrintf( const char *fmt, ... ) { + va_list argptr; + char msg[MAXPRINTMSG]; + + if( !developer || !developer->integer ) + return; // don't confuse non-developers with techie stuff... + + va_start( argptr, fmt ); + Q_vsnprintf( msg, sizeof( msg ), fmt, argptr ); + va_end( argptr ); + + Com_Printf( S_COLOR_BLUE"%s", msg ); +} + +/* +================ +Com_WPrintf + +================ +*/ +void Com_WPrintf( const char *fmt, ... ) { + va_list argptr; + char msg[MAXPRINTMSG]; + + va_start( argptr, fmt ); + Q_vsnprintf( msg, sizeof( msg ), fmt, argptr ); + va_end( argptr ); + + Com_Printf( S_COLOR_YELLOW"WARNING: %s", msg ); +} + +/* +================ +Com_EPrintf + +================ +*/ +void Com_EPrintf( const char *fmt, ... ) { + va_list argptr; + char msg[MAXPRINTMSG]; + + va_start( argptr, fmt ); + Q_vsnprintf( msg, sizeof( msg ), fmt, argptr ); + va_end( argptr ); + + Com_Printf( S_COLOR_RED"ERROR: %s", msg ); +} + + +/* +============= +Com_Error + +Both client and server can use this, and it will +do the apropriate things. +============= +*/ +void Com_Error( comErrorType_t code, const char *fmt, ... ) { + va_list argptr; + static qboolean recursive; + + if( recursive ) { +#ifdef _DEBUG + Sys_DebugBreak(); +#endif + Sys_Error( "recursive error after: %s", com_errorMsg ); + } + recursive = qtrue; + + va_start( argptr, fmt ); + Q_vsnprintf( com_errorMsg, sizeof( com_errorMsg ), fmt, argptr ); + va_end( argptr ); + + /* fix up drity message buffers */ + MSG_Init(); + + Com_AbortRedirect(); + + if( code == ERR_DISCONNECT || code == ERR_SILENT ) { + SV_Shutdown( va( "Server was killed: %s", com_errorMsg ), + KILL_DISCONNECT ); + CL_Disconnect( code, com_errorMsg ); + Com_Printf( S_COLOR_YELLOW "%s\n", com_errorMsg ); + recursive = qfalse; + longjmp( abortframe, -1 ); + } + + if( com_debug_break && com_debug_break->integer ) { + Sys_DebugBreak(); + } + + if( code == ERR_DROP ) { + Com_Printf( S_COLOR_RED "********************\n" + "ERROR: %s\n" + "********************\n", com_errorMsg ); + SV_Shutdown( va( "Server crashed: %s\n", com_errorMsg ), KILL_DROP ); + CL_Disconnect( ERR_DROP, com_errorMsg ); + recursive = qfalse; + longjmp( abortframe, -1 ); + } + + if( com_logFile ) { + FS_FPrintf( com_logFile, "FATAL: %s\n", com_errorMsg ); + } + + SV_Shutdown( va( "Server fatal crashed: %s\n", com_errorMsg ), KILL_DROP ); + CL_Shutdown(); + Qcommon_Shutdown( qtrue ); + + Sys_Error( "%s", com_errorMsg ); +} + +/* +=================== +Com_LevelPrint +=================== +*/ +void Com_LevelPrint( comPrintType_t type, const char *str ) { + switch( type ) { + case PRINT_DEVELOPER: + Com_DPrintf( "%s", str ); + break; + case PRINT_WARNING: + Com_WPrintf( "%s", str ); + break; + case PRINT_ERROR: + Com_EPrintf( "%s", str ); + break; + default: + Com_Printf( "%s", str ); + break; + } +} + +/* +=================== +Com_LevelError +=================== +*/ +void Com_LevelError( comErrorType_t code, const char *str ) { + Com_Error( code, "%s", str ); +} + +/* +============= +Com_Quit + +Both client and server can use this, and it will +do the apropriate things. +============= +*/ +void Com_Quit( void ) { + SV_Shutdown( "Server quit\n", KILL_DROP ); + CL_Shutdown(); + Qcommon_Shutdown( qfalse ); + + Sys_Quit(); +} + + +// ============================================================================ + +#ifndef DEDICATED_ONLY + +/* +=============== +Com_WriteConfiguration + +Writes key bindings and archived cvars to config.cfg +=============== +*/ +static qboolean Com_WriteConfiguration( const char *path ) { + fileHandle_t f; + + FS_FOpenFile( path, &f, FS_MODE_WRITE ); + if( !f ) { + Com_WPrintf( "Couldn't write %s\n", path ); + return qfalse; + } + + FS_FPrintf( f, "// generated by q2pro, do not modify\n" ); + + Key_WriteBindings( f ); + Cvar_WriteVariables( f ); + + FS_FCloseFile( f ); + + return qtrue; +} + +/* +=============== +Com_WriteConfig_f +=============== +*/ +static void Com_WriteConfig_f( void ) { + char buffer[MAX_QPATH]; + + if( Cmd_Argc() > 2 ) { + Com_Printf( "Usage: %s [cfgfile]\n", Cmd_Argv( 0 ) ); + return; + } + + if( Cmd_Argc() < 2 ) { + strcpy( buffer, COM_CONFIG_NAME ); + } else { + Cmd_ArgvBuffer( 1, buffer, sizeof( buffer ) ); + COM_DefaultExtension( buffer, ".cfg", sizeof( buffer ) ); + } + + if( Com_WriteConfiguration( buffer ) ) { + Com_Printf( "Wrote %s\n", buffer ); + } +} + +#endif + +/* +============================================================================== + + ZONE MEMORY ALLOCATION + +just cleared malloc with counters now... + +============================================================================== +*/ + +#define Z_MAGIC 0x1d0d +#define Z_TAIL 0x5b7b + +typedef struct zhead_s { + uint16 magic; + uint16 tag; // for group free + size_t size; + struct zhead_s *prev, *next; +} zhead_t; + +static zhead_t z_chain; + +typedef struct zstatic_s { + zhead_t z; + char data[2]; /* !!make sure 'tail' field is aligned properly */ + uint16 tail; +} zstatic_t; + +static zstatic_t z_static[] = { + { { Z_MAGIC, TAG_STATIC, sizeof( zstatic_t ) }, { '0', '\0' }, Z_TAIL }, + { { Z_MAGIC, TAG_STATIC, sizeof( zstatic_t ) }, { '1', '\0' }, Z_TAIL }, + { { Z_MAGIC, TAG_STATIC, sizeof( zstatic_t ) }, { '2', '\0' }, Z_TAIL }, + { { Z_MAGIC, TAG_STATIC, sizeof( zstatic_t ) }, { '3', '\0' }, Z_TAIL }, + { { Z_MAGIC, TAG_STATIC, sizeof( zstatic_t ) }, { '4', '\0' }, Z_TAIL }, + { { Z_MAGIC, TAG_STATIC, sizeof( zstatic_t ) }, { '5', '\0' }, Z_TAIL }, + { { Z_MAGIC, TAG_STATIC, sizeof( zstatic_t ) }, { '6', '\0' }, Z_TAIL }, + { { Z_MAGIC, TAG_STATIC, sizeof( zstatic_t ) }, { '7', '\0' }, Z_TAIL }, + { { Z_MAGIC, TAG_STATIC, sizeof( zstatic_t ) }, { '8', '\0' }, Z_TAIL }, + { { Z_MAGIC, TAG_STATIC, sizeof( zstatic_t ) }, { '9', '\0' }, Z_TAIL }, + { { Z_MAGIC, TAG_STATIC, sizeof( zstatic_t ) }, { '\0' }, Z_TAIL }, +}; + +typedef struct zstats_s { + size_t count; + size_t bytes; + char *name; +} zstats_t; + +static zstats_t z_stats[TAG_MAX]; + +void Z_Check( void ) { + zhead_t *z; + + for( z = z_chain.next; z != &z_chain; z = z->next ) { + if( z->magic != Z_MAGIC ) { + Com_Error( ERR_FATAL, "Z_Check: bad magic" ); + } + + if( *( uint16 * )( ( byte * )z + z->size - sizeof( uint16 ) ) != Z_TAIL ) { + Com_Error( ERR_FATAL, "Z_Check: bad tail" ); + } + + if( z->tag == TAG_FREE ) { + Com_Error( ERR_FATAL, "Z_Check: bad tag" ); + } + } +} + +void Z_LeakTest( memtag_t tag ) { + zhead_t *z; + zstats_t *s; + size_t numLeaks, numBytes; + + numLeaks = numBytes = 0; + for( z = z_chain.next; z != &z_chain; z = z->next ) { + if( z->magic != Z_MAGIC ) + Com_Error( ERR_FATAL, "Z_LeakTest: bad magic" ); + + if( *( uint16 * )( ( byte * )z + z->size - sizeof( uint16 ) ) != Z_TAIL ) { + Com_Error( ERR_FATAL, "Z_LeakTest: bad tail" ); + } + + if( z->tag == TAG_FREE ) { + Com_Error( ERR_FATAL, "Z_LeakTest: bad tag" ); + } + + if( z->tag == tag ) { + numLeaks++; + numBytes += z->size; + } + } + + if( numLeaks ) { + s = &z_stats[tag < TAG_MAX ? tag : TAG_FREE]; + Com_Printf( S_COLOR_YELLOW "************* Z_LeakTest *************\n" + "%s leaked %u bytes of memory (%u object%s)\n" + "**************************************\n", + s->name, numBytes, numLeaks, + ( numLeaks % 10 ) == 1 ? "" : "s" ); + } +} + +/* +======================== +Z_Free +======================== +*/ +void Z_Free( void *ptr ) { + zhead_t *z; + zstats_t *s; + + if( !ptr ) { + return; + } + + z = ( ( zhead_t * )ptr ) - 1; + + if( z->magic != Z_MAGIC ) { + Com_Error( ERR_FATAL, "Z_Free: bad magic" ); + } + + if( *( uint16 * )( ( byte * )z + z->size - sizeof( uint16 ) ) != Z_TAIL ) { + Com_Error( ERR_FATAL, "Z_Free: bad tail" ); + } + + if( z->tag == TAG_FREE ) { + Com_Error( ERR_FATAL, "Z_Free: bad tag" ); + } + + s = &z_stats[z->tag < TAG_MAX ? z->tag : TAG_FREE]; + s->count--; + s->bytes -= z->size; + + if( z->tag != TAG_STATIC ) { + z->prev->next = z->next; + z->next->prev = z->prev; + + free( z ); + } +} + +/* +======================== +Z_Realloc +======================== +*/ +void *Z_Realloc( void *ptr, size_t size ) { + zhead_t *z; + zstats_t *s; + + if( !ptr ) { + return Z_Malloc( size ); + } + + if( !size ) { + Z_Free( ptr ); + return NULL; + } + + z = ( ( zhead_t * )ptr ) - 1; + + if( z->magic != Z_MAGIC ) { + Com_Error( ERR_FATAL, "Z_Realloc: bad magic" ); + } + + if( *( uint16 * )( ( byte * )z + z->size - sizeof( uint16 ) ) != Z_TAIL ) { + Com_Error( ERR_FATAL, "Z_Realloc: bad tail" ); + } + + if( z->tag == TAG_FREE ) { + Com_Error( ERR_FATAL, "Z_Realloc: bad tag" ); + } + + if( z->tag == TAG_STATIC ) { + Z_Free( ptr ); + return Z_Malloc( size ); + } + + s = &z_stats[z->tag < TAG_MAX ? z->tag : TAG_FREE]; + s->bytes -= z->size; + + size += sizeof( zhead_t ) + sizeof( uint16 ); + size = ( size + 3 ) & ~3; + + z = realloc( z, size ); + if( !z ) { + Com_Error( ERR_FATAL, "Z_Realloc: couldn't reallocate %u bytes", size ); + } + + z->size = size; + z->prev->next = z; + z->next->prev = z; + + s->bytes += size; + + *( uint16 * )( ( byte * )z + size - sizeof( uint16 ) ) = Z_TAIL; + + return ( void * )( z + 1 ); +} + +/* +======================== +Z_Stats_f +======================== +*/ +void Z_Stats_f( void ) { + size_t z_bytes, z_count; + zstats_t *s; + int i; + + Com_Printf( "-----------------\n" ); + Com_Printf( " bytes blocks\n\n" ); + + z_bytes = 0; + z_count = 0; + for( i = 0, s = z_stats; i < TAG_MAX; i++, s++ ) { + if( !s->count ) { + continue; + } + + Com_Printf( "%9u %6u %s\n", s->bytes, s->count, s->name ); + z_bytes += s->bytes; + z_count += s->count; + } + + Com_Printf( "-----------------\n" ); + Com_Printf( "%9u %6u %s\n", z_bytes, z_count, "total" ); +} + +/* +======================== +Z_FreeTags +======================== +*/ +void Z_FreeTags( memtag_t tag ) { + zhead_t *z, *next; + + for( z = z_chain.next; z != &z_chain; z = next ) { + if( z->magic != Z_MAGIC ) { + Com_Error( ERR_FATAL, "Z_FreeTags: bad magic" ); + } + + if( *( uint16 * )( ( byte * )z + z->size - sizeof( uint16 ) ) != + Z_TAIL ) + { + Com_Error( ERR_FATAL, "Z_FreeTags: bad tail" ); + } + + if( z->tag == TAG_FREE ) { + Com_Error( ERR_FATAL, "Z_FreeTags: bad tag" ); + } + next = z->next; + if( z->tag == tag ) { + Z_Free( ( void * )( z + 1 ) ); + } + } +} + +/* +======================== +Z_TagMalloc +======================== +*/ +void *Z_TagMalloc( size_t size, memtag_t tag ) { + zhead_t *z; + zstats_t *s; + + if( !size ) { + return NULL; + } + + if( tag == TAG_FREE ) + Com_Error( ERR_FATAL, "Z_TagMalloc: bad tag" ); + + size += sizeof( zhead_t ) + sizeof( uint16 ); + size = ( size + 3 ) & ~3; + z = malloc( size ); + if( !z ) { + Com_Error( ERR_FATAL, "Z_TagMalloc: couldn't allocate %u bytes", size ); + } + z->magic = Z_MAGIC; + z->tag = tag; + z->size = size; + + z->next = z_chain.next; + z->prev = &z_chain; + z_chain.next->prev = z; + z_chain.next = z; + + *( uint16 * )( ( byte * )z + size - sizeof( uint16 ) ) = Z_TAIL; + + s = &z_stats[tag < TAG_MAX ? tag : TAG_FREE]; + s->count++; + s->bytes += size; + + return ( void * )( z + 1 ); +} + +void *Z_TagMallocz( size_t size, memtag_t tag ) { + void *ptr; + + if( !size ) { + return NULL; + } + + ptr = Z_TagMalloc( size, tag ); + memset( ptr, 0, size ); + + return ptr; +} + +static byte *z_reserved; +static size_t z_reservedUnuse; +static size_t z_reservedTotal; + +void Z_TagReserve( size_t size, memtag_t tag ) { + z_reserved = Z_TagMalloc( size, tag ); + z_reservedTotal = size; + z_reservedUnuse = 0; +} + +void *Z_ReservedAlloc( size_t size ) { + void *ptr; + + if( z_reservedUnuse + size > z_reservedTotal ) { + Com_Error( ERR_FATAL, "Z_ReservedAlloc: out of space" ); + } + + ptr = z_reserved + z_reservedUnuse; + z_reservedUnuse += size; + + return ptr; + +} + +void *Z_ReservedAllocz( size_t size ) { + void *ptr = Z_ReservedAlloc( size ); + memset( ptr, 0, size ); + + return ptr; + +} + +char *Z_ReservedCopyString( const char *in ) { + char *out; + int length; + + if( !in ) { + return NULL; + } + + length = strlen( in ) + 1; + + out = Z_ReservedAlloc( length ); + strcpy( out, in ); + + return out; +} + +/* +======================== +Z_Init +======================== +*/ +void Z_Init( void ) { + zstats_t *s; + int i; + static char *names[TAG_MAX] = { + "game", + "static", + "generic", + "cmd", + "cvar", + "filesystem", + "renderer", + "ui", + "cgame", + "server", + "mvd", + "sound", + "cmodel", + "lua" + }; + + z_chain.next = z_chain.prev = &z_chain; + + for( i = 0, s = z_stats; i < TAG_MAX; i++, s++ ) { + s->name = names[i] ? names[i] : "unknown"; + } +} + +/* +================ +Z_TagCopyString +================ +*/ +char *Z_TagCopyString( const char *in, memtag_t tag ) { + char *out; + int length; + + if( !in ) { + return NULL; + } + + length = strlen( in ) + 1; + + out = Z_TagMalloc( length, tag ); + strcpy( out, in ); + + return out; +} + +/* +================ +Cvar_CopyString +================ +*/ +char *Cvar_CopyString( const char *in ) { + char *out; + int length; + zstatic_t *z; + + if( !in ) { + return NULL; + } + + if( !in[0] ) { + z = &z_static[10]; + z_stats[TAG_STATIC].count++; + z_stats[TAG_STATIC].bytes += z->z.size; + return z->data; + } + + if( !in[1] && Q_isdigit( in[0] ) ) { + z = &z_static[ in[0] - '0' ]; + z_stats[TAG_STATIC].count++; + z_stats[TAG_STATIC].bytes += z->z.size; + return z->data; + } + + length = strlen( in ) + 1; + + out = Z_TagMalloc( length, TAG_CVAR ); + strcpy( out, in ); + + return out; +} + +/* +============================================================================== + + FIFO + +============================================================================== +*/ + +qboolean FIFO_Read( fifo_t *fifo, void *buffer, int length ) { + byte *dst = buffer; + int as = fifo->ay - fifo->ax; + int wrapped = length - as; + + if( wrapped < 0 ) { + memcpy( dst, fifo->data + fifo->ax, length ); + fifo->ax += length; + } else { + if( wrapped > fifo->bs ) { + return qfalse; + } + memcpy( dst, fifo->data + fifo->ax, as ); + memcpy( dst + as, fifo->data, wrapped ); + fifo->ax = wrapped; + fifo->ay = fifo->bs; + fifo->bs = 0; + } + + return qtrue; +} + +qboolean FIFO_Write( fifo_t *fifo, const void *buffer, int length ) { + const byte *src = buffer; + int tail, wrapped; + + if( fifo->bs ) { + if( fifo->bs + length > fifo->ax ) { + return qfalse; + } + memcpy( fifo->data + fifo->bs, src, length ); + fifo->bs += length; + return qtrue; + } + + tail = fifo->size - fifo->ay; + wrapped = length - tail; + + if( wrapped < 0 ) { + memcpy( fifo->data + fifo->ay, src, length ); + fifo->ay += length; + } else { + if( wrapped > fifo->ax ) { + return qfalse; + } + memcpy( fifo->data + fifo->ay, src, tail ); + memcpy( fifo->data, src + tail, wrapped ); + fifo->ay = fifo->size; + fifo->bs = wrapped; + } + + return qtrue; +} + +/* +============================================================================== + + INIT / SHUTDOWN + +============================================================================== +*/ + +/* +============= +Com_FillAPI +============= +*/ +void Com_FillAPI( commonAPI_t *api ) { + api->Print = Com_LevelPrint; + api->Error = Com_LevelError; + api->TagMalloc = Z_TagMalloc; + api->Realloc = Z_Realloc; + api->Free = Z_Free; +} + +/* +============= +Com_Time_m +============= +*/ +void Com_Time_m( char *buffer, int bufferSize ) { + time_t clock; + struct tm *localTime; + + time( &clock ); + localTime = localtime( &clock ); + + strftime( buffer, bufferSize, com_time_format->string, localTime ); +} + +/* +============= +Com_Date_m +============= +*/ +static void Com_Date_m( char *buffer, int bufferSize ) { + time_t clock; + struct tm *localTime; + + time( &clock ); + localTime = localtime( &clock ); + + strftime( buffer, bufferSize, com_date_format->string, localTime ); +} + +static void Com_LastError_f( void ) { + if( com_errorMsg[0] ) { + Com_Printf( "%s\n", com_errorMsg ); + } else { + Com_Printf( "No error.\n" ); + } +} + +#ifdef _DEBUG + +/* +============= +Com_Error_f + +Just throw a fatal error to +test error shutdown procedures +============= +*/ +void Com_Error_f( void ) { + Com_Error( ERR_FATAL, "%s", Cmd_Argv( 1 ) ); +} + +void Com_ErrorDrop_f( void ) { + Com_Error( ERR_DROP, "%s", Cmd_Argv( 1 ) ); +} + +void Com_Freeze_f( void ) { + int seconds, time; + + if( Cmd_Argc() < 2 ) { + Com_Printf( "Usage: %s <seconds>\n", Cmd_Argv( 0 ) ); + return; + } + + seconds = atoi( Cmd_Argv( 1 ) ); + if( seconds < 1 ) { + return; + } + + time = Sys_Milliseconds() + seconds * 1000; + while( Sys_Milliseconds() < time ) + ; + +} + +void Com_Crash_f( void ) { + *( uint32 * )0 = 0x123456; +} + +static void Com_VsnprintfTest_f( void ) { + char buffer[32]; + int ret; + qboolean fail; + + /* test if returned length is valid, no overflow, large buffer */ + Com_Printf( "test 1\n" ); + fail = qfalse; + memset( buffer, '@', sizeof( buffer ) ); + ret = Com_sprintf( buffer, sizeof( buffer ), "%s", "abcd" ); + if( ret != 4 ) { + Com_Printf( "returned %d instead of %d\n", ret, 4 ); + fail = qtrue; + } + if( buffer[4] != 0 ) { + Com_Printf( "buffer left unterminated\n" ); + fail = qtrue; + } + if( buffer[5] != '@' ) { + Com_Printf( "buffer overflowed\n" ); + fail = qtrue; + } + if( !fail ) { + Com_Printf( "succeeded\n" ); + } else { + Com_Printf( "failed\n" ); + } + + /* test if returned length is valid, no overflow, small buffer */ + Com_Printf( "test 2\n" ); + fail = qfalse; + memset( buffer, '@', sizeof( buffer ) ); + ret = Com_sprintf( buffer, 5, "%s", "abcd" ); + if( ret != 4 ) { + Com_Printf( "returned %d instead of %d\n", ret, 4 ); + } + if( buffer[4] != 0 ) { + Com_Printf( "buffer left unterminated\n" ); + fail = qtrue; + } + if( buffer[5] != '@' ) { + Com_Printf( "buffer overflowed\n" ); + fail = qtrue; + } + if( !fail ) { + Com_Printf( "succeeded\n" ); + } else { + Com_Printf( "failed\n" ); + } + + /* test if returned length is valid, with overflow */ + Com_Printf( "test 3\n" ); + fail = qfalse; + memset( buffer, '@', sizeof( buffer ) ); + ret = Com_sprintf( buffer, 5, "%s", "abcdefgh" ); + if( ret != 4 ) { + Com_Printf( "returned %d instead of %d\n", ret, 4 ); + fail = qtrue; + } + if( buffer[4] != 0 ) { + Com_Printf( "buffer left unterminated\n" ); + fail = qtrue; + } + if( buffer[5] != '@' ) { + Com_Printf( "buffer overflowed\n" ); + fail = qtrue; + } + + if( !fail ) { + Com_Printf( "succeeded\n" ); + } else { + Com_Printf( "failed\n" ); + } +} + +#endif + +const char *Com_FileNameGenerator( const char *path, const char *ext, + const char *partial, qboolean stripExtension, int state ) { + static int length, numFiles; + static char **list; + static int curpos; + char *s, *p; + + if( state == 2 ) { + goto finish; + } + + if( !state ) { + length = strlen( partial ); + list = FS_ListFiles( path, ext, 0, &numFiles ); + curpos = 0; + } + + while( curpos < numFiles ) { + s = list[curpos++]; + if( stripExtension ) { + p = COM_FileExtension( s ); + *p = 0; + } + if( !strncmp( s, partial, length ) ) { + return s; + } + } + +finish: + if( list ) { + FS_FreeFileList( list ); + list = NULL; + } + return NULL; +} + +const char *Com_FileNameGeneratorByFilter( const char *path, const char *filter, + const char *partial, qboolean stripExtension, int state ) { + static int length, numFiles; + static char **list; + static int curpos; + char *s, *p; + + if( state == 2 ) { + goto finish; + } + + if( !state ) { + length = strlen( partial ); + list = FS_ListFiles( path, filter, FS_SEARCH_SAVEPATH | + FS_SEARCH_BYFILTER, &numFiles ); + curpos = 0; + } + + while( curpos < numFiles ) { + s = list[curpos++]; + if( stripExtension ) { + p = COM_FileExtension( s ); + *p = 0; + } + if( !strncmp( s, partial, length ) ) { + return s; + } + } + +finish: + if( list ) { + FS_FreeFileList( list ); + list = NULL; + } + return NULL; +} + +#define MAX_LINE_COMMANDS 128 + +static char *com_commands[MAX_LINE_COMMANDS]; +static int com_numCommands; + +/* +=============== +Com_ParseCommandLine + +=============== +*/ +static void Com_ParseCommandLine( char *commandLine ) { + qboolean inquote = qfalse; + + if( !commandLine ) { + return; + } + + com_numCommands = 0; + while( *commandLine ) { + if( *commandLine == '\"' ) { + inquote ^= 1; + } else if( *commandLine == '+' && !inquote ) { + com_commands[com_numCommands] = commandLine + 1; + com_numCommands++; + + *commandLine = 0; + + if( com_numCommands == MAX_LINE_COMMANDS ) { + break; + } + } + + commandLine++; + + } +} + +/* +=============== +Com_AddEarlyCommands + +Adds command line parameters as script statements. +Commands lead with a +, and continue until another + + +Set commands are added early, so they are guaranteed to be set before +the client and server initialize for the first time. + +Other commands are added late, after all initialization is complete. +=============== +*/ +static void Com_AddEarlyCommands( qboolean clear ) { + int i; + char *s; + + for( i = 0; i < com_numCommands; i++ ) { + s = com_commands[i]; + if( !*s ) { + continue; + } + Cmd_TokenizeString( s, qfalse ); + if( strcmp( Cmd_Argv( 0 ), "set" ) ) { + continue; + } + if( Cmd_Argc() != 3 ) { + Com_Printf( "Usage: +set <variable> <value>\n" ); + } else { + Cvar_SetEx( Cmd_Argv( 1 ), Cmd_Argv( 2 ), CVAR_SET_COMMAND_LINE ); + } + if( clear ) { + *s = 0; + } + } +} + +/* +================= +Com_AddLateCommands + +Adds command line parameters as script statements +Commands lead with a + and continue until another + + +Returns qtrue if any late commands were added, which +will keep the demoloop from immediately starting + +Assumes +set commands are already filtered out +================= +*/ +static qboolean Com_AddLateCommands( void ) { + int i; + char *s; + qboolean ret = qfalse; + + for( i = 0; i < com_numCommands; i++ ) { + s = com_commands[i]; + if( !*s ) { + continue; + } + Cbuf_AddText( s ); + Cbuf_AddText( "\n" ); + ret = qtrue; + } + + if( ret ) { + Cbuf_Execute(); + } + + return ret; +} + + +/* +================= +Qcommon_Init +================= +*/ +void Qcommon_Init( char *commandLine ) { + static const char *version = APPLICATION " " VERSION " " __DATE__ " " BUILDSTRING " " CPUSTRING; + + if( _setjmp( abortframe ) ) + Sys_Error( "Error during initialization: %s", com_errorMsg ); + + Com_Printf( S_COLOR_CYAN "%s\n", version ); + + Com_ParseCommandLine( commandLine ); + + // prepare enough of the subsystems to handle + // cvar and command buffer management + Z_Init(); + MSG_Init(); + Cbuf_Init(); + Cmd_Init(); + Cvar_Init(); + Key_Init(); + Prompt_Init(); + Con_Init(); + + Com_FillAPI( &com ); + + // + // init commands and vars + // + host_speeds = Cvar_Get ("host_speeds", "0", 0); + developer = Cvar_Get ("developer", "0", 0); + timescale = Cvar_Get ("timescale", "1", CVAR_CHEAT ); + fixedtime = Cvar_Get ("fixedtime", "0", CVAR_CHEAT ); + logfile_active = Cvar_Get( "logfile", "0", 0 ); + logfile_flush = Cvar_Get( "logfile_flush", "0", 0 ); + logfile_name = Cvar_Get( "logfile_name", COM_LOGFILE_NAME, 0 ); + logfile_prefix = Cvar_Get( "logfile_prefix", "", 0 ); +#ifdef DEDICATED_ONLY + dedicated = Cvar_Get ("dedicated", "1", CVAR_ROM); +#else + dedicated = Cvar_Get ("dedicated", "0", CVAR_NOSET); +#endif + sv_running = Cvar_Get( "sv_running", "0", CVAR_ROM ); + sv_paused = Cvar_Get( "sv_paused", "0", CVAR_ROM ); + cl_running = Cvar_Get( "cl_running", "0", CVAR_ROM ); + cl_paused = Cvar_Get( "cl_paused", "0", CVAR_ROM ); + com_timedemo = Cvar_Get( "timedemo", "0", CVAR_CHEAT ); + com_sleep = Cvar_Get( "com_sleep", "1", 0 ); + com_date_format = Cvar_Get( "com_date_format", "%F", 0 ); + com_time_format = Cvar_Get( "com_time_format", "%T", 0 ); + com_debug_break = Cvar_Get( "com_debug_break", "0", 0 ); + Cvar_Get( "version", version, CVAR_SERVERINFO|CVAR_ROM ); + + Cmd_AddCommand ("z_stats", Z_Stats_f); + +#ifndef DEDICATED_ONLY + if( !dedicated->integer ) { + Cmd_AddCommand( "writeconfig", Com_WriteConfig_f ); + } +#endif + + Cmd_AddMacro( "com_date", Com_Date_m ); + Cmd_AddMacro( "com_time", Com_Time_m ); + + // add any system-wide configuration files + Sys_AddDefaultConfig(); + Cbuf_Execute(); + + // we need to add the early commands twice, because + // a basedir or cddir needs to be set before execing + // config files, but we want other parms to override + // the settings of the config files + Com_AddEarlyCommands( qfalse ); + + // do not accept CVAR_NOSET variable changes anymore + com_initialized = qtrue; + + Sys_Init(); + FS_Init(); + + Sys_RunConsole(); + + // after FS is initialized, open logfile + logfile_active->changed = logfile_active_changed; + logfile_flush->changed = logfile_param_changed; + logfile_name->changed = logfile_param_changed; + logfile_active_changed( logfile_active ); + + Cbuf_AddText( "exec "COM_DEFAULTCFG_NAME"\n" ); + Cbuf_Execute(); + + Cbuf_AddText( "exec "COM_CONFIG_NAME"\n" ); + Cbuf_Execute(); + + Cbuf_AddText( "exec "COM_AUTOEXECCFG_NAME"\n" ); + Cbuf_Execute(); + + Com_AddEarlyCommands( qtrue ); + +#ifdef _DEBUG + Cmd_AddCommand( "error", Com_Error_f ); + Cmd_AddCommand( "errordrop", Com_ErrorDrop_f ); + Cmd_AddCommand( "freeze", Com_Freeze_f ); + Cmd_AddCommand( "crash", Com_Crash_f ); + Cmd_AddCommand( "vstest", Com_VsnprintfTest_f ); +#endif + + Cmd_AddCommand( "lasterror", Com_LastError_f ); + + Cmd_AddCommand( "quit", Com_Quit ); + + srand( Sys_Milliseconds() ); + + Netchan_Init(); + CM_Init(); + SV_Init(); + CL_Init(); + NET_Init(); + + if( dedicated->integer ) { + NET_Config( NET_SERVER ); + } + + Sys_RunConsole(); + + // add + commands from command line + if( !Com_AddLateCommands() ) { + // if the user didn't give any commands, run default action + if( dedicated->integer ) { + Cbuf_AddText( "dedicated_start\n" ); + } else { + // TODO + //Cbuf_AddText( "d1\n" ); + } + Cbuf_Execute(); + } else { + // the user asked for something explicit + // so drop the loading plaque + SCR_EndLoadingPlaque(); + } + + + Com_Printf( "====== " APPLICATION " initialized ======\n\n" ); + Com_Printf( S_COLOR_CYAN APPLICATION " " VERSION ", " __DATE__ "\n" +#ifdef USE_ZLIB + S_COLOR_RESET "w/ zlib " ZLIB_VERSION "\n" +#endif + ); + Com_Printf( "http://q2pro.sf.net\n\n" ); + + com_numCommands = 0; + + com_eventTime = Sys_Realtime(); +} + +/* +============== +Com_ProcessEvents +============== +*/ +void Com_ProcessEvents( void ) { + neterr_t ret; + + do { + ret = NET_GetPacket( NS_SERVER ); + if( ret == NET_AGAIN ) { + break; + } + SV_PacketEvent( ret ); + } while( ret == NET_OK ); + + Sys_RunConsole(); + +#ifndef DEDICATED_ONLY + do { + ret = NET_GetPacket( NS_CLIENT ); + if( ret == NET_AGAIN ) { + break; + } + if( cl_running->integer ) { + CL_PacketEvent( ret ); + } + } while( ret == NET_OK ); + + CL_PumpEvents(); + CL_InputFrame(); +#endif +} + +#ifndef DEDICATED_ONLY +/* +============== +Com_ProcessLoopback +============== +*/ +static void Com_ProcessLoopback( void ) { + int i; + + memset( &net_from, 0, sizeof( net_from ) ); + net_from.type = NA_LOOPBACK; + + // Process loopback packets + for( i = 0; i < 2; i++ ) { + while( NET_GetLoopPacket( NS_SERVER ) ) { + if( sv_running->integer ) { + SV_PacketEvent( NET_OK ); + } + } + + while( NET_GetLoopPacket( NS_CLIENT ) ) { + if( cl_running->integer ) { + CL_PacketEvent( NET_OK ); + } + } + } +} +#endif + +/* +================= +Qcommon_Frame +================= +*/ +void Qcommon_Frame( void ) { + int time_before, time_event, time_between, time_after; + uint32 realTime, msec; + + if( _setjmp( abortframe ) ) { + return; // an ERR_DROP was thrown + } + + time_before = time_event = time_between = time_after = 0; + + if( host_speeds->integer ) + time_before = Sys_Milliseconds(); + + do { + Com_ProcessEvents(); + realTime = Sys_Realtime(); + if( com_eventTime > realTime ) { + com_eventTime = realTime; + } + msec = realTime - com_eventTime; + } while( msec < 1 ); + com_eventTime = realTime; + + if( msec > 250 ) { + Com_DPrintf( "Hitch warning: %u msec frame time\n", msec ); + msec = 100; // time was unreasonable, + // host OS was hibernated or something + } + + if( fixedtime->integer ) { + Cvar_ClampInteger( fixedtime, 1, 1000 ); + msec = fixedtime->integer; + } else if( timescale->value > 0 ) { + msec *= timescale->value; + if( msec < 1 ) + msec = 1; + } + + // this is the only place where console commands are processed. + Cbuf_Execute(); + + if( host_speeds->integer ) + time_event = Sys_Milliseconds(); + + SV_Frame( msec ); + + if( host_speeds->integer ) + time_between = Sys_Milliseconds(); + +#ifndef DEDICATED_ONLY + Com_ProcessLoopback(); +#endif + + CL_Frame( msec ); + + if( host_speeds->integer ) + time_after = Sys_Milliseconds(); + + if( host_speeds->integer ) { + int all, ev, sv, gm, cl, rf; + + all = time_after - time_before; + ev = time_event - time_before; + sv = time_between - time_event; + cl = time_after - time_between; + gm = time_after_game - time_before_game; + rf = time_after_ref - time_before_ref; + sv -= gm; + cl -= rf; + + Com_Printf( "all:%3i ev:%3i sv:%3i gm:%3i cl:%3i rf:%3i\n", + all, ev, sv, gm, cl, rf ); + } + + cvar_infoModified = 0; + + com_localTime += msec; + com_framenum++; +} + +/* +================= +Qcommon_Shutdown +================= +*/ +void Qcommon_Shutdown( qboolean fatalError ) { +#ifndef DEDICATED_ONLY + if( !fatalError && dedicated && !dedicated->integer ) { + Com_WriteConfiguration( COM_CONFIG_NAME ); + } +#endif + NET_Shutdown(); + LogFile_Close(); + FS_Shutdown( qtrue ); +} + |