q3rally/engine/code/qcommon/cvar.c
zturtleman 96a9e2a9aa ioquake3 resync to revision 3511 from 3444.
This updates from SDL 2.0.4 to SDL 2.0.8.

Fix nullptr dereference in front of nullptr check in FS_CheckPak0
Fix undefined behaviour due to shifting signed in snd_mem.c
Fix shifting bits out of byte in tr_font.c
Fix shift into sign in cl_cin.c
Fix signed bit operations in MSG_ReadBits
Add missing address operator in cm_polylib.c
OpenGL1: Decay float[8] to float * in tr_marks.c
Avoid srcList[-1] in snd_openal.c
Fix the behaviour of CVAR_LATCH|CVAR_CHEAT cvars
Maximize cURL buffer size
Fix mouse grab after toggling fullscreen
Fix q3history buffer not cleared between mods and OOB-access
Revert "Removed "Color Depth" from q3_ui system settings, it didn't control anything."
Fix displayed color/depth/stencil bits values
Restore setting r_colorbits in q3_ui
Make setting r_stencilbits more consistent in Team Arena UI
Fix map list in Team Arena start server menu after entering SP menu
Support SDL audio devices that require float32 samples.
sdl_snd.c should just initialize SDL audio without checking SDL_WasInit().
There's no need to SDL_PauseAudio(1) before calling SDL_CloseAudio().
Added audio capture support to SDL backend.
Use the SDL2 audio device interface instead of the legacy 1.2 API.
Disable SDL audio capture until prebuilt SDL libraries are updated to 2.0.8.
Update SDL2 to 2.0.8
Add SDL 2.0.1 headers for macOS PPC
Make macOS Universal Bundle target 10.6 for x86 and x86_64
Fix possible bot goal state NULL pointer dereference
Fix uninitialized bot_goal_t fields
Remove unnecessary NULL pointer check in Cmd_RemoveCommand
Make UI_DrawProportionalString handle NULL string
Fix compiling against macOS system OpenAL and SDL2 frameworks
Fix array index in CanDamage() function - discovered by MARTY
Fix compiling Makefile (broke in macOS frameworks commit)
Fix clearing keys for control in Team Arena UI
Make s_useOpenAL be CVAR_LATCH
Improvements for dedicated camera followers (team follow1/2)
Fix not closing description.txt and fix path seperator
Fix duplicate bots displayed in Team Arena ingame add bot menu
OpenGL2: Fix parsing specularScale in shaders
Don't allow SDL audio capture using pulseaudio
Isolate the Altivec code so non-Altivec PPC targets can use the same binary.
Limit -maltivec to specific source files on OpenBSD too (untested)
Use SDL 2.0.1 headers for macOS ppc64
Fix console offset while Team Arena voiceMenu is open
OpenGL2: Readd r_deluxeSpecular.
Fix client kicked as unpure when missing the latest cgame/ui pk3s
Don't create multiple windows when GL context creation fails
Require OpenGL 1.2 for GL_CLAMP_TO_EDGE
Fix Linux uninstaller requiring Bash
Fix Linux uninstaller redirecting stderr to stdout in preuninstall.sh
Reported by @illwieckz.
Fix in_restart causing fatal error while video is shutdown
Allow pkg-config binary to be overridden with PKG_CONFIG
Make testgun command without argument disable test gun model
Remove unused renderer_buffer variable
Don't upload 8 bit grayscale images as 16 bit luminance
OpenGL1: Use RE_UploadCinematic() instead of duplicate code
Don't load non-core GL functions for OpenGL 3.2 core context
Load OpenGL ES 2.0 function procs
Don't check fixed function GL extensions when using shader pipeline
OpenGL2: Fix world VAO cache drawing when glIndex_t is unsigned short
OpenGL2: Misc fixes and cleanup
Fix IQM root joint backlerp when joint number is more than 0
Improve IQM loading
Improve IQM CPU vertex skinning performance
OpenGL2: Add GPU vertex skinning for IQM models
2018-07-30 11:35:12 +00:00

1464 lines
29 KiB
C

