This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/engine/common.c

1990 lines
42 KiB
C

//=======================================================================
// Copyright XashXT Group 2008 ©
// common.c - misc functions used in client and server
//=======================================================================
#include "common.h"
#include "basefiles.h"
#include "mathlib.h"
/*
===============================================================================
SYSTEM IO
===============================================================================
*/
void Sys_Error( const char *error, ... )
{
char errorstring[MAX_INPUTLINE];
static bool recursive = false;
va_list argptr;
va_start( argptr, error );
com.vsprintf( errorstring, error, argptr );
va_end( argptr );
// don't multiple executes
if( recursive )
{
// echo to system console and log
Sys_Print( va("Sys_RecursiveError: %s\n", errorstring ));
return;
}
recursive = true;
// prepare host to close
com.sprintf( host.finalmsg, "Server fatal crashed: %s\n", errorstring );
host.state = HOST_ERROR; // lock shutdown state
Host_FreeRender(); // close render to show message error
com.error("%s", errorstring );
}
/*
================
Sys_SendKeyEvents
Send Key_Event calls
================
*/
void Sys_SendKeyEvents( void )
{
// grab frame time
host.sv_timer = Sys_GetKeyEvents();
host.cl_timer = host.realtime * 1000;
host.realtime = Sys_DoubleTime();
}
/*
=======================================================================
VIRTUAL MACHINE COMMON UTILS
=======================================================================
*/
bool VM_ValidateArgs( const char *builtin, int num_argc )
{
if( prog->argc < num_argc )
{
MsgDev( D_ERROR, "%s called with too few parameters\n", builtin );
return false;
}
else if( prog->argc < num_argc )
{
MsgDev( D_ERROR, "%s called with too many parameters\n", builtin );
return false;
}
return true;
}
void VM_ValidateString( const char *s )
{
if( s[0] <= ' ' ) PRVM_ERROR( "%s: bad string\n", PRVM_NAME );
}
/*
=========
VM_VarArgs
supports follow prefixes:
%d - integer or bool (declared as float)
%i - integer or bool (declared as float)
%f - float
%g - formatted float with cutoff zeroes
%s - string
%p - function pointer (will be printed function name)
%e - entity (will be print entity number) - just in case
%v - vector (format: %g %g %g)
=========
*/
const char *VM_VarArgs( int start_arg )
{
static char vm_string[MAX_STRING_CHARS];
int arg = start_arg + 1;// skip format string
char *out, *outend;
static string vm_arg;
const char *s, *m;
mfunction_t *func;
float *vec;
// get the format string
s = PRVM_G_STRING((OFS_PARM0 + start_arg * 3));
out = vm_string;
outend = out + MAX_STRING_CHARS - 1;
memset( vm_string, 0, MAX_STRING_CHARS - 1 );
while( out < outend && *s )
{
if( arg > prog->argc ) break; // simple boundschecker
if( *s != '%' )
{
*out++ = *s++; // copy symbols
continue; // wait for percents
}
switch((int)s[1])
{
case 'd': com.snprintf( vm_arg, MAX_STRING, "%d", (int)PRVM_G_FLOAT(OFS_PARM0+arg*3)); break;
case 'i': com.snprintf( vm_arg, MAX_STRING, "%i", (int)PRVM_G_FLOAT(OFS_PARM0+arg*3)); break;
case 's': com.snprintf( vm_arg, MAX_STRING, "%s", PRVM_G_STRING(OFS_PARM0+arg*3)); break;
case 'f': com.snprintf( vm_arg, MAX_STRING, "%f", PRVM_G_FLOAT(OFS_PARM0+arg*3)); break;
case 'g': com.snprintf( vm_arg, MAX_STRING, "%g", PRVM_G_FLOAT(OFS_PARM0+arg*3)); break;
case 'e': com.snprintf( vm_arg, MAX_STRING, "%i", PRVM_G_EDICTNUM(OFS_PARM0+arg*3)); break;
case 'p': // function ptr
func = prog->functions + PRVM_G_INT(OFS_PARM0+arg*3);
if(!func->s_name) com.strncpy( vm_arg, "(null)", MAX_STRING ); // MSVCRT style
else com.snprintf( vm_arg, MAX_STRING, "%s", PRVM_GetString(func->s_name));
break;
case 'v': // vector
vec = PRVM_G_VECTOR((OFS_PARM0+arg*3));
com.snprintf( vm_arg, MAX_STRING, "%g %g %g", vec[0], vec[1], vec[2] );
break;
default:
arg++; // skip invalid arg
continue;
}
s += 2;
m = vm_arg, arg++;
while( out < outend && *m )
*out++ = *m++; // copy next arg
}
return vm_string;
}
vfile_t *VM_GetFileHandle( int index )
{
if( index < 0 || index >= PRVM_MAX_OPENFILES )
{
Msg("VM_GetFileHandle: invalid file handle %i\n", index );
return NULL;
}
if( prog->file[index] == NULL )
{
Msg("VM_GetFileHandle: no such file handle %i (or file has been closed)\n", index );
return NULL;
}
return prog->file[index];
}
void VM_Files_Init( void )
{
int i;
for( i = 0; i < PRVM_MAX_OPENFILES; i++ )
prog->file[i] = NULL;
}
void VM_Files_CloseAll( void )
{
int i;
for( i = 0; i < PRVM_MAX_OPENFILES; i++ )
{
if( prog->file[i] )
FS_Close(VFS_Close(prog->file[i]));
prog->file[i] = NULL;
}
}
void VM_Cmd_Init( void )
{
// only init the stuff for the current prog
VM_Files_Init();
}
void VM_Cmd_Reset( void )
{
VM_Files_CloseAll();
}
/*
=======================================================================
VIRTUAL MACHINE GENERIC API
=======================================================================
*/
/*
=========
VM_ConPrintf
void Con_Printf( ... )
=========
*/
void VM_ConPrintf( void )
{
com.print(VM_VarArgs( 0 ));
}
/*
=========
VM_ConDPrintf
void Con_DPrintf( float level, ... )
=========
*/
void VM_ConDPrintf( void )
{
if(host.developer < (int)PRVM_G_FLOAT(OFS_PARM0))
return;
com.print(VM_VarArgs( 1 ));
}
/*
=========
VM_HostError
void Com_Error( ... )
=========
*/
void VM_HostError( void )
{
edict_t *ed;
const char *s = VM_VarArgs( 0 );
Msg( "====== %s ERROR in %s:\n%s\n", PRVM_NAME, PRVM_GetString(prog->xfunction->s_name), s );
if( prog->pev )
{
ed = PRVM_G_EDICT( prog->pev->ofs );
PRVM_ED_Print( ed );
}
Host_Error("%s: Program error in function %s:\n%s\n", PRVM_NAME, PRVM_GetString(prog->xfunction->s_name), s );
}
/*
=================
VM_EdictError
void Ed_Error( ... )
=================
*/
void VM_EdictError( void )
{
edict_t *ed;
const char *s = VM_VarArgs( 0 );
Msg( "======OBJECT ERROR======\n", PRVM_NAME, PRVM_GetString(prog->xfunction->s_name), s );
if( prog->pev )
{
ed = PRVM_G_EDICT( prog->pev->ofs );
PRVM_ED_Print( ed );
PRVM_ED_Free( ed );
}
else PRVM_ERROR ("VM_objecterror: pev not defined !");
Msg("%s OBJECT ERROR in %s:\n%s\n", PRVM_NAME, PRVM_GetString(prog->xfunction->s_name), s );
}
/*
=========
VM_SysExit
void Sys_Exit( void )
=========
*/
void VM_SysExit( void )
{
if(!VM_ValidateArgs( "Sys_Exit", 0 ))
return;
// using only for debugging :)
if( host.developer ) com.exit();
}
/*
=========
VM_CmdArgv
string Cmd_Argv( float arg )
=========
*/
void VM_CmdArgv( void )
{
int arg;
if(!VM_ValidateArgs( "Cmd_Argv", 1 ))
return;
arg = (int)PRVM_G_FLOAT(OFS_PARM0);
if( arg >= 0 && arg < Cmd_Argc())
PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(Cmd_Argv( arg ));
else PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString( NULL );
}
/*
=========
VM_CmdArgc
float Cmd_Argc( void )
=========
*/
void VM_CmdArgc( void )
{
if(!VM_ValidateArgs( "Cmd_Argc", 0 ))
return;
PRVM_G_FLOAT(OFS_RETURN) = Cmd_Argc();
}
/*
=========
VM_ComTrace
void Com_Trace( float enable )
=========
*/
void VM_ComTrace( void )
{
if(!VM_ValidateArgs( "Com_Trace", 1 ))
return;
if(PRVM_G_FLOAT(OFS_PARM0))
prog->trace = true;
else prog->trace = false;
}
/*
=========
VM_ComFileExists
float Com_FileExists( string filename )
=========
*/
void VM_ComFileExists( void )
{
if(!VM_ValidateArgs( "Com_FileExists", 1 ))
return;
VM_ValidateString(PRVM_G_STRING(OFS_PARM0));
PRVM_G_FLOAT(OFS_RETURN) = FS_FileExists(PRVM_G_STRING(OFS_PARM0)) ? 1.0f : 0.0f;
}
/*
=========
VM_ComFileSize
float Com_FileSize( string filename )
=========
*/
void VM_ComFileSize( void )
{
if(!VM_ValidateArgs( "Com_FileSize", 1 ))
return;
VM_ValidateString(PRVM_G_STRING(OFS_PARM0));
PRVM_G_FLOAT(OFS_RETURN) = (float)FS_FileSize(PRVM_G_STRING(OFS_PARM0));
}
/*
=========
VM_ComFileTime
float Com_FileTime( string filename )
=========
*/
void VM_ComFileTime( void )
{
if(!VM_ValidateArgs( "Com_FileTime", 1 ))
return;
VM_ValidateString(PRVM_G_STRING(OFS_PARM0));
PRVM_G_FLOAT(OFS_RETURN) = (float)FS_FileTime(PRVM_G_STRING(OFS_PARM0));
}
/*
=========
VM_ComLoadScript
float Com_LoadScript( string filename )
=========
*/
void VM_ComLoadScript( void )
{
static byte *script;
static size_t length;
if(!VM_ValidateArgs( "Com_LoadScript", 1 ))
return;
VM_ValidateString(PRVM_G_STRING(OFS_PARM0));
// place script into memory
if( script && length ) Mem_Free( script );
script = FS_LoadFile( PRVM_G_STRING(OFS_PARM0), &length );
PRVM_G_FLOAT(OFS_RETURN) = (float)Com_LoadScript( "VM script", script, length );
}
/*
=========
VM_ComResetScript
void Com_ResetScript( void )
=========
*/
void VM_ComResetScript( void )
{
if(!VM_ValidateArgs( "Com_ResetScript", 0 ))
return;
Com_ResetScript();
}
/*
=========
VM_ComReadToken
string Com_ReadToken( float newline )
=========
*/
void VM_ComReadToken( void )
{
if(!VM_ValidateArgs( "Com_ReadToken", 1 ))
return;
// using slow, but safe parsing method
if( PRVM_G_FLOAT(OFS_PARM0) )
{
Com_GetToken( true ); // newline token is always safe
PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString( com_token );
}
else
{
if(Com_TryToken()) // token available
PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString( com_token );
else PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString( NULL );
}
}
/*
=========
VM_ComFilterToken
float Com_Filter( string mask, string s, float casecmp )
=========
*/
void VM_ComFilterToken( void )
{
const char *mask, *name;
bool casecmp;
if(!VM_ValidateArgs( "Com_Filter", 3 ))
return;
VM_ValidateString(PRVM_G_STRING(OFS_PARM0));
VM_ValidateString(PRVM_G_STRING(OFS_PARM1));
mask = PRVM_G_STRING(OFS_PARM0);
name = PRVM_G_STRING(OFS_PARM1);
casecmp = PRVM_G_FLOAT(OFS_PARM2) ? true : false;
PRVM_G_FLOAT(OFS_RETURN) = (float)Com_Filter( (char *)mask, (char *)name, casecmp );
}
/*
=========
VM_ComSearchFiles
float Com_Search( string mask, float casecmp )
=========
*/
void VM_ComSearchFiles( void )
{
const char *mask;
bool casecmp;
int numfiles = 0;
if(!VM_ValidateArgs( "Com_Search", 2 ))
return;
VM_ValidateString(PRVM_G_STRING(OFS_PARM0));
// release previous search
if( prog->search ) Mem_Free( prog->search );
mask = PRVM_G_STRING(OFS_PARM0);
casecmp = PRVM_G_FLOAT(OFS_PARM1) ? true : false;
prog->search = FS_Search( mask, casecmp );
if( prog->search ) numfiles = prog->search->numfilenames;
PRVM_G_FLOAT(OFS_RETURN) = (float)numfiles;
}
/*
=========
VM_ComSearchNames
string Com_SearchFilename( float num )
=========
*/
void VM_ComSearchNames( void )
{
int num;
if(!VM_ValidateArgs( "Com_SearchFilename", 1 ))
return;
num = PRVM_G_FLOAT(OFS_PARM0);
if( prog->search && prog->search->numfilenames > num ) // in-range
PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString( prog->search->filenames[num] );
else PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString( NULL );
}
/*
=========
VM_RandomLong
float RandomLong( float min, float max )
=========
*/
void VM_RandomLong( void )
{
if(!VM_ValidateArgs( "RandomLong", 2 ))
return;
PRVM_G_FLOAT(OFS_RETURN) = Com_RandomLong(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
}
/*
=========
VM_RandomFloat
float RandomFloat( float min, float max )
=========
*/
void VM_RandomFloat( void )
{
if(!VM_ValidateArgs( "RandomFloat", 2 ))
return;
PRVM_G_FLOAT(OFS_RETURN) = Com_RandomFloat(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
}
/*
=========
VM_RandomVector
float RandomVector( vector min, vector max )
=========
*/
void VM_RandomVector( void )
{
vec3_t temp;
float *min, *max;
if(!VM_ValidateArgs( "RandomVector", 2 ))
return;
min = PRVM_G_VECTOR(OFS_PARM0);
max = PRVM_G_VECTOR(OFS_PARM1);
temp[0] = RANDOM_FLOAT(min[0], max[0]);
temp[1] = RANDOM_FLOAT(min[1], max[1]);
temp[2] = RANDOM_FLOAT(min[2], max[2]);
VectorNormalize2( temp, PRVM_G_VECTOR(OFS_RETURN));
}
/*
=========
VM_CvarRegister
void Cvar_Register( string name, string value, float flags )
=========
*/
void VM_CvarRegister( void )
{
const char *name, *value;
int flags = 0;
if(!VM_ValidateArgs( "Cvar_Register", 3 ))
return;
VM_ValidateString(PRVM_G_STRING(OFS_PARM0));
VM_ValidateString(PRVM_G_STRING(OFS_PARM1));
name = PRVM_G_STRING(OFS_PARM0);
value = PRVM_G_STRING(OFS_PARM1);
if((int)PRVM_G_FLOAT(OFS_PARM2) & 1)
flags |= CVAR_ARCHIVE;
if((int)PRVM_G_FLOAT(OFS_PARM2) & 2)
flags |= CVAR_CHEAT;
// register new cvar
Cvar_Get( name, value, flags );
}
/*
=========
VM_CvarSetValue
void Cvar_SetValue( string name, float value )
=========
*/
void VM_CvarSetValue( void )
{
const char *name;
float value;
if(!VM_ValidateArgs( "Cvar_SetValue", 2 ))
return;
VM_ValidateString(PRVM_G_STRING(OFS_PARM0));
name = PRVM_G_STRING(OFS_PARM0);
value = PRVM_G_FLOAT(OFS_PARM1);
// set new value
Cvar_SetValue( name, value );
}
/*
=========
VM_CvarSetString
void Cvar_SetString( string name, string value )
=========
*/
void VM_CvarSetString( void )
{
const char *name, *value;
if(!VM_ValidateArgs( "Cvar_SetString", 2 ))
return;
VM_ValidateString(PRVM_G_STRING(OFS_PARM0));
VM_ValidateString(PRVM_G_STRING(OFS_PARM1));
name = PRVM_G_STRING(OFS_PARM0);
value = PRVM_G_STRING(OFS_PARM1);
// set new value
Cvar_Set( name, value );
}
/*
=========
VM_CvarGetValue
float Cvar_GetValue( string name )
=========
*/
void VM_CvarGetValue( void )
{
const char *name;
if(!VM_ValidateArgs( "Cvar_GetValue", 1 ))
return;
VM_ValidateString(PRVM_G_STRING(OFS_PARM0));
name = PRVM_G_STRING(OFS_PARM0);
PRVM_G_FLOAT(OFS_RETURN) = Cvar_VariableValue( name );
}
/*
=================
VM_LocalCmd
cmd( string, ... )
=================
*/
void VM_LocalCmd( void )
{
Cbuf_AddText(VM_VarArgs( 0 ));
}
/*
=========
VM_ComVA
string va( ... )
=========
*/
void VM_ComVA( void )
{
PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(VM_VarArgs( 0 ));
}
/*
=========
VM_ComStrlen
float strlen( string text )
=========
*/
void VM_ComStrlen( void )
{
if(!VM_ValidateArgs( "Com_Strlen", 1 )) return;
PRVM_G_FLOAT(OFS_RETURN) = com.cstrlen(PRVM_G_STRING(OFS_PARM0));
}
/*
=========
VM_TimeStamp
string Com_TimeStamp( float format )
=========
*/
void VM_TimeStamp( void )
{
if(!VM_ValidateArgs( "Com_TimeStamp", 1 ))
return;
PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(timestamp((int)PRVM_G_FLOAT(OFS_PARM0)));
}
/*
=========
VM_SpawnEdict
entity spawn( void )
=========
*/
void VM_SpawnEdict( void )
{
edict_t *ed;
prog->xfunction->builtinsprofile += 20;
ed = PRVM_ED_Alloc();
VM_RETURN_EDICT( ed );
}
/*
=========
VM_RemoveEdict
void remove( entity ent )
=========
*/
void VM_RemoveEdict( void )
{
edict_t *ed;
if(!VM_ValidateArgs( "remove", 1 ))
return;
prog->xfunction->builtinsprofile += 20;
ed = PRVM_G_EDICT(OFS_PARM0);
if( PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts )
{
if( host.developer >= D_INFO )
VM_Warning( "VM_RemoveEdict: tried to remove the null entity or a reserved entity!\n" );
}
else if( ed->priv.ed->free )
{
if( host.developer >= D_INFO )
VM_Warning( "VM_RemoveEdict: tried to remove an already freed entity!\n" );
}
else PRVM_ED_Free( ed );
}
/*
=========
VM_NextEdict
void nextent( entity ent )
=========
*/
void VM_NextEdict( void )
{
edict_t *ent;
int i;
if(!VM_ValidateArgs( "nextent", 1 ))
return;
i = PRVM_G_EDICTNUM( OFS_PARM0 );
while( 1 )
{
prog->xfunction->builtinsprofile++;
i++;
if( i == prog->num_edicts )
{
VM_RETURN_EDICT( prog->edicts );
return;
}
ent = PRVM_EDICT_NUM(i);
if(!ent->priv.ed->free)
{
VM_RETURN_EDICT( ent );
return;
}
}
}
/*
=================
VM_copyentity
void copyentity( entity src, entity dst )
=================
*/
void VM_CopyEdict( void )
{
edict_t *in, *out;
if(!VM_ValidateArgs( "copyentity", 1 ))
return;
in = PRVM_G_EDICT(OFS_PARM0);
out = PRVM_G_EDICT(OFS_PARM1);
Mem_Copy( out->progs.vp, in->progs.vp, prog->progs->entityfields * 4 );
}
/*
=========
VM_FindEdict
entity find( entity start, .string field, string match )
=========
*/
void VM_FindEdict( void )
{
int e, f;
const char *s, *t;
edict_t *ed;
if(!VM_ValidateArgs( "find", 2 ))
return;
e = PRVM_G_EDICTNUM(OFS_PARM0);
f = PRVM_G_INT(OFS_PARM1);
s = PRVM_G_STRING(OFS_PARM2);
if(!s) s = "";
for( e++; e < prog->num_edicts; e++ )
{
prog->xfunction->builtinsprofile++;
ed = PRVM_EDICT_NUM(e);
if( ed->priv.ed->free )continue;
t = PRVM_GetString(*(string_t *)&((float*)ed->progs.vp)[f]);
if(!t) t = "";
if(!com.strcmp( t, s ))
{
VM_RETURN_EDICT( ed );
return;
}
}
VM_RETURN_EDICT( prog->edicts );
}
/*
=========
VM_FindField
entity findfloat(entity start, .float field, float match)
=========
*/
void VM_FindField( void )
{
int e, f;
float s;
edict_t *ed;
if(!VM_ValidateArgs( "findfloat", 2 ))
return;
e = PRVM_G_EDICTNUM(OFS_PARM0);
f = PRVM_G_INT(OFS_PARM1);
s = PRVM_G_FLOAT(OFS_PARM2);
for( e++; e < prog->num_edicts; e++ )
{
prog->xfunction->builtinsprofile++;
ed = PRVM_EDICT_NUM(e);
if( ed->priv.ed->free ) continue;
if(((float*)ed->progs.vp)[f] == s )
{
VM_RETURN_EDICT( ed );
return;
}
}
VM_RETURN_EDICT( prog->edicts );
}
/*
=======================================================================
VIRTUAL MACHINE FILESYSTEM API
=======================================================================
*/
/*
=========
VM_FS_Open
float fopen( string filename, float mode )
=========
*/
void VM_FS_Open( void )
{
int filenum, mode;
const char *modestring, *filename;
if(!VM_ValidateArgs( "fopen", 2 )) return;
for( filenum = 0; filenum < PRVM_MAX_OPENFILES; filenum++ )
if( prog->file[filenum] == NULL ) break;
if( filenum >= PRVM_MAX_OPENFILES )
{
PRVM_G_FLOAT(OFS_RETURN) = -2;
VM_Warning( "fopen: %s file handles limit exceeded( max %i )\n", PRVM_NAME, PRVM_MAX_OPENFILES );
return;
}
mode = (int)PRVM_G_FLOAT( OFS_PARM1 );
switch( mode )
{
case 0: modestring = "rb"; break; // FILE_READ
case 1: modestring = "ab"; break; // FILE_APPEND
case 2: modestring = "wb"; break; // FILE_WRITE
default:
PRVM_G_FLOAT(OFS_RETURN) = -3;
VM_Warning( "fopen: no such mode %i\n", mode );
return;
}
filename = PRVM_G_STRING(OFS_PARM0);
VM_ValidateString(PRVM_G_STRING(OFS_PARM0));
prog->file[filenum] = VFS_Open(FS_Open( filename, modestring ), modestring );
if (prog->file[filenum] == NULL)
{
PRVM_G_FLOAT(OFS_RETURN) = -1;
VM_Warning("fopen: can't open %s [%s]\n", filename, modestring );
}
else PRVM_G_FLOAT(OFS_RETURN) = filenum;
}
/*
=========
VM_FS_Close
float fclose( float handle )
=========
*/
void VM_FS_Close( void )
{
vfile_t *handle;
if(!VM_ValidateArgs( "fclose", 2 )) return;
handle = VM_GetFileHandle((int)PRVM_G_FLOAT( OFS_PARM0 ));
if(!handle) return;
FS_Close(VFS_Close( handle ));
}
/*
=========
VM_FS_Gets
string fgets( float handle )
=========
*/
void VM_FS_Gets( void )
{
static char string[MAX_INPUTLINE];
vfile_t *handle;
int c;
if(!VM_ValidateArgs( "fgets", 1 )) return;
handle = VM_GetFileHandle((int)PRVM_G_FLOAT( OFS_PARM0 ));
if(!handle) return;
c = VFS_Gets( handle, string, MAX_INPUTLINE );
if( c >= 0 ) PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString( string );
else PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString( NULL );
}
/*
=========
VM_FS_Puts
string fputs( float handle )
=========
*/
void VM_FS_Puts( void )
{
vfile_t *handle;
const char *s;
if(!VM_ValidateArgs( "fputs", 1 )) return;
handle = VM_GetFileHandle((int)PRVM_G_FLOAT( OFS_PARM0 ));
if(!handle) return;
s = VM_VarArgs( 1 );
VFS_Print( handle, s );
}
/*
=======================================================================
VIRTUAL MACHINE MATHLIB
=======================================================================
*/
/*
=================
VM_min
float min( float a, float b )
=================
*/
void VM_min( void )
{
if(!VM_ValidateArgs( "min", 2 )) return;
PRVM_G_FLOAT(OFS_RETURN) = min(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
}
/*
=================
VM_max
float max( float a, float b )
=================
*/
void VM_max( void )
{
if(!VM_ValidateArgs( "max", 2 )) return;
PRVM_G_FLOAT(OFS_RETURN) = max(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
}
/*
=================
VM_bound
float bound( float min, float value, float max )
=================
*/
void VM_bound( void )
{
if(!VM_ValidateArgs( "bound", 3 )) return;
PRVM_G_FLOAT(OFS_RETURN) = bound(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1), PRVM_G_FLOAT(OFS_PARM2));
}
/*
=================
VM_pow
float pow( float a, float b )
=================
*/
void VM_pow( void )
{
if(!VM_ValidateArgs( "pow", 2 )) return;
PRVM_G_FLOAT(OFS_RETURN) = pow(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
}
/*
=========
VM_sin
float sin( float f )
=========
*/
void VM_sin( void )
{
if(!VM_ValidateArgs( "sin", 1 )) return;
PRVM_G_FLOAT(OFS_RETURN) = sin(PRVM_G_FLOAT(OFS_PARM0));
}
/*
=========
VM_cos
float cos( float f )
=========
*/
void VM_cos( void )
{
if(!VM_ValidateArgs( "cos", 1 )) return;
PRVM_G_FLOAT(OFS_RETURN) = cos(PRVM_G_FLOAT(OFS_PARM0));
}
/*
=========
VM_tan
float tan( float f )
=========
*/
void VM_tan( void )
{
if(!VM_ValidateArgs( "tan", 1 )) return;
PRVM_G_FLOAT(OFS_RETURN) = tan(PRVM_G_FLOAT(OFS_PARM0));
}
/*
=========
VM_asin
float asin( float f )
=========
*/
void VM_asin( void )
{
if(!VM_ValidateArgs( "asin", 1 )) return;
PRVM_G_FLOAT(OFS_RETURN) = asin(PRVM_G_FLOAT(OFS_PARM0));
}
/*
=========
VM_acos
float acos( float f )
=========
*/
void VM_acos( void )
{
if(!VM_ValidateArgs( "acos", 1 )) return;
PRVM_G_FLOAT(OFS_RETURN) = acos(PRVM_G_FLOAT(OFS_PARM0));
}
/*
=========
VM_atan
float atan( float f )
=========
*/
void VM_atan( void )
{
if(!VM_ValidateArgs( "atan", 1 )) return;
PRVM_G_FLOAT(OFS_RETURN) = atan(PRVM_G_FLOAT(OFS_PARM0));
}
/*
=========
VM_sqrt
float sqrt( float f )
=========
*/
void VM_sqrt( void )
{
if(!VM_ValidateArgs( "sqrt", 1 )) return;
PRVM_G_FLOAT(OFS_RETURN) = sqrt(PRVM_G_FLOAT(OFS_PARM0));
}
/*
=========
VM_rint
float rint( float f )
=========
*/
void VM_rint( void )
{
float f;
if(!VM_ValidateArgs( "rint", 1 ))
return;
f = PRVM_G_FLOAT(OFS_PARM0);
if( f > 0 ) PRVM_G_FLOAT(OFS_RETURN) = floor(f + 0.5);
else PRVM_G_FLOAT(OFS_RETURN) = ceil(f - 0.5);
}
/*
=========
VM_floor
float floor( float f )
=========
*/
void VM_floor( void )
{
if(!VM_ValidateArgs( "floor", 1 )) return;
PRVM_G_FLOAT(OFS_RETURN) = floor(PRVM_G_FLOAT(OFS_PARM0));
}
/*
=========
VM_ceil
float ceil( float f )
=========
*/
void VM_ceil( void )
{
if(!VM_ValidateArgs( "ceil", 1 )) return;
PRVM_G_FLOAT(OFS_RETURN) = ceil(PRVM_G_FLOAT(OFS_PARM0));
}
/*
=========
VM_fabs
float fabs( float f )
=========
*/
void VM_fabs( void )
{
if(!VM_ValidateArgs( "fabs", 1 )) return;
PRVM_G_FLOAT(OFS_RETURN) = fabs(PRVM_G_FLOAT(OFS_PARM0));
}
/*
=========
VM_modulo
float mod( float val, float m )
=========
*/
void VM_mod( void )
{
int val, m;
if(!VM_ValidateArgs( "fmod", 2 )) return;
val = (int)PRVM_G_FLOAT(OFS_PARM0);
m = (int)PRVM_G_FLOAT(OFS_PARM1);
PRVM_G_FLOAT(OFS_RETURN) = (float)(val % m);
}
/*
=========
VM_VectorNormalize
vector VectorNormalize( vector v )
=========
*/
void VM_VectorNormalize( void )
{
if(!VM_ValidateArgs( "normalize", 1 )) return;
VectorNormalize2( PRVM_G_VECTOR(OFS_PARM0), PRVM_G_VECTOR(OFS_RETURN));
}
/*
=========
VM_VectorLength
float VectorLength( vector v )
=========
*/
void VM_VectorLength( void )
{
if(!VM_ValidateArgs( "veclength", 1 )) return;
PRVM_G_FLOAT(OFS_RETURN) = VectorLength(PRVM_G_VECTOR(OFS_PARM0));
}
prvm_builtin_t std_builtins[] =
{
NULL, // #0 (leave blank as default, but can include easter egg )
// system events
VM_ConPrintf, // #1 void Con_Printf( ... )
VM_ConDPrintf, // #2 void Con_DPrintf( float level, ... )
VM_HostError, // #3 void Com_Error( ... )
VM_SysExit, // #4 void Sys_Exit( void )
VM_CmdArgv, // #5 string Cmd_Argv( float arg )
VM_CmdArgc, // #6 float Cmd_Argc( void )
NULL, // #7 -- reserved --
NULL, // #8 -- reserved --
NULL, // #9 -- reserved --
NULL, // #10 -- reserved --
// common tools
VM_ComTrace, // #11 void Com_Trace( float enable )
VM_ComFileExists, // #12 float Com_FileExists( string filename )
VM_ComFileSize, // #13 float Com_FileSize( string filename )
VM_ComFileTime, // #14 float Com_FileTime( string filename )
VM_ComLoadScript, // #15 float Com_LoadScript( string filename )
VM_ComResetScript, // #16 void Com_ResetScript( void )
VM_ComReadToken, // #17 string Com_ReadToken( float newline )
VM_ComFilterToken, // #18 float Com_Filter( string mask, string s, float casecmp )
VM_ComSearchFiles, // #19 float Com_Search( string mask, float casecmp )
VM_ComSearchNames, // #20 string Com_SearchFilename( float num )
VM_RandomLong, // #21 float RandomLong( float min, float max )
VM_RandomFloat, // #22 float RandomFloat( float min, float max )
VM_RandomVector, // #23 vector RandomVector( vector min, vector max )
VM_CvarRegister, // #24 void Cvar_Register( string name, string value, float flags )
VM_CvarSetValue, // #25 void Cvar_SetValue( string name, float value )
VM_CvarGetValue, // #26 float Cvar_GetValue( string name )
VM_CvarSetString, // #27 void Cvar_SetString( string name, string value )
VM_ComVA, // #28 string va( ... )
VM_ComStrlen, // #29 float strlen( string text )
VM_TimeStamp, // #30 string Com_TimeStamp( float format )
VM_LocalCmd, // #31 void LocalCmd( ... )
NULL, // #32 -- reserved --
NULL, // #33 -- reserved --
NULL, // #34 -- reserved --
NULL, // #35 -- reserved --
NULL, // #36 -- reserved --
NULL, // #37 -- reserved --
NULL, // #38 -- reserved --
NULL, // #39 -- reserved --
NULL, // #40 -- reserved --
// quakec intrinsics ( compiler will be lookup this functions, don't remove or rename )
VM_SpawnEdict, // #41 entity spawn( void )
VM_RemoveEdict, // #42 void remove( entity ent )
VM_NextEdict, // #43 entity nextent( entity ent )
VM_CopyEdict, // #44 void copyentity( entity src, entity dst )
NULL, // #45 -- reserved --
NULL, // #46 -- reserved --
NULL, // #47 -- reserved --
NULL, // #48 -- reserved --
NULL, // #49 -- reserved --
NULL, // #50 -- reserved --
// filesystem
VM_FS_Open, // #51 float fopen( string filename, float mode )
VM_FS_Close, // #52 void fclose( float handle )
VM_FS_Gets, // #53 string fgets( float handle )
VM_FS_Puts, // #54 void fputs( float handle, string s )
NULL, // #55 -- reserved --
NULL, // #56 -- reserved --
NULL, // #57 -- reserved --
NULL, // #58 -- reserved --
NULL, // #59 -- reserved --
NULL, // #60 -- reserved --
// mathlib
VM_min, // #61 float min(float a, float b )
VM_max, // #62 float max(float a, float b )
VM_bound, // #63 float bound(float min, float val, float max)
VM_pow, // #64 float pow(float x, float y)
VM_sin, // #65 float sin(float f)
VM_cos, // #66 float cos(float f)
VM_tan, // #67 float tan(float f)
VM_asin, // #68 float asin(float f)
VM_acos, // #69 float acos(float f)
VM_atan, // #70 float atan(float f)
VM_sqrt, // #71 float sqrt(float f)
VM_rint, // #72 float rint (float v)
VM_floor, // #73 float floor(float v)
VM_ceil, // #74 float ceil (float v)
VM_fabs, // #75 float fabs (float f)
VM_mod, // #76 float fmod( float val, float m )
NULL, // #77 -- reserved --
NULL, // #78 -- reserved --
VM_VectorNormalize, // #79 vector VectorNormalize( vector v )
VM_VectorLength, // #80 float VectorLength( vector v )
e10, e10 // #81 - #100 are reserved for future expansions
};
/*
=======================================================================
INFOSTRING STUFF
=======================================================================
*/
static char sv_info[MAX_INFO_STRING];
/*
===============
Info_Print
printing current key-value pair
===============
*/
void Info_Print (char *s)
{
char key[512];
char value[512];
char *o;
int l;
if (*s == '\\') s++;
while (*s)
{
o = key;
while (*s && *s != '\\') *o++ = *s++;
l = o - key;
if (l < 20)
{
memset (o, ' ', 20-l);
key[20] = 0;
}
else *o = 0;
Msg ("%s", key);
if (!*s)
{
Msg ("MISSING VALUE\n");
return;
}
o = value;
s++;
while (*s && *s != '\\') *o++ = *s++;
*o = 0;
if (*s) s++;
Msg ("%s\n", value);
}
}
/*
===============
Info_ValueForKey
Searches the string for the given
key and returns the associated value, or an empty string.
===============
*/
char *Info_ValueForKey (char *s, char *key)
{
char pkey[512];
static char value[2][512]; // use two buffers so compares work without stomping on each other
static int valueindex;
char *o;
valueindex ^= 1;
if (*s == '\\') s++;
while (1)
{
o = pkey;
while (*s != '\\')
{
if (!*s) return "";
*o++ = *s++;
}
*o = 0;
s++;
o = value[valueindex];
while (*s != '\\' && *s)
{
if (!*s) return "";
*o++ = *s++;
}
*o = 0;
if (!strcmp (key, pkey) ) return value[valueindex];
if (!*s) return "";
s++;
}
}
void Info_RemoveKey (char *s, char *key)
{
char *start;
char pkey[512];
char value[512];
char *o;
if (strstr (key, "\\")) return;
while (1)
{
start = s;
if (*s == '\\') s++;
o = pkey;
while (*s != '\\')
{
if (!*s) return;
*o++ = *s++;
}
*o = 0;
s++;
o = value;
while (*s != '\\' && *s)
{
if (!*s) return;
*o++ = *s++;
}
*o = 0;
if (!strcmp (key, pkey) )
{
strcpy (start, s); // remove this part
return;
}
if (!*s) return;
}
}
/*
==================
Info_Validate
Some characters are illegal in info strings because they
can mess up the server's parsing
==================
*/
bool Info_Validate (char *s)
{
if (strstr (s, "\"")) return false;
if (strstr (s, ";")) return false;
return true;
}
void Info_SetValueForKey (char *s, char *key, char *value)
{
char newi[MAX_INFO_STRING], *v;
int c, maxsize = MAX_INFO_STRING;
if (strstr (key, "\\") || strstr (value, "\\") )
{
Msg ("Can't use keys or values with a \\\n");
return;
}
if (strstr (key, ";") )
{
Msg ("Can't use keys or values with a semicolon\n");
return;
}
if (strstr (key, "\"") || strstr (value, "\"") )
{
Msg ("Can't use keys or values with a \"\n");
return;
}
if (strlen(key) > MAX_INFO_KEY - 1 || strlen(value) > MAX_INFO_KEY-1)
{
Msg ("Keys and values must be < 64 characters.\n");
return;
}
Info_RemoveKey (s, key);
if (!value || !com.strlen(value)) return;
com.sprintf (newi, "\\%s\\%s", key, value);
if (com.strlen(newi) + com.strlen(s) > maxsize)
{
Msg ("Info string length exceeded\n");
return;
}
// only copy ascii values
s += com.strlen(s);
v = newi;
while (*v)
{
c = *v++;
c &= 127; // strip high bits
if (c >= 32 && c < 127) *s++ = c;
}
*s = 0;
}
static void Cvar_LookupBitInfo(const char *name, const char *string, const char *info, void *unused)
{
Info_SetValueForKey((char *)info, (char *)name, (char *)string);
}
char *Cvar_Userinfo (void)
{
sv_info[0] = 0; // clear previous calls
Cvar_LookupVars( CVAR_USERINFO, sv_info, NULL, Cvar_LookupBitInfo );
return sv_info;
}
char *Cvar_Serverinfo (void)
{
sv_info[0] = 0; // clear previous calls
Cvar_LookupVars( CVAR_SERVERINFO, sv_info, NULL, Cvar_LookupBitInfo );
return sv_info;
}
/*
=======================================================================
FILENAME AUTOCOMPLETION
=======================================================================
*/
/*
=====================================
Cmd_GetMapList
Prints or complete map filename
=====================================
*/
bool Cmd_GetMapList( const char *s, char *completedname, int length )
{
search_t *t;
file_t *f;
string message;
string matchbuf;
byte buf[MAX_SYSPATH]; // 1 kb
int i, nummaps;
t = FS_Search(va("maps/%s*.bsp", s), true );
if( !t ) return false;
FS_FileBase(t->filenames[0], matchbuf );
strncpy( completedname, matchbuf, length );
if(t->numfilenames == 1) return true;
for(i = 0, nummaps = 0; i < t->numfilenames; i++)
{
const char *data = NULL;
char *entities = NULL;
char entfilename[MAX_QPATH];
int ver = -1, lumpofs = 0, lumplen = 0;
const char *ext = FS_FileExtension( t->filenames[i] );
if( com.stricmp(ext, "bsp" )) continue;
strncpy(message, "^1error^7", sizeof(message));
f = FS_Open(t->filenames[i], "rb" );
if( f )
{
memset(buf, 0, 1024);
FS_Read(f, buf, 1024);
if(!memcmp(buf, "IBSP", 4))
{
dheader_t *header = (dheader_t *)buf;
ver = LittleLong(((int *)buf)[1]);
switch(ver)
{
case 38: // quake2
case 39: // xash3d
case 46: // quake3
case 47: // return to castle wolfenstein
lumpofs = LittleLong(header->lumps[LUMP_ENTITIES].fileofs);
lumplen = LittleLong(header->lumps[LUMP_ENTITIES].filelen);
break;
}
}
else
{
lump_t ents; // quake1 entity lump
memcpy(&ents, buf + 4, sizeof(lump_t)); // skip first four bytes (version)
ver = LittleLong(((int *)buf)[0]);
switch( ver )
{
case 28: // quake 1 beta
case 29: // quake 1 regular
case 30: // Half-Life regular
lumpofs = LittleLong(ents.fileofs);
lumplen = LittleLong(ents.filelen);
break;
default:
ver = 0;
break;
}
}
com.strncpy(entfilename, t->filenames[i], sizeof(entfilename));
FS_StripExtension( entfilename );
FS_DefaultExtension( entfilename, ".ent" );
entities = (char *)FS_LoadFile(entfilename, NULL);
if( !entities && lumplen >= 10 )
{
FS_Seek(f, lumpofs, SEEK_SET);
entities = (char *)Z_Malloc(lumplen + 1);
FS_Read(f, entities, lumplen);
}
if( entities )
{
// if there are entities to parse, a missing message key just
// means there is no title, so clear the message string now
message[0] = 0;
data = entities;
while(Com_ParseToken(&data))
{
if(!strcmp(com_token, "{" )) continue;
else if(!strcmp(com_token, "}" )) break;
else if(!strcmp(com_token, "message" ))
{
// get the message contents
Com_ParseToken(&data);
strncpy(message, com_token, sizeof(message));
}
}
}
}
if( entities )Mem_Free(entities);
if( f )FS_Close(f);
FS_FileBase(t->filenames[i], matchbuf );
switch(ver)
{
case 28: strncpy((char *)buf, "Quake1 beta", sizeof(buf)); break;
case 29: strncpy((char *)buf, "Quake1", sizeof(buf)); break;
case 30: strncpy((char *)buf, "Half-Life", sizeof(buf)); break;
case 38: strncpy((char *)buf, "Quake 2", sizeof(buf)); break;
case 39: strncpy((char *)buf, "Xash 3D", sizeof(buf)); break;
case 46: strncpy((char *)buf, "Quake 3", sizeof(buf)); break;
case 47: strncpy((char *)buf, "RTCW", sizeof(buf)); break;
default: strncpy((char *)buf, "??", sizeof(buf)); break;
}
Msg("%16s (%s) ^3%s^7\n", matchbuf, buf, message);
nummaps++;
}
Msg("\n^3 %i maps found.\n", nummaps );
Mem_Free( t );
// cut shortestMatch to the amount common with s
for( i = 0; matchbuf[i]; i++ )
{
if(tolower(completedname[i]) != tolower(matchbuf[i]))
completedname[i] = 0;
}
return true;
}
/*
=====================================
Cmd_GetFontList
Prints or complete font filename
=====================================
*/
bool Cmd_GetFontList( const char *s, char *completedname, int length )
{
search_t *t;
string matchbuf;
int i, numfonts;
t = FS_Search(va("graphics/fonts/%s*.dds", s ), true);
if(!t) return false;
FS_FileBase(t->filenames[0], matchbuf );
if(completedname && length) com.strncpy( completedname, matchbuf, length );
if(t->numfilenames == 1) return true;
for(i = 0, numfonts = 0; i < t->numfilenames; i++)
{
const char *ext = FS_FileExtension( t->filenames[i] );
if( com.stricmp(ext, "dds" )) continue;
FS_FileBase(t->filenames[i], matchbuf );
Msg("%16s\n", matchbuf );
numfonts++;
}
Msg("\n^3 %i fonts found.\n", numfonts );
Mem_Free(t);
// cut shortestMatch to the amount common with s
if(completedname && length)
{
for( i = 0; matchbuf[i]; i++ )
{
if(com.tolower(completedname[i]) != tolower(matchbuf[i]))
completedname[i] = 0;
}
}
return true;
}
/*
=====================================
Cmd_GetDemoList
Prints or complete demo filename
=====================================
*/
bool Cmd_GetDemoList( const char *s, char *completedname, int length )
{
search_t *t;
string matchbuf;
int i, numdems;
t = FS_Search(va("demos/%s*.dem", s ), true);
if(!t) return false;
FS_FileBase(t->filenames[0], matchbuf );
if(completedname && length) strncpy( completedname, matchbuf, length );
if(t->numfilenames == 1) return true;
for(i = 0, numdems = 0; i < t->numfilenames; i++)
{
const char *ext = FS_FileExtension( t->filenames[i] );
if( com.stricmp(ext, "dem" )) continue;
FS_FileBase(t->filenames[i], matchbuf );
Msg("%16s\n", matchbuf );
numdems++;
}
Msg("\n^3 %i fonts found.\n", numdems );
Mem_Free(t);
// cut shortestMatch to the amount common with s
if(completedname && length)
{
for( i = 0; matchbuf[i]; i++ )
{
if(com.tolower(completedname[i]) != tolower(matchbuf[i]))
completedname[i] = 0;
}
}
return true;
}
/*
=====================================
Cmd_GetMovieList
Prints or complete movie filename
=====================================
*/
bool Cmd_GetMovieList( const char *s, char *completedname, int length )
{
search_t *t;
string matchbuf;
int i, nummovies;
t = FS_Search(va("video/%s*.roq", s ), true);
if(!t) return false;
FS_FileBase(t->filenames[0], matchbuf );
if(completedname && length) strncpy( completedname, matchbuf, length );
if(t->numfilenames == 1) return true;
for(i = 0, nummovies = 0; i < t->numfilenames; i++)
{
const char *ext = FS_FileExtension( t->filenames[i] );
if( com.stricmp(ext, "roq" )) continue;
FS_FileBase(t->filenames[i], matchbuf );
Msg("%16s\n", matchbuf );
nummovies++;
}
Msg("\n^3 %i movies found.\n", nummovies );
Mem_Free(t);
// cut shortestMatch to the amount common with s
if(completedname && length)
{
for( i = 0; matchbuf[i]; i++ )
{
if(com.tolower(completedname[i]) != tolower(matchbuf[i]))
completedname[i] = 0;
}
}
return true;
}
/*
=====================================
Cmd_GetMusicList
Prints or complete background track filename
=====================================
*/
bool Cmd_GetMusicList( const char *s, char *completedname, int length )
{
search_t *t;
string matchbuf;
int i, numtracks;
t = FS_Search(va("music/%s*.ogg", s ), true);
if(!t) return false;
FS_FileBase(t->filenames[0], matchbuf );
if(completedname && length) strncpy( completedname, matchbuf, length );
if(t->numfilenames == 1) return true;
for(i = 0, numtracks = 0; i < t->numfilenames; i++)
{
const char *ext = FS_FileExtension( t->filenames[i] );
if( com.stricmp(ext, "ogg" )) continue;
FS_FileBase(t->filenames[i], matchbuf );
Msg("%16s\n", matchbuf );
numtracks++;
}
Msg("\n^3 %i soundtracks found.\n", numtracks );
Mem_Free(t);
// cut shortestMatch to the amount common with s
if(completedname && length)
{
for( i = 0; matchbuf[i]; i++ )
{
if(com.tolower(completedname[i]) != tolower(matchbuf[i]))
completedname[i] = 0;
}
}
return true;
}
/*
=====================================
Cmd_GetSoundList
Prints or complete sound filename
=====================================
*/
bool Cmd_GetSoundList( const char *s, char *completedname, int length )
{
search_t *t;
string matchbuf;
int i, numsounds;
const char *snddir = "sound/"; // constant
t = FS_Search(va("%s%s*.*", snddir, s ), true);
if(!t) return false;
com.strncpy( matchbuf, t->filenames[0] + com.strlen(snddir), MAX_STRING );
FS_StripExtension( matchbuf );
if( completedname && length ) com.strncpy( completedname, matchbuf, length );
if(t->numfilenames == 1) return true;
for(i = 0, numsounds = 0; i < t->numfilenames; i++)
{
const char *ext = FS_FileExtension( t->filenames[i] );
if(com.stricmp(ext, "wav") && com.stricmp(ext, "ogg")) continue;
com.strncpy( matchbuf, t->filenames[i] + com.strlen(snddir), MAX_STRING );
FS_StripExtension( matchbuf );
Msg("%16s\n", matchbuf );
numsounds++;
}
Msg("\n^3 %i sounds found.\n", numsounds );
Mem_Free(t);
// cut shortestMatch to the amount common with s
if( completedname && length )
{
for( i = 0; matchbuf[i]; i++ )
{
if(com.tolower(completedname[i]) != com.tolower(matchbuf[i]))
completedname[i] = 0;
}
}
return true;
}
/*
============
Cmd_WriteVariables
Appends lines containing "set variable value" for all variables
with the archive flag set to true.
============
*/
static void Cmd_WriteCvar(const char *name, const char *string, const char *unused, void *f)
{
FS_Printf(f, "seta %s \"%s\"\n", name, string );
}
void Cmd_WriteVariables( file_t *f )
{
FS_Printf (f, "unsetall\n" );
Cvar_LookupVars( CVAR_ARCHIVE, NULL, f, Cmd_WriteCvar );
}
float frand(void)
{
return (rand()&32767)* (1.0/32767);
}
float crand(void)
{
return (rand()&32767)* (2.0/32767) - 1;
}