1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 
19 */
20 // cvar.c -- dynamic variable tracking
21 
22 #include "qcommon.h"
23 
24 cvarAPI_t	cvar;
25 
26 cvar_t	*cvar_vars;
27 
28 uint32	cvar_latchedModified;
29 uint32	cvar_infoModified;
30 
31 #define Cvar_Malloc( size )		Z_TagMalloc( size, TAG_CVAR )
32 
33 #define CVARHASH_SIZE	256
34 
35 static cvar_t *cvarHash[CVARHASH_SIZE];
36 static cvarSubsystem_t currentSubsystem;
37 
38 static cvar_t	*cvar_silent;
39 
40 /*
41 ============
42 Cvar_FindVar
43 ============
44 */
Cvar_FindVar(const char * var_name)45 cvar_t *Cvar_FindVar( const char *var_name ) {
46 	cvar_t	*var;
47 	int hash;
48 
49 	hash = Com_HashString( var_name, CVARHASH_SIZE );
50 
51 	for( var = cvarHash[hash]; var; var = var->hashNext ) {
52 		if( !strcmp( var_name, var->name ) ) {
53 			return var;
54 		}
55 	}
56 
57 	return NULL;
58 }
59 
60 /*
61 ============
62 Cvar_Exists
63 ============
64 */
Cvar_Exists(const char * name)65 qboolean Cvar_Exists( const char *name ) {
66 	if( Cvar_FindVar( name ) ) {
67 		return qtrue;
68 	}
69 	return qfalse;
70 }
71 
72 /*
73 ============
74 Cvar_VariableValue
75 ============
76 */
Cvar_VariableValue(const char * var_name)77 float Cvar_VariableValue( const char *var_name ) {
78 	cvar_t	*var;
79 
80 	var = Cvar_FindVar( var_name );
81 	if( !var )
82 		return 0;
83 
84 	return var->value;
85 }
86 
87 /*
88 ============
89 Cvar_VariableInteger
90 ============
91 */
Cvar_VariableInteger(const char * var_name)92 int Cvar_VariableInteger( const char *var_name ) {
93 	cvar_t	*var;
94 
95 	var = Cvar_FindVar( var_name );
96 	if( !var )
97 		return 0;
98 
99 	return var->integer;
100 }
101 
102 
103 /*
104 ============
105 Cvar_VariableString
106 ============
107 */
Cvar_VariableString(const char * var_name)108 char *Cvar_VariableString( const char *var_name ) {
109 	cvar_t *var;
110 
111 	var = Cvar_FindVar( var_name );
112 	if( !var )
113 		return "";
114 
115 	return var->string;
116 }
117 
118 /*
119 ============
120 Cvar_VariableStringBuffer
121 ============
122 */
Cvar_VariableStringBuffer(const char * var_name,char * buffer,int bufferSize)123 void Cvar_VariableStringBuffer( const char *var_name, char *buffer,
124         int bufferSize )
125 {
126 	Q_strncpyz( buffer, Cvar_VariableString( var_name ), bufferSize );
127 }
128 
129 
130 
Cvar_Generator(const char * text,int state)131 const char *Cvar_Generator( const char *text, int state ) {
132     static int length;
133     static cvar_t *cvar;
134     const char *name;
135 
136     if( !state ) {
137         length = strlen( text );
138         cvar = cvar_vars;
139     }
140 
141     while( cvar ) {
142         name = cvar->name;
143         cvar = cvar->next;
144 		if( !strncmp( text, name, length ) ) {
145             return name;
146         }
147     }
148 
149     return NULL;
150 }
151 
Cvar_ParseString(cvar_t * var)152 static void Cvar_ParseString( cvar_t *var ) {
153 	char *s = var->string;
154 
155 	if( s[0] == '0' && s[1] == 'x' ) {
156 		var->integer = COM_ParseHex( s + 2 );
157 		var->value = ( float )var->integer;
158 	} else {
159 		var->integer = atoi( var->string );
160 		var->value = atof( var->string );
161 	}
162 }
163 
164 /*
165 ============
166 Cvar_Get
167 
168 If the variable already exists, the value will not be set
169 The flags will be or'ed in if the variable exists.
170 ============
171 */
Cvar_Get(const char * var_name,const char * var_value,int flags)172 cvar_t *Cvar_Get( const char *var_name, const char *var_value, int flags ) {
173 	cvar_t	*var;
174 	uint32 hash;
175 	int length;
176 
177 	if( !var_name ) {
178 		Com_Error( ERR_FATAL, "Cvar_Get: NULL variable name" );
179 	}
180 	if( !var_value ) {
181 		var_value = "";
182 	}
183 
184 	if( flags & CVAR_INFOMASK ) {
185 		length = strlen( var_name );
186 		if( length >= MAX_INFO_KEY ) {
187 			Com_WPrintf( "Oversize info cvar name: %d chars.\n", length );
188 			goto badvar;
189 		}
190 		if( !Info_ValidateSubstring( var_name ) ) {
191 			Com_WPrintf( "Invalid info cvar name.\n" );
192 			goto badvar;
193 		}
194 		length = strlen( var_value );
195 		if( length >= MAX_INFO_VALUE ) {
196 			Com_WPrintf( "Oversize info cvar value: %d chars.\n", length );
197 			goto badvar;
198 		}
199 		if( !Info_ValidateSubstring( var_value ) ) {
200 			Com_WPrintf( "Invalid info cvar value.\n" );
201 			goto badvar;
202 		}
203 	}
204 
205 	var = Cvar_FindVar( var_name );
206 	if( var ) {
207 		if( ( var->flags & CVAR_LATCHED ) && var->latched_string ) {
208 			if( strcmp( var->latched_string, var->string ) ) {
209 				// update latched cvar
210 				Z_Free( var->string );
211 				var->string = Cvar_CopyString( var->latched_string );
212 				Cvar_ParseString( var );
213 
214 				if( var->flags & CVAR_USERINFO ) {
215 					CL_UpdateUserinfo( var, CVAR_SET_DIRECT );
216 				}
217 
218 				var->modified = qtrue;
219 			}
220 
221 			Z_Free( var->latched_string );
222 			var->latched_string = NULL;
223 		}
224 
225 		if( !( flags & CVAR_USER_CREATED ) ) {
226 			if( var->flags & CVAR_USER_CREATED ) {
227 				/* update default string if cvar was set from command line */
228 				Z_Free( var->defaultString );
229 				var->defaultString = Cvar_CopyString( var_value );
230 				var->flags &= ~CVAR_USER_CREATED;
231 				var->subsystem = currentSubsystem;
232 			} else if( !( var->flags & CVAR_DEFAULTS_MIXED ) ) {
233 				if( strcmp( var->defaultString, var_value ) ) {
234 					Com_DPrintf( "Cvar \"%s\" given initial values: "
235 								"\"%s\" and \"%s\"\n",
236 									var->name, var->defaultString, var_value );
237 					var->flags |= CVAR_DEFAULTS_MIXED;
238 				}
239 			}
240             if( ( var->flags & CVAR_LATCHED ) && !( flags & CVAR_LATCHED ) ) {
241                 if( var->latched_string ) {
242                     Z_Free( var->latched_string );
243                     var->latched_string = NULL;
244                 }
245                 var->flags &= ~CVAR_LATCHED;
246             }
247 		} else {
248 			flags &= ~CVAR_USER_CREATED;
249 		}
250 
251 		var->flags |= flags;
252 		return var;
253 	}
254 
255 	/* once allocated, cvar name is never changed */
256 	length = strlen( var_name ) + 1;
257 	var = Cvar_Malloc( sizeof( *var ) + length );
258 	var->name = ( char * )( var + 1 );
259 	strcpy( var->name, var_name );
260 	var->string = Cvar_CopyString( var_value );
261     var->latched_string = NULL;
262 	var->defaultString = Cvar_CopyString( var_value );
263 	Cvar_ParseString( var );
264 	var->flags = flags;
265 	var->subsystem = currentSubsystem;
266     var->changedFunc = NULL;
267     var->changedArg = NULL;
268     var->description = NULL;
269 
270 	hash = Com_HashString( var_name, CVARHASH_SIZE );
271 
272 	// link the variable in
273 	var->next = cvar_vars;
274 	cvar_vars = var;
275 	var->hashNext = cvarHash[hash];
276 	cvarHash[hash] = var;
277 
278 	var->modified = qtrue;
279 
280 	return var;
281 
282 badvar:
283 	if( !( flags & CVAR_USER_CREATED ) ) {
284 		Com_Error( ERR_FATAL, "Cvar_Get: invalid info variable" );
285 	}
286 	return NULL;
287 }
288 
Cvar_Subsystem(cvarSubsystem_t subsystem)289 void Cvar_Subsystem( cvarSubsystem_t subsystem ) {
290 	currentSubsystem = subsystem;
291 }
292 
293 /*
294 ============
295 Cvar_SetByVar
296 ============
297 */
Cvar_SetByVar(cvar_t * var,const char * value,cvarSetSource_t source)298 void Cvar_SetByVar( cvar_t *var, const char *value, cvarSetSource_t source ) {
299 	int length;
300 
301 	if( !strcmp( value, var->string ) &&
302             !( var->flags & (CVAR_LATCHED|CVAR_LATCH) ) )
303     {
304 		return;		// not changed
305     }
306 
307 	Com_DPrintf( "Cvar_Set( \"%s\", \"%s\" )\n", var->name, value );
308 
309 	if( var->flags & CVAR_INFOMASK ) {
310 		length = strlen( value );
311 		if( length >= MAX_INFO_VALUE ) {
312 			Com_WPrintf(
313                     "Oversize info cvar value specified (%d chars), ignored.\n",
314                     length );
315 			return;
316 		}
317 		if( !Info_ValidateSubstring( value ) ) {
318 			Com_WPrintf( "Invalid info cvar value specified, ignored.\n" );
319 			return;
320 		}
321 	}
322 
323 	/* some cvars may not be changed by user at all */
324 	if( source != CVAR_SET_DIRECT ) {
325 		if( var->flags & CVAR_ROM ) {
326 			Com_Printf( "%s is read-only.\n", var->name );
327 			return;
328 		}
329 
330 		if( var->flags & CVAR_CHEAT ) {
331 			if( !CL_CheatsOK() ) {
332 				Com_Printf( "%s is cheat protected.\n", var->name );
333 				return;
334 			}
335 		}
336 	}
337 
338 	/* some cvars may require special processing if set by user from console */
339 	if( source == CVAR_SET_CONSOLE ) {
340 		if( var->flags & CVAR_NOSET ) {
341 			Com_Printf( "%s may be set from command line only.\n", var->name );
342 			return;
343 		}
344 
345 		if( var->flags & (CVAR_LATCHED|CVAR_LATCH) ) {
346 			if( !strcmp( var->string, value ) ) {
347 				/* set back to current value? */
348 				if( var->latched_string ) {
349 					Z_Free( var->latched_string );
350 					var->latched_string = NULL;
351 				}
352 				return;
353 			}
354 			if( var->latched_string ) {
355 				if( !strcmp( var->latched_string, value ) ) {
356 					/* latched string not changed? */
357 					return;
358 				}
359 				Z_Free( var->latched_string );
360 				var->latched_string = NULL;
361 			}
362 
363 
364 			if( var->flags & CVAR_LATCH ) {
365 				if( sv_running->integer ) {
366 					if( !cvar_silent->integer ) {
367 						Com_Printf( "%s will be changed for next game.\n",
368                             var->name );
369 					}
370 					var->latched_string = Cvar_CopyString( value );
371 					return;
372 				}
373 				/* server is down, it's ok to update this cvar now */
374 			} else {
375 				char *subsystem;
376 
377 				var->latched_string = Cvar_CopyString( value );
378 				cvar_latchedModified |= 1 << var->subsystem;
379 				if( cvar_silent->integer ) {
380 					return;
381 				}
382 
383 				switch( var->subsystem ) {
384 				case CVAR_SYSTEM_GENERIC:
385 					subsystem = "desired subsystem";
386 					break;
387 				case CVAR_SYSTEM_VIDEO:
388 					subsystem = "video subsystem";
389 					break;
390 				case CVAR_SYSTEM_SOUND:
391 					subsystem = "sound subsystem";
392 					break;
393 				case CVAR_SYSTEM_INPUT:
394 					subsystem = "input subsystem";
395 					break;
396 				case CVAR_SYSTEM_NET:
397 					subsystem = "network subsystem";
398 					break;
399 				case CVAR_SYSTEM_FILES:
400 					subsystem = "filesystem";
401 					break;
402 				default:
403 					Com_Error( ERR_FATAL, "Cvar_SetByVar: invalid subsystem %u",
404                             var->subsystem );
405 					subsystem = NULL;
406 					break;
407 				}
408 				Com_Printf( "%s will be changed upon restarting the %s.\n",
409 					var->name, subsystem );
410 				return;
411 			}
412 		}
413 
414 	}
415 
416 	/* free latched string, if any */
417 	if( var->latched_string ) {
418 		Z_Free( var->latched_string );
419 		var->latched_string = NULL;
420 	}
421 
422 	Z_Free( var->string );	// free the old value string
423 
424 	var->string = Cvar_CopyString( value );
425 	Cvar_ParseString( var );
426 
427 	if( var->flags & CVAR_INFOMASK ) {
428 		cvar_infoModified |= var->flags & CVAR_INFOMASK;
429 		if( var->flags & CVAR_USERINFO ) {
430 			CL_UpdateUserinfo( var, source );
431 		}
432 	}
433 
434 	var->modified = qtrue;
435 	if( source != CVAR_SET_DIRECT && var->changedFunc ) {
436 		var->changedFunc( var, var->changedArg );
437 	}
438 }
439 
440 /*
441 ============
442 Cvar_SetEx
443 ============
444 */
Cvar_SetEx(const char * var_name,const char * value,cvarSetSource_t source)445 cvar_t *Cvar_SetEx( const char *var_name, const char *value,
446         cvarSetSource_t source )
447 {
448 	cvar_t	*var;
449 
450 	var = Cvar_FindVar( var_name );
451 	if( !var ) {
452 		// create it
453 		return Cvar_Get( var_name, value, CVAR_USER_CREATED );
454 	}
455 
456 	Cvar_SetByVar( var, value, source );
457 
458 	return var;
459 }
460 
461 /*
462 ============
463 Cvar_FullSet
464 ============
465 */
Cvar_FullSet(const char * var_name,const char * value,int flags,cvarSetSource_t source)466 cvar_t *Cvar_FullSet( const char *var_name, const char *value, int flags, cvarSetSource_t source ) {
467 	cvar_t *var;
468 
469 	var = Cvar_FindVar( var_name );
470 	if( !var ) {
471 		// create it
472 		return Cvar_Get( var_name, value, flags | CVAR_USER_CREATED );
473 	}
474 
475 	Cvar_SetByVar( var, value, source );
476 
477 	// force retransmit of userinfo variables
478 	// needed for compatibility with q2admin
479 	if( ( var->flags | flags ) & CVAR_USERINFO ) {
480 		cvar_infoModified |= CVAR_USERINFO;
481 		CL_UpdateUserinfo( var, source );
482 	}
483 
484 	var->flags &= ~CVAR_INFOMASK;
485 	var->flags |= flags;
486 
487 	return var;
488 }
489 
490 /*
491 ============
492 Cvar_Set
493 ============
494 */
Cvar_Set(const char * var_name,const char * value)495 cvar_t *Cvar_Set( const char *var_name, const char *value ) {
496 	return Cvar_SetEx( var_name, value, CVAR_SET_DIRECT );
497 }
498 
499 /*
500 ============
501 Cvar_UserSet
502 ============
503 */
Cvar_UserSet(const char * var_name,const char * value)504 cvar_t *Cvar_UserSet( const char *var_name, const char *value ) {
505 	return Cvar_SetEx( var_name, value, CVAR_SET_CONSOLE );
506 }
507 
508 
509 /*
510 ============
511 Cvar_SetValue
512 ============
513 */
Cvar_SetValue(const char * var_name,float value)514 void Cvar_SetValue( const char *var_name, float value ) {
515 	char	val[32];
516 
517 	if( value == (int)value )
518 		Com_sprintf( val, sizeof( val ), "%i", (int)value );
519 	else
520 		Com_sprintf( val, sizeof( val ), "%f", value);
521 
522 	Cvar_SetEx( var_name, val, CVAR_SET_DIRECT );
523 }
524 
525 /*
526 ============
527 Cvar_SetInteger
528 ============
529 */
Cvar_SetInteger(const char * var_name,int value)530 void Cvar_SetInteger( const char *var_name, int value ) {
531 	char	val[32];
532 
533 	Com_sprintf( val, sizeof( val ), "%i", value );
534 
535 	Cvar_SetEx( var_name, val, CVAR_SET_DIRECT );
536 }
537 
538 /*
539 ============
540 Cvar_SetInteger
541 ============
542 */
Cvar_SetIntegerHex(const char * var_name,uint32 value)543 void Cvar_SetIntegerHex( const char *var_name, uint32 value ) {
544 	char	val[32];
545 
546 	Com_sprintf( val, sizeof( val ), "0x%X", value );
547 
548 	Cvar_SetEx( var_name, val, CVAR_SET_DIRECT );
549 }
550 
551 /*
552 ============
553 Cvar_ClampInteger
554 ============
555 */
Cvar_ClampInteger(cvar_t * var,int min,int max)556 void Cvar_ClampInteger( cvar_t *var, int min, int max ) {
557 	char	val[32];
558 
559 	if( var->integer < min ) {
560 		Com_sprintf( val, sizeof( val ), "%i", min );
561 		Cvar_SetByVar( var, val, CVAR_SET_DIRECT );
562 	} else if( var->integer > max ) {
563 		Com_sprintf( val, sizeof( val ), "%i", max );
564 		Cvar_SetByVar( var, val, CVAR_SET_DIRECT );
565 	}
566 }
567 
568 /*
569 ============
570 Cvar_ClampValue
571 ============
572 */
Cvar_ClampValue(cvar_t * var,float min,float max)573 void Cvar_ClampValue( cvar_t *var, float min, float max ) {
574 	char	val[32];
575 
576 	if( var->value < min ) {
577 		if( min == (int)min ) {
578 			Com_sprintf( val, sizeof( val ), "%i", (int)min );
579 		} else {
580 			Com_sprintf( val, sizeof( val ), "%f", min );
581 		}
582 		Cvar_SetByVar( var, val, CVAR_SET_DIRECT );
583 	} else if( var->value > max ) {
584 		if( max == (int)max ) {
585 			Com_sprintf( val, sizeof( val ), "%i", (int)max );
586 		} else {
587 			Com_sprintf( val, sizeof( val ), "%f", max );
588 		}
589 		Cvar_SetByVar( var, val, CVAR_SET_DIRECT );
590 	}
591 }
592 
593 /*
594 ==================
595 Cvar_FixCheats
596 ==================
597 */
Cvar_FixCheats(void)598 void Cvar_FixCheats( void ) {
599    cvar_t *var;
600 
601     if ( CL_CheatsOK() ) {
602         return;
603     }
604 
605     // fix any cheating cvars
606     for( var = cvar_vars ; var ; var = var->next ) {
607 		if( var->flags & CVAR_CHEAT ) {
608 			Cvar_SetByVar( var, var->defaultString, CVAR_SET_DIRECT );
609 		}
610     }
611 }
612 
613 /*
614 ============
615 Cvar_GetLatchedVars
616 
617 Any variables with latched values will now be updated
618 Only used for game latched cvars now
619 ============
620 */
Cvar_GetLatchedVars(void)621 void Cvar_GetLatchedVars( void ) {
622 	cvar_t	*var;
623 
624 	for( var = cvar_vars; var; var = var->next ) {
625 		if( !(var->flags & CVAR_LATCH) )
626 			continue;
627 		if( var->flags & CVAR_LATCHED )
628 			continue; // don't update engine cvars
629 		if( !var->latched_string )
630 			continue;
631 		Z_Free( var->string );
632 		var->string = var->latched_string;
633 		var->latched_string = NULL;
634 		Cvar_ParseString( var );
635 
636 		var->modified = qtrue;
637 	}
638 }
639 
640 /*
641 ============
642 Cvar_Command
643 
644 Handles variable inspection and changing from the console
645 ============
646 */
Cvar_Command(void)647 qboolean Cvar_Command( void ) {
648 	cvar_t			*v;
649 
650 // check variables
651 	v = Cvar_FindVar( Cmd_Argv( 0 ) );
652 	if( !v )
653 		return qfalse;
654 
655 // perform a variable print or set
656 	if( Cmd_Argc() == 1 ) {
657 		if( v->flags & CVAR_ROM ) {
658 			Com_Printf( "\"%s\" is \"%s\"\n", v->name, v->string );
659 		} else {
660 			Com_Printf( "\"%s\" is \"%s\"  ", v->name, v->string );
661             if( strcmp( v->string, v->defaultString ) ) {
662 			    Com_Printf( "default: \"%s\"  ", v->defaultString );
663             }
664 			if( v->latched_string && strcmp( v->latched_string, v->string ) ) {
665 				Com_Printf( "latched: \"%s\"", v->latched_string );
666 			}
667 			Com_Printf( "\n" );
668 		}
669 		return qtrue;
670 	}
671 
672 	Cvar_SetByVar( v, Cmd_Argv( 1 ), CVAR_SET_CONSOLE );
673 
674 	return qtrue;
675 }
676 
677 
678 /*
679 ============
680 Cvar_Set_f
681 
682 Allows setting and defining of arbitrary cvars from console
683 ============
684 */
Cvar_Set_f(void)685 void Cvar_Set_f( void ) {
686 	int		c, flags;
687 	char	*f;
688 	cvar_t	*var;
689 
690 	c = Cmd_Argc();
691 	if( c != 3 && c != 4 ) {
692 		Com_Printf( "Usage: set <variable> <value> [u / s]\n" );
693 		return;
694 	}
695 
696 	if( c != 4 ) {
697 		Cvar_SetEx( Cmd_Argv( 1 ), Cmd_Argv( 2 ), CVAR_SET_CONSOLE );
698 		return;
699 	}
700 
701 	f = Cmd_Argv( 3 );
702 	if( !strcmp( f, "u" ) ) {
703 		flags = CVAR_USERINFO;
704 	} else if( !strcmp( f, "s" ) ) {
705 		flags = CVAR_SERVERINFO;
706 	} else {
707 		Com_Printf( "Flags can only be 'u' or 's'\n" );
708 		return;
709 	}
710 
711 	var = Cvar_FullSet( Cmd_Argv( 1 ), Cmd_Argv( 2 ), flags, CVAR_SET_CONSOLE );
712 	if( !var ) {
713 		return;
714 	}
715 }
716 
717 /*
718 ============
719 Cvar_SetFlag_f
720 
721 Allows setting and defining of arbitrary cvars from console
722 ============
723 */
Cvar_SetFlag_f(void)724 static void Cvar_SetFlag_f( void ) {
725 	char	*s;
726 	int		flags;
727 
728 	if( Cmd_Argc() < 3 ) {
729 		Com_Printf( "Usage: %s <variable> <value>\n", Cmd_Argv( 0 ) );
730 		return;
731 	}
732 
733 	s = Cmd_Argv( 0 );
734 	if( !Q_stricmp( s, "setu" ) ) {
735 		flags = CVAR_USERINFO;
736 	} else if( !Q_stricmp( s, "sets" ) ) {
737 		flags = CVAR_SERVERINFO;
738 	} else if( !Q_stricmp( s, "seta" ) ) {
739 		flags = CVAR_ARCHIVE;
740 	} else {
741 		return;
742 	}
743 
744 	Cvar_FullSet( Cmd_Argv( 1 ), Cmd_ArgsFrom( 2 ), flags, CVAR_SET_CONSOLE );
745 }
746 
747 #ifndef DEDICATED_ONLY
748 
749 /*
750 ============
751 Cvar_WriteVariables
752 
753 Appends lines containing "set variable value" for all variables
754 with the archive flag set to true.
755 ============
756 */
Cvar_WriteVariables(fileHandle_t f)757 void Cvar_WriteVariables( fileHandle_t f ) {
758 	cvar_t	*var;
759     char *string;
760 
761 	for( var = cvar_vars; var; var = var->next ) {
762 		if( var->flags & CVAR_ARCHIVE ) {
763             if( ( var->flags & CVAR_LATCHED ) && var->latched_string ) {
764                 string = var->latched_string;
765             } else {
766                 string = var->string;
767             }
768 			FS_FPrintf( f, "set %s \"%s\"\n", var->name, string );
769 		}
770 	}
771 }
772 
773 #endif
774 
775 /*
776 ============
777 Cvar_List_f
778 
779 ============
780 */
Cvar_List_f(void)781 static void Cvar_List_f( void ) {
782 	cvar_t	*var;
783 	int		i, total;
784 	char *wildcard;
785 	int showDesc;
786 	int showFlags;
787 	char buffer[6];
788 
789 	if( Cmd_CheckParam( "h", "help" ) ) {
790 		Com_Printf( "Usage: %s [-f] [-d] [-h] [-w <wildcard>]\n",
791                 Cmd_Argv( 0 ) );
792 		Com_Printf( "Options:\n"
793 					"-d, --desc     : display description of each cvar\n"
794 					"-f, --flags    : display flags of each cvar\n"
795 					"-h, --help     : display this help message\n"
796 					"-w, --wildcard : filter cvars by wildcard\n"
797 					"Flags legend:\n"
798 					"C: cheat protected\n"
799 					"A: archived in config file\n"
800 					"U: included in userinfo\n"
801 					"S: included in serverinfo\n"
802 					"N: set from command line only\n"
803 					"R: read-only variable\n"
804 					"L: latched (requires subsystem restart)\n"
805 					"G: latched (requires game map restart)\n"
806 					"?: created by user\n" );
807 		return;
808 	}
809 
810 	showFlags = Cmd_CheckParam( "f", "flags" );
811 	showDesc = Cmd_CheckParam( "d", "desc" );
812 	wildcard = Cmd_FindParam( "w", "wildcard" );
813 
814 	buffer[sizeof( buffer ) - 1] = 0;
815 	i = 0;
816 	for( var = cvar_vars, total = 0; var; var = var->next, total++ ) {
817 		if( wildcard && !Com_WildCmp( wildcard, var->name, qtrue ) ) {
818 			continue;
819 		}
820 
821 		if( showFlags ) {
822 			memset( buffer, ' ', sizeof( buffer ) - 1 );
823 
824 			if( var->flags & CVAR_CHEAT )
825 				buffer[0] = 'C';
826 
827 			if( var->flags & CVAR_ARCHIVE )
828 				buffer[1] = 'A';
829 
830 			if( var->flags & CVAR_USERINFO )
831 				buffer[2] = 'U';
832 
833 			if( var->flags & CVAR_SERVERINFO )
834 				buffer[3] = 'S';
835 
836 			if( var->flags & CVAR_ROM )
837 				buffer[4] = 'R';
838             else if( var->flags & CVAR_NOSET )
839 				buffer[4] = 'N';
840 			else if( var->flags & CVAR_LATCHED )
841 				buffer[4] = 'L';
842 			else if( var->flags & CVAR_LATCH )
843 				buffer[4] = 'G';
844 			else if( var->flags & CVAR_USER_CREATED )
845 				buffer[4] = '?';
846 
847 			Com_Printf( "%s ", buffer );
848 		}
849 
850 		if( showDesc && var->description ) {
851 			Com_Printf( "%s \"%s\" %s\n", var->name, var->string,
852                     var->description );
853 		} else {
854 			Com_Printf( "%s \"%s\"\n", var->name, var->string );
855 		}
856 
857 		i++;
858 	}
859 	Com_Printf( "%i of %i cvars\n", i, total );
860 }
861 
862 /*
863 ============
864 Cvar_Toggle_f
865 ============
866 */
Cvar_Toggle_f(void)867 static void Cvar_Toggle_f( void ) {
868 	char buffer[MAX_STRING_CHARS];
869 	cvar_t *var;
870 	int i;
871 	int numArgs;
872 
873 	if( Cmd_Argc() < 2 ) {
874 		Com_Printf( "Usage: %s <variable> [values]\n", Cmd_Argv( 0 ) );
875 		return;
876 	}
877 
878 	var = Cvar_FindVar( Cmd_Argv( 1 ) );
879 	if( !var ) {
880 		Com_Printf( "%s is not a variable\n", Cmd_Argv( 1 ) );
881 		return;
882 	}
883 
884 	if( Cmd_Argc() < 3 ) {
885 		if( !strcmp( var->string, "0" ) ) {
886 			Cvar_SetByVar( var, "1", CVAR_SET_CONSOLE );
887 		} else if( !strcmp( var->string, "1" ) ) {
888 			Cvar_SetByVar( var, "0", CVAR_SET_CONSOLE );
889 		} else {
890 			Com_Printf( "\"%s\" is \"%s\", can't toggle\n",
891                     var->name, var->string );
892 		}
893 		return;
894 	}
895 
896 	Q_strncpyz( buffer, Cmd_RawArgsFrom( 2 ), sizeof( buffer ) );
897 	Cmd_TokenizeString( buffer, qfalse );
898 	numArgs = Cmd_Argc();
899 	for( i = 0; i < numArgs; i++ ) {
900 		if( !Q_stricmp( var->string, Cmd_Argv( i ) ) ) {
901 			i = ( i + 1 ) % numArgs;
902 			Cvar_SetByVar( var, Cmd_Argv( i ), CVAR_SET_CONSOLE );
903 			return;
904 		}
905 	}
906 
907 	Com_Printf( "\"%s\" is \"%s\", can't cycle\n", var->name, var->string );
908 }
909 
910 /*
911 ============
912 Cvar_Inc_f
913 ============
914 */
Cvar_Inc_f(void)915 static void Cvar_Inc_f( void ) {
916 	cvar_t *var;
917 	float val;
918 
919 	if( Cmd_Argc() < 2 ) {
920 		Com_Printf( "Usage: %s <variable> [value]\n", Cmd_Argv( 0 ) );
921 		return;
922 	}
923 
924 	var = Cvar_FindVar( Cmd_Argv( 1 ) );
925 	if( !var ) {
926 		Com_Printf( "%s is not a variable\n", Cmd_Argv( 1 ) );
927 		return;
928 	}
929 
930 	if( !COM_IsNumeric( var->string ) ) {
931 		Com_Printf( "\"%s\" is \"%s\", can't increment\n",
932                 var->name, var->string );
933 		return;
934 	}
935 
936 	val = 1.0f;
937 	if( Cmd_Argc() > 2 ) {
938 		val = atof( Cmd_Argv( 2 ) );
939 	}
940 
941 	Cvar_SetValue( var->name, var->value + val );
942 }
943 
944 /*
945 ============
946 Cvar_Reset_f
947 ============
948 */
Cvar_Reset_f(void)949 static void Cvar_Reset_f( void ) {
950 	cvar_t *var;
951 
952 	if( Cmd_Argc() < 2 ) {
953 		Com_Printf( "Usage: %s <variable>\n", Cmd_Argv( 0 ) );
954 		return;
955 	}
956 
957 	var = Cvar_FindVar( Cmd_Argv( 1 ) );
958 	if( !var ) {
959 		Com_Printf( "%s is not a variable\n", Cmd_Argv( 1 ) );
960 		return;
961 	}
962 
963 	Cvar_SetByVar( var, var->defaultString, CVAR_SET_CONSOLE );
964 }
965 
Cvar_BitInfo(int bit)966 char *Cvar_BitInfo( int bit ) {
967 	static char	info[MAX_INFO_STRING];
968 	char newi[MAX_INFO_STRING];
969 	cvar_t	*var;
970 	int length, totalLength;
971 
972 	info[0] = 0;
973 	totalLength = 0;
974 
975 	for( var = cvar_vars; var; var = var->next ) {
976 		if( !( var->flags & bit ) ) {
977             continue;
978         }
979         if( !var->string[0] ) {
980             continue;
981         }
982         length = Com_sprintf( newi, sizeof( newi ), "\\%s\\%s",
983                 var->name, var->string );
984         if( totalLength + length > MAX_INFO_STRING - 1 ) {
985             break;
986         }
987         strcpy( info + totalLength, newi );
988         totalLength += length;
989 	}
990 	return info;
991 }
992 
993 // used for generating enhanced server status response
Cvar_BitInfo_Big(int bit)994 char *Cvar_BitInfo_Big( int bit ) {
995 	static char	info[MAX_STRING_CHARS];
996 	char newi[MAX_STRING_CHARS];
997 	cvar_t	*var;
998 	int length, totalLength;
999 
1000 	info[0] = 0;
1001 	totalLength = 0;
1002 
1003 	for( var = cvar_vars; var; var = var->next ) {
1004 		if( !( var->flags & bit ) ) {
1005             continue;
1006         }
1007         if( !var->string[0] ) {
1008             continue;
1009         }
1010         length = Com_sprintf( newi, sizeof( newi ), "\\%s\\%s",
1011                 var->name, var->string );
1012         if( totalLength + length > MAX_STRING_CHARS - 1 ) {
1013             break;
1014         }
1015         strcpy( info + totalLength, newi );
1016         totalLength += length;
1017 	}
1018 	return info;
1019 }
1020 
1021 /*
1022 ============
1023 Cvar_FillAPI
1024 ============
1025 */
Cvar_FillAPI(cvarAPI_t * api)1026 void Cvar_FillAPI( cvarAPI_t *api ) {
1027 	api->Get = Cvar_Get;
1028 	api->Set = Cvar_Set;
1029 	api->SetValue = Cvar_SetValue;
1030 	api->SetInteger = Cvar_SetInteger;
1031 	api->VariableValue = Cvar_VariableValue;
1032 	api->VariableInteger = Cvar_VariableInteger;
1033 	api->VariableString = Cvar_VariableString;
1034 	api->VariableStringBuffer = Cvar_VariableStringBuffer;
1035 	api->Subsystem = Cvar_Subsystem;
1036 }
1037 
1038 /*
1039 ============
1040 Cvar_Init
1041 ============
1042 */
Cvar_Init(void)1043 void Cvar_Init( void ) {
1044 	cvar_silent = Cvar_Get( "cvar_silent", "0", 0 );
1045 
1046 	Cmd_AddCommandEx( "set", Cvar_Set_f, Cvar_Generator );
1047 	Cmd_AddCommandEx( "setu", Cvar_SetFlag_f, Cvar_Generator );
1048 	Cmd_AddCommandEx( "sets", Cvar_SetFlag_f, Cvar_Generator );
1049 	Cmd_AddCommandEx( "seta", Cvar_SetFlag_f, Cvar_Generator );
1050 	Cmd_AddCommand( "cvarlist", Cvar_List_f );
1051 	Cmd_AddCommandEx( "cvar_toggle", Cvar_Toggle_f, Cvar_Generator );
1052 	Cmd_AddCommandEx( "cvar_inc", Cvar_Inc_f, Cvar_Generator );
1053 	Cmd_AddCommandEx( "cvar_reset", Cvar_Reset_f, Cvar_Generator );
1054 
1055 	Cvar_FillAPI( &cvar );
1056 }
1057 
1058