/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code 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.
Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// cvar.c -- dynamic variable tracking
#include "q_shared.h"
#include "qcommon.h"
cvar_t *cvar_vars = NULL;
cvar_t *cvar_cheats;
int cvar_modifiedFlags;
#define MAX_CVARS 2048
cvar_t cvar_indexes[MAX_CVARS];
int cvar_numIndexes;
#define FILE_HASH_SIZE 256
static cvar_t *hashTable[FILE_HASH_SIZE];
/*
================
return a hash value for the filename
================
*/
static long generateHashValue( const char *fname ) {
int i;
long hash;
char letter;
hash = 0;
i = 0;
while (fname[i] != '\0') {
letter = tolower(fname[i]);
hash+=(long)(letter)*(i+119);
i++;
}
hash &= (FILE_HASH_SIZE-1);
return hash;
}
/*
============
Cvar_ValidateString
============
*/
static qboolean Cvar_ValidateString( const char *s ) {
if ( !s ) {
return qfalse;
}
if ( strchr( s, '\\' ) ) {
return qfalse;
}
if ( strchr( s, '\"' ) ) {
return qfalse;
}
if ( strchr( s, ';' ) ) {
return qfalse;
}
return qtrue;
}
/*
============
Cvar_FindVar
============
*/
static cvar_t *Cvar_FindVar( const char *var_name ) {
cvar_t *var;
long hash;
hash = generateHashValue(var_name);
for (var=hashTable[hash] ; var ; var=var->hashNext) {
if (!Q_stricmp(var_name, var->name)) {
return var;
}
}
return NULL;
}
/*
============
Cvar_VariableValue
============
*/
float Cvar_VariableValue( const char *var_name ) {
cvar_t *var;
var = Cvar_FindVar (var_name);
if (!var)
return 0;
return var->value;
}
/*
============
Cvar_VariableIntegerValue
============
*/
int Cvar_VariableIntegerValue( const char *var_name ) {
cvar_t *var;
var = Cvar_FindVar (var_name);
if (!var)
return 0;
return var->integer;
}
/*
============
Cvar_VariableString
============
*/
char *Cvar_VariableString( const char *var_name ) {
cvar_t *var;
var = Cvar_FindVar (var_name);
if (!var)
return "";
return var->string;
}
/*
============
Cvar_VariableStringBuffer
============
*/
void Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ) {
cvar_t *var;
var = Cvar_FindVar (var_name);
if (!var) {
*buffer = 0;
}
else {
Q_strncpyz( buffer, var->string, bufsize );
}
}
/*
============
Cvar_Flags
============
*/
int Cvar_Flags(const char *var_name)
{
cvar_t *var;
if(!(var = Cvar_FindVar(var_name)))
return CVAR_NONEXISTENT;
else
{
if(var->modified)
return var->flags | CVAR_MODIFIED;
else
return var->flags;
}
}
/*
============
Cvar_CommandCompletion
============
*/
void Cvar_CommandCompletion(void (*callback)(const char *s))
{
cvar_t *cvar;
for(cvar = cvar_vars; cvar; cvar = cvar->next)
{
if(cvar->name)
callback(cvar->name);
}
}
/*
============
Cvar_Validate
============
*/
static const char *Cvar_Validate( cvar_t *var,
const char *value, qboolean warn )
{
static char s[ MAX_CVAR_VALUE_STRING ];
float valuef;
qboolean changed = qfalse;
if( !var->validate )
return value;
if( !value )
return value;
if( Q_isanumber( value ) )
{
valuef = atof( value );
if( var->integral )
{
if( !Q_isintegral( valuef ) )
{
if( warn )
Com_Printf( "WARNING: cvar '%s' must be integral", var->name );
valuef = (int)valuef;
changed = qtrue;
}
}
}
else
{
if( warn )
Com_Printf( "WARNING: cvar '%s' must be numeric", var->name );
valuef = atof( var->resetString );
changed = qtrue;
}
if( valuef < var->min )
{
if( warn )
{
if( changed )
Com_Printf( " and is" );
else
Com_Printf( "WARNING: cvar '%s'", var->name );
if( Q_isintegral( var->min ) )
Com_Printf( " out of range (min %d)", (int)var->min );
else
Com_Printf( " out of range (min %f)", var->min );
}
valuef = var->min;
changed = qtrue;
}
else if( valuef > var->max )
{
if( warn )
{
if( changed )
Com_Printf( " and is" );
else
Com_Printf( "WARNING: cvar '%s'", var->name );
if( Q_isintegral( var->max ) )
Com_Printf( " out of range (max %d)", (int)var->max );
else
Com_Printf( " out of range (max %f)", var->max );
}
valuef = var->max;
changed = qtrue;
}
if( changed )
{
if( Q_isintegral( valuef ) )
{
Com_sprintf( s, sizeof( s ), "%d", (int)valuef );
if( warn )
Com_Printf( ", setting to %d\n", (int)valuef );
}
else
{
Com_sprintf( s, sizeof( s ), "%f", valuef );
if( warn )
Com_Printf( ", setting to %f\n", valuef );
}
return s;
}
else
return value;
}
/*
============
Cvar_Get
If the variable already exists, the value will not be set unless CVAR_ROM
The flags will be or'ed in if the variable exists.
============
*/
cvar_t *Cvar_Get( const char *var_name, const char *var_value, int flags ) {
cvar_t *var;
long hash;
int index;
if ( !var_name || ! var_value ) {
Com_Error( ERR_FATAL, "Cvar_Get: NULL parameter" );
}
if ( !Cvar_ValidateString( var_name ) ) {
Com_Printf("invalid cvar name string: %s\n", var_name );
var_name = "BADNAME";
}
#if 0 // FIXME: values with backslash happen
if ( !Cvar_ValidateString( var_value ) ) {
Com_Printf("invalid cvar value string: %s\n", var_value );
var_value = "BADVALUE";
}
#endif
var = Cvar_FindVar (var_name);
if(var)
{
var_value = Cvar_Validate(var, var_value, qfalse);
// Make sure the game code cannot mark engine-added variables as gamecode vars
if(var->flags & CVAR_VM_CREATED)
{
if(!(flags & CVAR_VM_CREATED))
var->flags &= ~CVAR_VM_CREATED;
}
else if (!(var->flags & CVAR_USER_CREATED))
{
if(flags & CVAR_VM_CREATED)
flags &= ~CVAR_VM_CREATED;
}
// if the C code is now specifying a variable that the user already
// set a value for, take the new value as the reset value
if(var->flags & CVAR_USER_CREATED)
{
var->flags &= ~CVAR_USER_CREATED;
Z_Free( var->resetString );
var->resetString = CopyString( var_value );
if(flags & CVAR_ROM)
{
// this variable was set by the user,
// so force it to value given by the engine.
if(var->latchedString)
Z_Free(var->latchedString);
var->latchedString = CopyString(var_value);
}
}
// Make sure servers cannot mark engine-added variables as SERVER_CREATED
if(var->flags & CVAR_SERVER_CREATED)
{
if(!(flags & CVAR_SERVER_CREATED))
var->flags &= ~CVAR_SERVER_CREATED;
}
else
{
if(flags & CVAR_SERVER_CREATED)
flags &= ~CVAR_SERVER_CREATED;
}
var->flags |= flags;
// only allow one non-empty reset string without a warning
if ( !var->resetString[0] ) {
// we don't have a reset string yet
Z_Free( var->resetString );
var->resetString = CopyString( var_value );
} else if ( var_value[0] && strcmp( var->resetString, var_value ) ) {
Com_DPrintf( "Warning: cvar \"%s\" given initial values: \"%s\" and \"%s\"\n",
var_name, var->resetString, var_value );
}
// if we have a latched string, take that value now
if ( var->latchedString ) {
char *s;
s = var->latchedString;
var->latchedString = NULL; // otherwise cvar_set2 would free it
Cvar_Set2( var_name, s, qtrue );
Z_Free( s );
}
// ZOID--needs to be set so that cvars the game sets as
// SERVERINFO get sent to clients
cvar_modifiedFlags |= flags;
return var;
}
//
// allocate a new cvar
//
// find a free cvar
for(index = 0; index < MAX_CVARS; index++)
{
if(!cvar_indexes[index].name)
break;
}
if(index >= MAX_CVARS)
{
if(!com_errorEntered)
Com_Error(ERR_FATAL, "Error: Too many cvars, cannot create a new one!");
return NULL;
}
var = &cvar_indexes[index];
if(index >= cvar_numIndexes)
cvar_numIndexes = index + 1;
var->name = CopyString (var_name);
var->string = CopyString (var_value);
var->modified = qtrue;
var->modificationCount = 1;
var->value = atof (var->string);
var->integer = atoi(var->string);
var->resetString = CopyString( var_value );
var->validate = qfalse;
var->description = NULL;
// link the variable in
var->next = cvar_vars;
if(cvar_vars)
cvar_vars->prev = var;
var->prev = NULL;
cvar_vars = var;
var->flags = flags;
// note what types of cvars have been modified (userinfo, archive, serverinfo, systeminfo)
cvar_modifiedFlags |= var->flags;
hash = generateHashValue(var_name);
var->hashIndex = hash;
var->hashNext = hashTable[hash];
if(hashTable[hash])
hashTable[hash]->hashPrev = var;
var->hashPrev = NULL;
hashTable[hash] = var;
return var;
}
/*
============
Cvar_Print
Prints the value, default, and latched string of the given variable
============
*/
void Cvar_Print( cvar_t *v ) {
Com_Printf ("\"%s\" is:\"%s" S_COLOR_WHITE "\"",
v->name, v->string );
if ( !( v->flags & CVAR_ROM ) ) {
if ( !Q_stricmp( v->string, v->resetString ) ) {
Com_Printf (", the default" );
} else {
Com_Printf (" default:\"%s" S_COLOR_WHITE "\"",
v->resetString );
}
}
Com_Printf ("\n");
if ( v->latchedString ) {
Com_Printf( "latched: \"%s\"\n", v->latchedString );
}
if ( v->description ) {
Com_Printf( "%s\n", v->description );
}
}
/*
============
Cvar_Set2
============
*/
cvar_t *Cvar_Set2( const char *var_name, const char *value, qboolean force ) {
cvar_t *var;
// Com_DPrintf( "Cvar_Set2: %s %s\n", var_name, value );
if ( !Cvar_ValidateString( var_name ) ) {
Com_Printf("invalid cvar name string: %s\n", var_name );
var_name = "BADNAME";
}
#if 0 // FIXME
if ( value && !Cvar_ValidateString( value ) ) {
Com_Printf("invalid cvar value string: %s\n", value );
var_value = "BADVALUE";
}
#endif
var = Cvar_FindVar (var_name);
if (!var) {
if ( !value ) {
return NULL;
}
// create it
if ( !force ) {
return Cvar_Get( var_name, value, CVAR_USER_CREATED );
} else {
return Cvar_Get (var_name, value, 0);
}
}
if (!value ) {
value = var->resetString;
}
value = Cvar_Validate(var, value, qtrue);
if((var->flags & CVAR_LATCH) && var->latchedString)
{
if(!strcmp(value, var->string))
{
Z_Free(var->latchedString);
var->latchedString = NULL;
return var;
}
if(!strcmp(value, var->latchedString))
return var;
}
else if(!strcmp(value, var->string))
return var;
// note what types of cvars have been modified (userinfo, archive, serverinfo, systeminfo)
cvar_modifiedFlags |= var->flags;
if (!force)
{
if (var->flags & CVAR_ROM)
{
Com_Printf ("%s is read only.\n", var_name);
return var;
}
if (var->flags & CVAR_INIT)
{
Com_Printf ("%s is write protected.\n", var_name);
return var;
}
if ((var->flags & CVAR_CHEAT) && !cvar_cheats->integer)
{
Com_Printf ("%s is cheat protected.\n", var_name);
return var;
}
if (var->flags & CVAR_LATCH)
{
if (var->latchedString)
{
if (strcmp(value, var->latchedString) == 0)
return var;
Z_Free (var->latchedString);
}
else
{
if (strcmp(value, var->string) == 0)
return var;
}
Com_Printf ("%s will be changed upon restarting.\n", var_name);
var->latchedString = CopyString(value);
var->modified = qtrue;
var->modificationCount++;
return var;
}
}
else
{
if (var->latchedString)
{
Z_Free (var->latchedString);
var->latchedString = NULL;
}
}
if (!strcmp(value, var->string))
return var; // not changed
var->modified = qtrue;
var->modificationCount++;
Z_Free (var->string); // free the old value string
var->string = CopyString(value);
var->value = atof (var->string);
var->integer = atoi (var->string);
return var;
}
/*
============
Cvar_Set
============
*/
void Cvar_Set( const char *var_name, const char *value) {
Cvar_Set2 (var_name, value, qtrue);
}
/*
============
Cvar_SetSafe
============
*/
void Cvar_SetSafe( const char *var_name, const char *value )
{
int flags = Cvar_Flags( var_name );
if((flags != CVAR_NONEXISTENT) && (flags & CVAR_PROTECTED))
{
if( value )
Com_Error( ERR_DROP, "Restricted source tried to set "
"\"%s\" to \"%s\"", var_name, value );
else
Com_Error( ERR_DROP, "Restricted source tried to "
"modify \"%s\"", var_name );
return;
}
Cvar_Set( var_name, value );
}
/*
============
Cvar_SetLatched
============
*/
void Cvar_SetLatched( const char *var_name, const char *value) {
Cvar_Set2 (var_name, value, qfalse);
}
/*
============
Cvar_SetValue
============
*/
void Cvar_SetValue( const char *var_name, float value) {
char val[32];
if ( value == (int)value ) {
Com_sprintf (val, sizeof(val), "%i",(int)value);
} else {
Com_sprintf (val, sizeof(val), "%f",value);
}
Cvar_Set (var_name, val);
}
/*
============
Cvar_SetValueSafe
============
*/
void Cvar_SetValueSafe( const char *var_name, float value )
{
char val[32];
if( Q_isintegral( value ) )
Com_sprintf( val, sizeof(val), "%i", (int)value );
else
Com_sprintf( val, sizeof(val), "%f", value );
Cvar_SetSafe( var_name, val );
}
/*
============
Cvar_Reset
============
*/
void Cvar_Reset( const char *var_name ) {
Cvar_Set2( var_name, NULL, qfalse );
}
/*
============
Cvar_ForceReset
============
*/
void Cvar_ForceReset(const char *var_name)
{
Cvar_Set2(var_name, NULL, qtrue);
}
/*
============
Cvar_SetCheatState
Any testing variables will be reset to the safe values
============
*/
void Cvar_SetCheatState(void)
{
cvar_t *var;
// set all default vars to the safe value
for(var = cvar_vars; var ; var = var->next)
{
if(var->flags & CVAR_CHEAT)
{
// the CVAR_LATCHED|CVAR_CHEAT vars might escape the reset here
// because of a different var->latchedString
if (var->latchedString)
{
Z_Free(var->latchedString);
var->latchedString = NULL;
}
if (strcmp(var->resetString,var->string))
Cvar_Set(var->name, var->resetString);
}
}
}
/*
============
Cvar_Command
Handles variable inspection and changing from the console
============
*/
qboolean Cvar_Command( void ) {
cvar_t *v;
// check variables
v = Cvar_FindVar (Cmd_Argv(0));
if (!v) {
return qfalse;
}
// perform a variable print or set
if ( Cmd_Argc() == 1 ) {
Cvar_Print( v );
return qtrue;
}
// set the value if forcing isn't required
Cvar_Set2 (v->name, Cmd_Args(), qfalse);
return qtrue;
}
/*
============
Cvar_Print_f
Prints the contents of a cvar
(preferred over Cvar_Command where cvar names and commands conflict)
============
*/
void Cvar_Print_f(void)
{
char *name;
cvar_t *cv;
if(Cmd_Argc() != 2)
{
Com_Printf ("usage: print <variable>\n");
return;
}
name = Cmd_Argv(1);
cv = Cvar_FindVar(name);
if(cv)
Cvar_Print(cv);
else
Com_Printf ("Cvar %s does not exist.\n", name);
}
/*
============
Cvar_Toggle_f
Toggles a cvar for easy single key binding, optionally through a list of
given values
============
*/
void Cvar_Toggle_f( void ) {
int i, c = Cmd_Argc();
char *curval;
if(c < 2) {
Com_Printf("usage: toggle <variable> [value1, value2, ...]\n");
return;
}
if(c == 2) {
Cvar_Set2(Cmd_Argv(1), va("%d",
!Cvar_VariableValue(Cmd_Argv(1))),
qfalse);
return;
}
if(c == 3) {
Com_Printf("toggle: nothing to toggle to\n");
return;
}
curval = Cvar_VariableString(Cmd_Argv(1));
// don't bother checking the last arg for a match since the desired
// behaviour is the same as no match (set to the first argument)
for(i = 2; i + 1 < c; i++) {
if(strcmp(curval, Cmd_Argv(i)) == 0) {
Cvar_Set2(Cmd_Argv(1), Cmd_Argv(i + 1), qfalse);
return;
}
}
// fallback
Cvar_Set2(Cmd_Argv(1), Cmd_Argv(2), qfalse);
}
/*
============
Cvar_Set_f
Allows setting and defining of arbitrary cvars from console, even if they
weren't declared in C code.
============
*/
void Cvar_Set_f( void ) {
int c;
char *cmd;
cvar_t *v;
c = Cmd_Argc();
cmd = Cmd_Argv(0);
if ( c < 2 ) {
Com_Printf ("usage: %s <variable> <value>\n", cmd);
return;
}
if ( c == 2 ) {
Cvar_Print_f();
return;
}
v = Cvar_Set2 (Cmd_Argv(1), Cmd_ArgsFrom(2), qfalse);
if( !v ) {
return;
}
switch( cmd[3] ) {
case 'a':
if( !( v->flags & CVAR_ARCHIVE ) ) {
v->flags |= CVAR_ARCHIVE;
cvar_modifiedFlags |= CVAR_ARCHIVE;
}
break;
case 'u':
if( !( v->flags & CVAR_USERINFO ) ) {
v->flags |= CVAR_USERINFO;
cvar_modifiedFlags |= CVAR_USERINFO;
}
break;
case 's':
if( !( v->flags & CVAR_SERVERINFO ) ) {
v->flags |= CVAR_SERVERINFO;
cvar_modifiedFlags |= CVAR_SERVERINFO;
}
break;
}
}
/*
============
Cvar_Reset_f
============
*/
void Cvar_Reset_f( void ) {
if ( Cmd_Argc() != 2 ) {
Com_Printf ("usage: reset <variable>\n");
return;
}
Cvar_Reset( Cmd_Argv( 1 ) );
}
/*
============
Cvar_WriteVariables
Appends lines containing "set variable value" for all variables
with the archive flag set to qtrue.
============
*/
void Cvar_WriteVariables(fileHandle_t f)
{
cvar_t *var;
char buffer[1024];
for (var = cvar_vars; var; var = var->next)
{
if(!var->name || Q_stricmp( var->name, "cl_cdkey" ) == 0)
continue;
if( var->flags & CVAR_ARCHIVE ) {
// write the latched value, even if it hasn't taken effect yet
if ( var->latchedString ) {
if( strlen( var->name ) + strlen( var->latchedString ) + 10 > sizeof( buffer ) ) {
Com_Printf( S_COLOR_YELLOW "WARNING: value of variable "
"\"%s\" too long to write to file\n", var->name );
continue;
}
Com_sprintf (buffer, sizeof(buffer), "seta %s \"%s\"\n", var->name, var->latchedString);
} else {
if( strlen( var->name ) + strlen( var->string ) + 10 > sizeof( buffer ) ) {
Com_Printf( S_COLOR_YELLOW "WARNING: value of variable "
"\"%s\" too long to write to file\n", var->name );
continue;
}
Com_sprintf (buffer, sizeof(buffer), "seta %s \"%s\"\n", var->name, var->string);
}
FS_Write( buffer, strlen( buffer ), f );
}
}
}
/*
============
Cvar_List_f
============
*/
void Cvar_List_f( void ) {
cvar_t *var;
int i;
char *match;
if ( Cmd_Argc() > 1 ) {
match = Cmd_Argv( 1 );
} else {
match = NULL;
}
i = 0;
for (var = cvar_vars ; var ; var = var->next, i++)
{
if(!var->name || (match && !Com_Filter(match, var->name, qfalse)))
continue;
if (var->flags & CVAR_SERVERINFO) {
Com_Printf("S");
} else {
Com_Printf(" ");
}
if (var->flags & CVAR_SYSTEMINFO) {
Com_Printf("s");
} else {
Com_Printf(" ");
}
if (var->flags & CVAR_USERINFO) {
Com_Printf("U");
} else {
Com_Printf(" ");
}
if (var->flags & CVAR_ROM) {
Com_Printf("R");
} else {
Com_Printf(" ");
}
if (var->flags & CVAR_INIT) {
Com_Printf("I");
} else {
Com_Printf(" ");
}
if (var->flags & CVAR_ARCHIVE) {
Com_Printf("A");
} else {
Com_Printf(" ");
}
if (var->flags & CVAR_LATCH) {
Com_Printf("L");
} else {
Com_Printf(" ");
}
if (var->flags & CVAR_CHEAT) {
Com_Printf("C");
} else {
Com_Printf(" ");
}
if (var->flags & CVAR_USER_CREATED) {
Com_Printf("?");
} else {
Com_Printf(" ");
}
Com_Printf (" %s \"%s\"\n", var->name, var->string);
}
Com_Printf ("\n%i total cvars\n", i);
Com_Printf ("%i cvar indexes\n", cvar_numIndexes);
}
/*
============
Cvar_ListModified_f
============
*/
void Cvar_ListModified_f( void ) {
cvar_t *var;
int totalModified;
char *value;
char *match;
if ( Cmd_Argc() > 1 ) {
match = Cmd_Argv( 1 );
} else {
match = NULL;
}
totalModified = 0;
for (var = cvar_vars ; var ; var = var->next)
{
if ( !var->name || !var->modificationCount )
continue;
value = var->latchedString ? var->latchedString : var->string;
if ( !strcmp( value, var->resetString ) )
continue;
totalModified++;
if (match && !Com_Filter(match, var->name, qfalse))
continue;
if (var->flags & CVAR_SERVERINFO) {
Com_Printf("S");
} else {
Com_Printf(" ");
}
if (var->flags & CVAR_SYSTEMINFO) {
Com_Printf("s");
} else {
Com_Printf(" ");
}
if (var->flags & CVAR_USERINFO) {
Com_Printf("U");
} else {
Com_Printf(" ");
}
if (var->flags & CVAR_ROM) {
Com_Printf("R");
} else {
Com_Printf(" ");
}
if (var->flags & CVAR_INIT) {
Com_Printf("I");
} else {
Com_Printf(" ");
}
if (var->flags & CVAR_ARCHIVE) {
Com_Printf("A");
} else {
Com_Printf(" ");
}
if (var->flags & CVAR_LATCH) {
Com_Printf("L");
} else {
Com_Printf(" ");
}
if (var->flags & CVAR_CHEAT) {
Com_Printf("C");
} else {
Com_Printf(" ");
}
if (var->flags & CVAR_USER_CREATED) {
Com_Printf("?");
} else {
Com_Printf(" ");
}
Com_Printf (" %s \"%s\", default \"%s\"\n", var->name, value, var->resetString);
}
Com_Printf ("\n%i total modified cvars\n", totalModified);
}
/*
============
Cvar_Unset
Unsets a cvar
============
*/
cvar_t *Cvar_Unset(cvar_t *cv)
{
cvar_t *next = cv->next;
// note what types of cvars have been modified (userinfo, archive, serverinfo, systeminfo)
cvar_modifiedFlags |= cv->flags;
if(cv->name)
Z_Free(cv->name);
if(cv->string)
Z_Free(cv->string);
if(cv->latchedString)
Z_Free(cv->latchedString);
if(cv->resetString)
Z_Free(cv->resetString);
if(cv->description)
Z_Free(cv->description);
if(cv->prev)
cv->prev->next = cv->next;
else
cvar_vars = cv->next;
if(cv->next)
cv->next->prev = cv->prev;
if(cv->hashPrev)
cv->hashPrev->hashNext = cv->hashNext;
else
hashTable[cv->hashIndex] = cv->hashNext;
if(cv->hashNext)
cv->hashNext->hashPrev = cv->hashPrev;
Com_Memset(cv, '\0', sizeof(*cv));
return next;
}
/*
============
Cvar_Unset_f
Unsets a userdefined cvar
============
*/
void Cvar_Unset_f(void)
{
cvar_t *cv;
if(Cmd_Argc() != 2)
{
Com_Printf("Usage: %s <varname>\n", Cmd_Argv(0));
return;
}
cv = Cvar_FindVar(Cmd_Argv(1));
if(!cv)
return;
if(cv->flags & CVAR_USER_CREATED)
Cvar_Unset(cv);
else
Com_Printf("Error: %s: Variable %s is not user created.\n", Cmd_Argv(0), cv->name);
}
/*
============
Cvar_Restart
Resets all cvars to their hardcoded values and removes userdefined variables
and variables added via the VMs if requested.
============
*/
void Cvar_Restart(qboolean unsetVM)
{
cvar_t *curvar;
curvar = cvar_vars;
while(curvar)
{
if((curvar->flags & CVAR_USER_CREATED) ||
(unsetVM && (curvar->flags & CVAR_VM_CREATED)))
{
// throw out any variables the user/vm created
curvar = Cvar_Unset(curvar);
continue;
}
if(!(curvar->flags & (CVAR_ROM | CVAR_INIT | CVAR_NORESTART)))
{
// Just reset the rest to their default values.
Cvar_Set2(curvar->name, curvar->resetString, qfalse);
}
curvar = curvar->next;
}
}
/*
============
Cvar_Restart_f
Resets all cvars to their hardcoded values
============
*/
void Cvar_Restart_f(void)
{
Cvar_Restart(qfalse);
}
/*
=====================
Cvar_InfoString
=====================
*/
char *Cvar_InfoString(int bit)
{
static char info[MAX_INFO_STRING];
cvar_t *var;
info[0] = 0;
for(var = cvar_vars; var; var = var->next)
{
if(var->name && (var->flags & bit))
Info_SetValueForKey (info, var->name, var->string);
}
return info;
}
/*
=====================
Cvar_InfoString_Big
handles large info strings ( CS_SYSTEMINFO )
=====================
*/
char *Cvar_InfoString_Big(int bit)
{
static char info[BIG_INFO_STRING];
cvar_t *var;
info[0] = 0;
for (var = cvar_vars; var; var = var->next)
{
if(var->name && (var->flags & bit))
Info_SetValueForKey_Big (info, var->name, var->string);
}
return info;
}
/*
=====================
Cvar_InfoStringBuffer
=====================
*/
void Cvar_InfoStringBuffer( int bit, char* buff, int buffsize ) {
Q_strncpyz(buff,Cvar_InfoString(bit),buffsize);
}
/*
=====================
Cvar_CheckRange
=====================
*/
void Cvar_CheckRange( cvar_t *var, float min, float max, qboolean integral )
{
var->validate = qtrue;
var->min = min;
var->max = max;
var->integral = integral;
// Force an initial range check
Cvar_Set( var->name, var->string );
}
/*
=====================
Cvar_SetDescription
=====================
*/
void Cvar_SetDescription( cvar_t *var, const char *var_description )
{
if( var_description && var_description[0] != '\0' )
{
if( var->description != NULL )
{
Z_Free( var->description );
}
var->description = CopyString( var_description );
}
}
/*
=====================
Cvar_Register
basically a slightly modified Cvar_Get for the interpreted modules
=====================
*/
void Cvar_Register(vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags)
{
cvar_t *cv;
// There is code in Cvar_Get to prevent CVAR_ROM cvars being changed by the
// user. In other words CVAR_ARCHIVE and CVAR_ROM are mutually exclusive
// flags. Unfortunately some historical game code (including single player
// baseq3) sets both flags. We unset CVAR_ROM for such cvars.
if ((flags & (CVAR_ARCHIVE | CVAR_ROM)) == (CVAR_ARCHIVE | CVAR_ROM)) {
Com_DPrintf( S_COLOR_YELLOW "WARNING: Unsetting CVAR_ROM from cvar '%s', "
"since it is also CVAR_ARCHIVE\n", varName );
flags &= ~CVAR_ROM;
}
// Don't allow VM to specific a different creator or other internal flags.
if ( flags & CVAR_USER_CREATED ) {
Com_DPrintf( S_COLOR_YELLOW "WARNING: VM tried to set CVAR_USER_CREATED on cvar '%s'\n", varName );
flags &= ~CVAR_USER_CREATED;
}
if ( flags & CVAR_SERVER_CREATED ) {
Com_DPrintf( S_COLOR_YELLOW "WARNING: VM tried to set CVAR_SERVER_CREATED on cvar '%s'\n", varName );
flags &= ~CVAR_SERVER_CREATED;
}
if ( flags & CVAR_PROTECTED ) {
Com_DPrintf( S_COLOR_YELLOW "WARNING: VM tried to set CVAR_PROTECTED on cvar '%s'\n", varName );
flags &= ~CVAR_PROTECTED;
}
if ( flags & CVAR_MODIFIED ) {
Com_DPrintf( S_COLOR_YELLOW "WARNING: VM tried to set CVAR_MODIFIED on cvar '%s'\n", varName );
flags &= ~CVAR_MODIFIED;
}
if ( flags & CVAR_NONEXISTENT ) {
Com_DPrintf( S_COLOR_YELLOW "WARNING: VM tried to set CVAR_NONEXISTENT on cvar '%s'\n", varName );
flags &= ~CVAR_NONEXISTENT;
}
cv = Cvar_FindVar(varName);
// Don't modify cvar if it's protected.
if ( cv && ( cv->flags & CVAR_PROTECTED ) ) {
Com_DPrintf( S_COLOR_YELLOW "WARNING: VM tried to register protected cvar '%s' with value '%s'%s\n",
varName, defaultValue, ( flags & ~cv->flags ) != 0 ? " and new flags" : "" );
} else {
cv = Cvar_Get(varName, defaultValue, flags | CVAR_VM_CREATED);
}
if (!vmCvar)
return;
vmCvar->handle = cv - cvar_indexes;
vmCvar->modificationCount = -1;
Cvar_Update( vmCvar );
}
/*
=====================
Cvar_Update
updates an interpreted modules' version of a cvar
=====================
*/
void Cvar_Update( vmCvar_t *vmCvar ) {
cvar_t *cv = NULL;
assert(vmCvar);
if ( (unsigned)vmCvar->handle >= cvar_numIndexes ) {
Com_Error( ERR_DROP, "Cvar_Update: handle out of range" );
}
cv = cvar_indexes + vmCvar->handle;
if ( cv->modificationCount == vmCvar->modificationCount ) {
return;
}
if ( !cv->string ) {
return; // variable might have been cleared by a cvar_restart
}
vmCvar->modificationCount = cv->modificationCount;
if ( strlen(cv->string)+1 > MAX_CVAR_VALUE_STRING )
Com_Error( ERR_DROP, "Cvar_Update: src %s length %u exceeds MAX_CVAR_VALUE_STRING",
cv->string,
(unsigned int) strlen(cv->string));
Q_strncpyz( vmCvar->string, cv->string, MAX_CVAR_VALUE_STRING );
vmCvar->value = cv->value;
vmCvar->integer = cv->integer;
}
/*
==================
Cvar_CompleteCvarName
==================
*/
void Cvar_CompleteCvarName( char *args, int argNum )
{
if( argNum == 2 )
{
// Skip "<cmd> "
char *p = Com_SkipTokens( args, 1, " " );
if( p > args )
Field_CompleteCommand( p, qfalse, qtrue );
}
}
/*
============
Cvar_Init
Reads in all archived cvars
============
*/
void Cvar_Init (void)
{
Com_Memset(cvar_indexes, '\0', sizeof(cvar_indexes));
Com_Memset(hashTable, '\0', sizeof(hashTable));
cvar_cheats = Cvar_Get("sv_cheats", "1", CVAR_ROM | CVAR_SYSTEMINFO );
Cmd_AddCommand ("print", Cvar_Print_f);
Cmd_AddCommand ("toggle", Cvar_Toggle_f);
Cmd_SetCommandCompletionFunc( "toggle", Cvar_CompleteCvarName );
Cmd_AddCommand ("set", Cvar_Set_f);
Cmd_SetCommandCompletionFunc( "set", Cvar_CompleteCvarName );
Cmd_AddCommand ("sets", Cvar_Set_f);
Cmd_SetCommandCompletionFunc( "sets", Cvar_CompleteCvarName );
Cmd_AddCommand ("setu", Cvar_Set_f);
Cmd_SetCommandCompletionFunc( "setu", Cvar_CompleteCvarName );
Cmd_AddCommand ("seta", Cvar_Set_f);
Cmd_SetCommandCompletionFunc( "seta", Cvar_CompleteCvarName );
Cmd_AddCommand ("reset", Cvar_Reset_f);
Cmd_SetCommandCompletionFunc( "reset", Cvar_CompleteCvarName );
Cmd_AddCommand ("unset", Cvar_Unset_f);
Cmd_SetCommandCompletionFunc("unset", Cvar_CompleteCvarName);
Cmd_AddCommand ("cvarlist", Cvar_List_f);
Cmd_AddCommand ("cvar_modified", Cvar_ListModified_f);
Cmd_AddCommand ("cvar_restart", Cvar_Restart_f);
}