1 /*
2 ===========================================================================
3 
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
8 
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 #include "sys/platform.h"
30 #include "framework/async/AsyncNetwork.h"
31 #include "framework/Common.h"
32 #include "framework/Session.h"
33 
34 #include "framework/CVarSystem.h"
35 
36 idCVar * idCVar::staticVars = NULL;
37 
38 /*
39 ===============================================================================
40 
41 	idInternalCVar
42 
43 ===============================================================================
44 */
45 
46 class idInternalCVar : public idCVar {
47 	friend class idCVarSystemLocal;
48 public:
49 							idInternalCVar( void );
50 							idInternalCVar( const char *newName, const char *newValue, int newFlags );
51 							idInternalCVar( const idCVar *cvar );
52 	virtual					~idInternalCVar( void );
53 
54 	const char **			CopyValueStrings( const char **strings );
55 	void					Update( const idCVar *cvar );
56 	void					UpdateValue( void );
57 	void					UpdateCheat( void );
58 	void					Set( const char *newValue, bool force, bool fromServer );
59 	void					Reset( void );
60 
61 private:
62 	idStr					nameString;				// name
63 	idStr					resetString;			// resetting will change to this value
64 	idStr					valueString;			// value
65 	idStr					descriptionString;		// description
66 
67 	virtual void			InternalSetString( const char *newValue );
68 	virtual void			InternalServerSetString( const char *newValue );
69 	virtual void			InternalSetBool( const bool newValue );
70 	virtual void			InternalSetInteger( const int newValue );
71 	virtual void			InternalSetFloat( const float newValue );
72 };
73 
74 /*
75 ============
76 idInternalCVar::idInternalCVar
77 ============
78 */
idInternalCVar(void)79 idInternalCVar::idInternalCVar( void ) {
80 }
81 
82 /*
83 ============
84 idInternalCVar::idInternalCVar
85 ============
86 */
idInternalCVar(const char * newName,const char * newValue,int newFlags)87 idInternalCVar::idInternalCVar( const char *newName, const char *newValue, int newFlags ) {
88 	nameString = newName;
89 	name = nameString.c_str();
90 	valueString = newValue;
91 	value = valueString.c_str();
92 	resetString = newValue;
93 	descriptionString = "";
94 	description = descriptionString.c_str();
95 	flags = ( newFlags & ~CVAR_STATIC ) | CVAR_MODIFIED;
96 	valueMin = 1;
97 	valueMax = -1;
98 	valueStrings = NULL;
99 	valueCompletion = 0;
100 	UpdateValue();
101 	UpdateCheat();
102 	internalVar = this;
103 }
104 
105 /*
106 ============
107 idInternalCVar::idInternalCVar
108 ============
109 */
idInternalCVar(const idCVar * cvar)110 idInternalCVar::idInternalCVar( const idCVar *cvar ) {
111 	nameString = cvar->GetName();
112 	name = nameString.c_str();
113 	valueString = cvar->GetString();
114 	value = valueString.c_str();
115 	resetString = cvar->GetString();
116 	descriptionString = cvar->GetDescription();
117 	description = descriptionString.c_str();
118 	flags = cvar->GetFlags() | CVAR_MODIFIED;
119 	valueMin = cvar->GetMinValue();
120 	valueMax = cvar->GetMaxValue();
121 	valueStrings = CopyValueStrings( cvar->GetValueStrings() );
122 	valueCompletion = cvar->GetValueCompletion();
123 	UpdateValue();
124 	UpdateCheat();
125 	internalVar = this;
126 }
127 
128 /*
129 ============
130 idInternalCVar::~idInternalCVar
131 ============
132 */
~idInternalCVar(void)133 idInternalCVar::~idInternalCVar( void ) {
134 	Mem_Free( valueStrings );
135 	valueStrings = NULL;
136 }
137 
138 
139 /*
140 ============
141 idInternalCVar::CopyValueStrings
142 ============
143 */
CopyValueStrings(const char ** strings)144 const char **idInternalCVar::CopyValueStrings( const char **strings ) {
145 	int i, totalLength;
146 	const char **ptr;
147 	char *str;
148 
149 	if ( !strings ) {
150 		return NULL;
151 	}
152 
153 	totalLength = 0;
154 	for ( i = 0; strings[i] != NULL; i++ ) {
155 		totalLength += idStr::Length( strings[i] ) + 1;
156 	}
157 
158 	ptr = (const char **) Mem_Alloc( ( i + 1 ) * sizeof( char * ) + totalLength );
159 	str = (char *) (((byte *)ptr) + ( i + 1 ) * sizeof( char * ) );
160 
161 	for ( i = 0; strings[i] != NULL; i++ ) {
162 		ptr[i] = str;
163 		strcpy( str, strings[i] );
164 		str += idStr::Length( strings[i] ) + 1;
165 	}
166 	ptr[i] = NULL;
167 
168 	return ptr;
169 }
170 
171 /*
172 ============
173 idInternalCVar::Update
174 ============
175 */
Update(const idCVar * cvar)176 void idInternalCVar::Update( const idCVar *cvar ) {
177 
178 	// if this is a statically declared variable
179 	if ( cvar->GetFlags() & CVAR_STATIC ) {
180 
181 		if ( flags & CVAR_STATIC ) {
182 
183 			// the code has more than one static declaration of the same variable, make sure they have the same properties
184 			if ( resetString.Icmp( cvar->GetString() ) != 0 ) {
185 				common->Warning( "CVar '%s' declared multiple times with different initial value", nameString.c_str() );
186 			}
187 			if ( ( flags & (CVAR_BOOL|CVAR_INTEGER|CVAR_FLOAT) ) != ( cvar->GetFlags() & (CVAR_BOOL|CVAR_INTEGER|CVAR_FLOAT) ) ) {
188 				common->Warning( "CVar '%s' declared multiple times with different type", nameString.c_str() );
189 			}
190 			if ( valueMin != cvar->GetMinValue() || valueMax != cvar->GetMaxValue() ) {
191 				common->Warning( "CVar '%s' declared multiple times with different minimum/maximum", nameString.c_str() );
192 			}
193 
194 		}
195 
196 		// the code is now specifying a variable that the user already set a value for, take the new value as the reset value
197 		resetString = cvar->GetString();
198 		descriptionString = cvar->GetDescription();
199 		description = descriptionString.c_str();
200 		valueMin = cvar->GetMinValue();
201 		valueMax = cvar->GetMaxValue();
202 		Mem_Free( valueStrings );
203 		valueStrings = CopyValueStrings( cvar->GetValueStrings() );
204 		valueCompletion = cvar->GetValueCompletion();
205 		UpdateValue();
206 		cvarSystem->SetModifiedFlags( cvar->GetFlags() );
207 	}
208 
209 	flags |= cvar->GetFlags();
210 
211 	UpdateCheat();
212 
213 	// only allow one non-empty reset string without a warning
214 	if ( resetString.Length() == 0 ) {
215 		resetString = cvar->GetString();
216 	} else if ( cvar->GetString()[0] && resetString.Cmp( cvar->GetString() ) != 0 ) {
217 		common->Warning( "cvar \"%s\" given initial values: \"%s\" and \"%s\"\n", nameString.c_str(), resetString.c_str(), cvar->GetString() );
218 	}
219 }
220 
221 /*
222 ============
223 idInternalCVar::UpdateValue
224 ============
225 */
UpdateValue(void)226 void idInternalCVar::UpdateValue( void ) {
227 	bool clamped = false;
228 
229 	if ( flags & CVAR_BOOL ) {
230 		integerValue = ( atoi( value ) != 0 );
231 		floatValue = integerValue;
232 		if ( idStr::Icmp( value, "0" ) != 0 && idStr::Icmp( value, "1" ) != 0 ) {
233 			valueString = idStr( (bool)( integerValue != 0 ) );
234 			value = valueString.c_str();
235 		}
236 	} else if ( flags & CVAR_INTEGER ) {
237 		integerValue = (int)atoi( value );
238 		if ( valueMin < valueMax ) {
239 			if ( integerValue < valueMin ) {
240 				integerValue = (int)valueMin;
241 				clamped = true;
242 			} else if ( integerValue > valueMax ) {
243 				integerValue = (int)valueMax;
244 				clamped = true;
245 			}
246 		}
247 		if ( clamped || !idStr::IsNumeric( value ) || idStr::FindChar( value, '.' ) ) {
248 			valueString = idStr( integerValue );
249 			value = valueString.c_str();
250 		}
251 		floatValue = (float)integerValue;
252 	} else if ( flags & CVAR_FLOAT ) {
253 		floatValue = (float)atof( value );
254 		if ( valueMin < valueMax ) {
255 			if ( floatValue < valueMin ) {
256 				floatValue = valueMin;
257 				clamped = true;
258 			} else if ( floatValue > valueMax ) {
259 				floatValue = valueMax;
260 				clamped = true;
261 			}
262 		}
263 		if ( clamped || !idStr::IsNumeric( value ) ) {
264 			valueString = idStr( floatValue );
265 			value = valueString.c_str();
266 		}
267 		integerValue = (int)floatValue;
268 	} else {
269 		if ( valueStrings && valueStrings[0] ) {
270 			integerValue = 0;
271 			for ( int i = 0; valueStrings[i]; i++ ) {
272 				if ( valueString.Icmp( valueStrings[i] ) == 0 ) {
273 					integerValue = i;
274 					break;
275 				}
276 			}
277 			valueString = valueStrings[integerValue];
278 			value = valueString.c_str();
279 			floatValue = (float)integerValue;
280 		} else if ( valueString.Length() < 32 ) {
281 			floatValue = (float)atof( value );
282 			integerValue = (int)floatValue;
283 		} else {
284 			floatValue = 0.0f;
285 			integerValue = 0;
286 		}
287 	}
288 }
289 
290 /*
291 ============
292 idInternalCVar::UpdateCheat
293 ============
294 */
UpdateCheat(void)295 void idInternalCVar::UpdateCheat( void ) {
296 	// all variables are considered cheats except for a few types
297 	if ( flags & ( CVAR_NOCHEAT | CVAR_INIT | CVAR_ROM | CVAR_ARCHIVE | CVAR_USERINFO | CVAR_SERVERINFO | CVAR_NETWORKSYNC ) ) {
298 		flags &= ~CVAR_CHEAT;
299 	} else {
300 		flags |= CVAR_CHEAT;
301 	}
302 }
303 
304 /*
305 ============
306 idInternalCVar::Set
307 ============
308 */
Set(const char * newValue,bool force,bool fromServer)309 void idInternalCVar::Set( const char *newValue, bool force, bool fromServer ) {
310 	if ( session && session->IsMultiplayer() && !fromServer ) {
311 #ifndef ID_TYPEINFO
312 		if ( ( flags & CVAR_NETWORKSYNC ) && idAsyncNetwork::client.IsActive() ) {
313 			common->Printf( "%s is a synced over the network and cannot be changed on a multiplayer client.\n", nameString.c_str() );
314 #if ID_ALLOW_CHEATS
315 			common->Printf( "ID_ALLOW_CHEATS override!\n" );
316 #else
317 			return;
318 #endif
319 		}
320 #endif
321 		if ( ( flags & CVAR_CHEAT ) && !cvarSystem->GetCVarBool( "net_allowCheats" ) ) {
322 			common->Printf( "%s cannot be changed in multiplayer.\n", nameString.c_str() );
323 #if ID_ALLOW_CHEATS
324 			common->Printf( "ID_ALLOW_CHEATS override!\n" );
325 #else
326 			return;
327 #endif
328 		}
329 	}
330 
331 	if ( !newValue ) {
332 		newValue = resetString.c_str();
333 	}
334 
335 	if ( !force ) {
336 		if ( flags & CVAR_ROM ) {
337 			common->Printf( "%s is read only.\n", nameString.c_str() );
338 			return;
339 		}
340 
341 		if ( flags & CVAR_INIT ) {
342 			common->Printf( "%s is write protected.\n", nameString.c_str() );
343 			return;
344 		}
345 	}
346 
347 	if ( valueString.Icmp( newValue ) == 0 ) {
348 		return;
349 	}
350 
351 	valueString = newValue;
352 	value = valueString.c_str();
353 	UpdateValue();
354 
355 	SetModified();
356 	cvarSystem->SetModifiedFlags( flags );
357 }
358 
359 /*
360 ============
361 idInternalCVar::Reset
362 ============
363 */
Reset(void)364 void idInternalCVar::Reset( void ) {
365 	valueString = resetString;
366 	value = valueString.c_str();
367 	UpdateValue();
368 }
369 
370 /*
371 ============
372 idInternalCVar::InternalSetString
373 ============
374 */
InternalSetString(const char * newValue)375 void idInternalCVar::InternalSetString( const char *newValue ) {
376 	Set( newValue, true, false );
377 }
378 
379 /*
380 ===============
381 idInternalCVar::InternalServerSetString
382 ===============
383 */
InternalServerSetString(const char * newValue)384 void idInternalCVar::InternalServerSetString( const char *newValue ) {
385 	Set( newValue, true, true );
386 }
387 
388 /*
389 ============
390 idInternalCVar::InternalSetBool
391 ============
392 */
InternalSetBool(const bool newValue)393 void idInternalCVar::InternalSetBool( const bool newValue ) {
394 	Set( idStr( newValue ), true, false );
395 }
396 
397 /*
398 ============
399 idInternalCVar::InternalSetInteger
400 ============
401 */
InternalSetInteger(const int newValue)402 void idInternalCVar::InternalSetInteger( const int newValue ) {
403 	Set( idStr( newValue ), true, false );
404 }
405 
406 /*
407 ============
408 idInternalCVar::InternalSetFloat
409 ============
410 */
InternalSetFloat(const float newValue)411 void idInternalCVar::InternalSetFloat( const float newValue ) {
412 	Set( idStr( newValue ), true, false );
413 }
414 
415 
416 /*
417 ===============================================================================
418 
419 	idCVarSystemLocal
420 
421 ===============================================================================
422 */
423 
424 class idCVarSystemLocal : public idCVarSystem {
425 public:
426 							idCVarSystemLocal( void );
427 
~idCVarSystemLocal(void)428 	virtual					~idCVarSystemLocal( void ) {}
429 
430 	virtual void			Init( void );
431 	virtual void			Shutdown( void );
432 	virtual bool			IsInitialized( void ) const;
433 
434 	virtual void			Register( idCVar *cvar );
435 
436 	virtual idCVar *		Find( const char *name );
437 
438 	virtual void			SetCVarString( const char *name, const char *value, int flags = 0 );
439 	virtual void			SetCVarBool( const char *name, const bool value, int flags = 0 );
440 	virtual void			SetCVarInteger( const char *name, const int value, int flags = 0 );
441 	virtual void			SetCVarFloat( const char *name, const float value, int flags = 0 );
442 
443 	virtual const char *	GetCVarString( const char *name ) const;
444 	virtual bool			GetCVarBool( const char *name ) const;
445 	virtual int				GetCVarInteger( const char *name ) const;
446 	virtual float			GetCVarFloat( const char *name ) const;
447 
448 	virtual bool			Command( const idCmdArgs &args );
449 
450 	virtual void			CommandCompletion( void(*callback)( const char *s ) );
451 	virtual void			ArgCompletion( const char *cmdString, void(*callback)( const char *s ) );
452 
453 	virtual void			SetModifiedFlags( int flags );
454 	virtual int				GetModifiedFlags( void ) const;
455 	virtual void			ClearModifiedFlags( int flags );
456 
457 	virtual void			ResetFlaggedVariables( int flags );
458 	virtual void			RemoveFlaggedAutoCompletion( int flags );
459 	virtual void			WriteFlaggedVariables( int flags, const char *setCmd, idFile *f ) const;
460 
461 	virtual const idDict *	MoveCVarsToDict( int flags ) const;
462 	virtual void			SetCVarsFromDict( const idDict &dict );
463 
464 	void					RegisterInternal( idCVar *cvar );
465 	idInternalCVar *		FindInternal( const char *name ) const;
466 	void					SetInternal( const char *name, const char *value, int flags );
467 
468 private:
469 	bool					initialized;
470 	idList<idInternalCVar*>	cvars;
471 	idHashIndex				cvarHash;
472 	int						modifiedFlags;
473 							// use a static dictionary to MoveCVarsToDict can be used from game
474 	static idDict			moveCVarsToDict;
475 
476 
477 private:
478 	static void				Toggle_f( const idCmdArgs &args );
479 	static void				Set_f( const idCmdArgs &args );
480 	static void				SetS_f( const idCmdArgs &args );
481 	static void				SetU_f( const idCmdArgs &args );
482 	static void				SetT_f( const idCmdArgs &args );
483 	static void				SetA_f( const idCmdArgs &args );
484 	static void				Reset_f( const idCmdArgs &args );
485 	static void				ListByFlags( const idCmdArgs &args, cvarFlags_t flags );
486 	static void				List_f( const idCmdArgs &args );
487 	static void				Restart_f( const idCmdArgs &args );
488 };
489 
490 idCVarSystemLocal			localCVarSystem;
491 idCVarSystem *				cvarSystem = &localCVarSystem;
492 
493 idDict						idCVarSystemLocal::moveCVarsToDict;
494 
495 #define NUM_COLUMNS				77		// 78 - 1
496 #define NUM_NAME_CHARS			33
497 #define NUM_DESCRIPTION_CHARS	( NUM_COLUMNS - NUM_NAME_CHARS )
498 #define FORMAT_STRING			"%-32s "
499 
CreateColumn(const char * text,int columnWidth,const char * indent,idStr & string)500 const char *CreateColumn( const char *text, int columnWidth, const char *indent, idStr &string ) {
501 	int i, lastLine;
502 
503 	string.Clear();
504 	for ( lastLine = i = 0; text[i] != '\0'; i++ ) {
505 		if ( i - lastLine >= columnWidth || text[i] == '\n' ) {
506 			while( i > 0 && text[i] > ' ' && text[i] != '/' && text[i] != ',' && text[i] != '\\' ) {
507 				i--;
508 			}
509 			while( lastLine < i ) {
510 				string.Append( text[lastLine++] );
511 			}
512 			string.Append( indent );
513 			lastLine++;
514 		}
515 	}
516 	while( lastLine < i ) {
517 		string.Append( text[lastLine++] );
518 	}
519 	return string.c_str();
520 }
521 
522 /*
523 ============
524 idCVarSystemLocal::FindInternal
525 ============
526 */
FindInternal(const char * name) const527 idInternalCVar *idCVarSystemLocal::FindInternal( const char *name ) const {
528 	int hash = cvarHash.GenerateKey( name, false );
529 	for ( int i = cvarHash.First( hash ); i != -1; i = cvarHash.Next( i ) ) {
530 		if ( cvars[i]->nameString.Icmp( name ) == 0 ) {
531 			return cvars[i];
532 		}
533 	}
534 	return NULL;
535 }
536 
537 /*
538 ============
539 idCVarSystemLocal::SetInternal
540 ============
541 */
SetInternal(const char * name,const char * value,int flags)542 void idCVarSystemLocal::SetInternal( const char *name, const char *value, int flags ) {
543 	int hash;
544 	idInternalCVar *internal;
545 
546 	internal = FindInternal( name );
547 
548 	if ( internal ) {
549 		internal->InternalSetString( value );
550 		internal->flags |= flags & ~CVAR_STATIC;
551 		internal->UpdateCheat();
552 	} else {
553 		internal = new idInternalCVar( name, value, flags );
554 		hash = cvarHash.GenerateKey( internal->nameString.c_str(), false );
555 		cvarHash.Add( hash, cvars.Append( internal ) );
556 	}
557 }
558 
559 /*
560 ============
561 idCVarSystemLocal::idCVarSystemLocal
562 ============
563 */
idCVarSystemLocal(void)564 idCVarSystemLocal::idCVarSystemLocal( void ) {
565 	initialized = false;
566 	modifiedFlags = 0;
567 }
568 
569 /*
570 ============
571 idCVarSystemLocal::Init
572 ============
573 */
Init(void)574 void idCVarSystemLocal::Init( void ) {
575 
576 	modifiedFlags = 0;
577 
578 	cmdSystem->AddCommand( "toggle", Toggle_f, CMD_FL_SYSTEM, "toggles a cvar" );
579 	cmdSystem->AddCommand( "set", Set_f, CMD_FL_SYSTEM, "sets a cvar" );
580 	cmdSystem->AddCommand( "sets", SetS_f, CMD_FL_SYSTEM, "sets a cvar and flags it as server info" );
581 	cmdSystem->AddCommand( "setu", SetU_f, CMD_FL_SYSTEM, "sets a cvar and flags it as user info" );
582 	cmdSystem->AddCommand( "sett", SetT_f, CMD_FL_SYSTEM, "sets a cvar and flags it as tool" );
583 	cmdSystem->AddCommand( "seta", SetA_f, CMD_FL_SYSTEM, "sets a cvar and flags it as archive" );
584 	cmdSystem->AddCommand( "reset", Reset_f, CMD_FL_SYSTEM, "resets a cvar" );
585 	cmdSystem->AddCommand( "listCvars", List_f, CMD_FL_SYSTEM, "lists cvars" );
586 	cmdSystem->AddCommand( "cvar_restart", Restart_f, CMD_FL_SYSTEM, "restart the cvar system" );
587 
588 	initialized = true;
589 }
590 
591 /*
592 ============
593 idCVarSystemLocal::Shutdown
594 ============
595 */
Shutdown(void)596 void idCVarSystemLocal::Shutdown( void ) {
597 	cvars.DeleteContents( true );
598 	cvarHash.Free();
599 	moveCVarsToDict.Clear();
600 	initialized = false;
601 }
602 
603 /*
604 ============
605 idCVarSystemLocal::IsInitialized
606 ============
607 */
IsInitialized(void) const608 bool idCVarSystemLocal::IsInitialized( void ) const {
609 	return initialized;
610 }
611 
612 /*
613 ============
614 idCVarSystemLocal::Register
615 ============
616 */
Register(idCVar * cvar)617 void idCVarSystemLocal::Register( idCVar *cvar ) {
618 	int hash;
619 	idInternalCVar *internal;
620 
621 	cvar->SetInternalVar( cvar );
622 
623 	internal = FindInternal( cvar->GetName() );
624 
625 	if ( internal ) {
626 		internal->Update( cvar );
627 	} else {
628 		internal = new idInternalCVar( cvar );
629 		hash = cvarHash.GenerateKey( internal->nameString.c_str(), false );
630 		cvarHash.Add( hash, cvars.Append( internal ) );
631 	}
632 
633 	cvar->SetInternalVar( internal );
634 }
635 
636 /*
637 ============
638 idCVarSystemLocal::Find
639 ============
640 */
Find(const char * name)641 idCVar *idCVarSystemLocal::Find( const char *name ) {
642 	return FindInternal( name );
643 }
644 
645 /*
646 ============
647 idCVarSystemLocal::SetCVarString
648 ============
649 */
SetCVarString(const char * name,const char * value,int flags)650 void idCVarSystemLocal::SetCVarString( const char *name, const char *value, int flags ) {
651 	SetInternal( name, value, flags );
652 }
653 
654 /*
655 ============
656 idCVarSystemLocal::SetCVarBool
657 ============
658 */
SetCVarBool(const char * name,const bool value,int flags)659 void idCVarSystemLocal::SetCVarBool( const char *name, const bool value, int flags ) {
660 	SetInternal( name, idStr( value ), flags );
661 }
662 
663 /*
664 ============
665 idCVarSystemLocal::SetCVarInteger
666 ============
667 */
SetCVarInteger(const char * name,const int value,int flags)668 void idCVarSystemLocal::SetCVarInteger( const char *name, const int value, int flags ) {
669 	SetInternal( name, idStr( value ), flags );
670 }
671 
672 /*
673 ============
674 idCVarSystemLocal::SetCVarFloat
675 ============
676 */
SetCVarFloat(const char * name,const float value,int flags)677 void idCVarSystemLocal::SetCVarFloat( const char *name, const float value, int flags ) {
678 	SetInternal( name, idStr( value ), flags );
679 }
680 
681 /*
682 ============
683 idCVarSystemLocal::GetCVarString
684 ============
685 */
GetCVarString(const char * name) const686 const char *idCVarSystemLocal::GetCVarString( const char *name ) const {
687 	idInternalCVar *internal = FindInternal( name );
688 	if ( internal ) {
689 		return internal->GetString();
690 	}
691 	return "";
692 }
693 
694 /*
695 ============
696 idCVarSystemLocal::GetCVarBool
697 ============
698 */
GetCVarBool(const char * name) const699 bool idCVarSystemLocal::GetCVarBool( const char *name ) const {
700 	idInternalCVar *internal = FindInternal( name );
701 	if ( internal ) {
702 		return internal->GetBool();
703 	}
704 	return false;
705 }
706 
707 /*
708 ============
709 idCVarSystemLocal::GetCVarInteger
710 ============
711 */
GetCVarInteger(const char * name) const712 int idCVarSystemLocal::GetCVarInteger( const char *name ) const {
713 	idInternalCVar *internal = FindInternal( name );
714 	if ( internal ) {
715 		return internal->GetInteger();
716 	}
717 	return 0;
718 }
719 
720 /*
721 ============
722 idCVarSystemLocal::GetCVarFloat
723 ============
724 */
GetCVarFloat(const char * name) const725 float idCVarSystemLocal::GetCVarFloat( const char *name ) const {
726 	idInternalCVar *internal = FindInternal( name );
727 	if ( internal ) {
728 		return internal->GetFloat();
729 	}
730 	return 0.0f;
731 }
732 
733 /*
734 ============
735 idCVarSystemLocal::Command
736 ============
737 */
Command(const idCmdArgs & args)738 bool idCVarSystemLocal::Command( const idCmdArgs &args ) {
739 	idInternalCVar *internal;
740 
741 	internal = FindInternal( args.Argv( 0 ) );
742 
743 	if ( internal == NULL ) {
744 		return false;
745 	}
746 
747 	if ( args.Argc() == 1 ) {
748 		// print the variable
749 		common->Printf( "\"%s\" is:\"%s\"" S_COLOR_WHITE " default:\"%s\"\n",
750 					internal->nameString.c_str(), internal->valueString.c_str(), internal->resetString.c_str() );
751 		if ( idStr::Length( internal->GetDescription() ) > 0 ) {
752 			common->Printf( S_COLOR_WHITE "%s\n", internal->GetDescription() );
753 		}
754 	} else {
755 		// set the value
756 		internal->Set( args.Args(), false, false );
757 	}
758 	return true;
759 }
760 
761 /*
762 ============
763 idCVarSystemLocal::CommandCompletion
764 ============
765 */
CommandCompletion(void (* callback)(const char * s))766 void idCVarSystemLocal::CommandCompletion( void(*callback)( const char *s ) ) {
767 	for( int i = 0; i < cvars.Num(); i++ ) {
768 		callback( cvars[i]->GetName() );
769 	}
770 }
771 
772 /*
773 ============
774 idCVarSystemLocal::ArgCompletion
775 ============
776 */
ArgCompletion(const char * cmdString,void (* callback)(const char * s))777 void idCVarSystemLocal::ArgCompletion( const char *cmdString, void(*callback)( const char *s ) ) {
778 	idCmdArgs args;
779 
780 	args.TokenizeString( cmdString, false );
781 
782 	for( int i = 0; i < cvars.Num(); i++ ) {
783 		if ( !cvars[i]->valueCompletion ) {
784 			continue;
785 		}
786 		if ( idStr::Icmp( args.Argv( 0 ), cvars[i]->nameString.c_str() ) == 0 ) {
787 			cvars[i]->valueCompletion( args, callback );
788 			break;
789 		}
790 	}
791 }
792 
793 /*
794 ============
795 idCVarSystemLocal::SetModifiedFlags
796 ============
797 */
SetModifiedFlags(int flags)798 void idCVarSystemLocal::SetModifiedFlags( int flags ) {
799 	modifiedFlags |= flags;
800 }
801 
802 /*
803 ============
804 idCVarSystemLocal::GetModifiedFlags
805 ============
806 */
GetModifiedFlags(void) const807 int idCVarSystemLocal::GetModifiedFlags( void ) const {
808 	return modifiedFlags;
809 }
810 
811 /*
812 ============
813 idCVarSystemLocal::ClearModifiedFlags
814 ============
815 */
ClearModifiedFlags(int flags)816 void idCVarSystemLocal::ClearModifiedFlags( int flags ) {
817 	modifiedFlags &= ~flags;
818 }
819 
820 /*
821 ============
822 idCVarSystemLocal::ResetFlaggedVariables
823 ============
824 */
ResetFlaggedVariables(int flags)825 void idCVarSystemLocal::ResetFlaggedVariables( int flags ) {
826 	for( int i = 0; i < cvars.Num(); i++ ) {
827 		idInternalCVar *cvar = cvars[i];
828 		if ( cvar->GetFlags() & flags ) {
829 			cvar->Set( NULL, true, true );
830 		}
831 	}
832 }
833 
834 /*
835 ============
836 idCVarSystemLocal::RemoveFlaggedAutoCompletion
837 ============
838 */
RemoveFlaggedAutoCompletion(int flags)839 void idCVarSystemLocal::RemoveFlaggedAutoCompletion( int flags ) {
840 	for( int i = 0; i < cvars.Num(); i++ ) {
841 		idInternalCVar *cvar = cvars[i];
842 		if ( cvar->GetFlags() & flags ) {
843 			cvar->valueCompletion = NULL;
844 		}
845 	}
846 }
847 
848 /*
849 ============
850 idCVarSystemLocal::WriteFlaggedVariables
851 
852 Appends lines containing "set variable value" for all variables
853 with the "flags" flag set to true.
854 ============
855 */
WriteFlaggedVariables(int flags,const char * setCmd,idFile * f) const856 void idCVarSystemLocal::WriteFlaggedVariables( int flags, const char *setCmd, idFile *f ) const {
857 	for( int i = 0; i < cvars.Num(); i++ ) {
858 		idInternalCVar *cvar = cvars[i];
859 		if ( cvar->GetFlags() & flags ) {
860 			f->Printf( "%s %s \"%s\"\n", setCmd, cvar->GetName(), cvar->GetString() );
861 		}
862 	}
863 }
864 
865 /*
866 ============
867 idCVarSystemLocal::MoveCVarsToDict
868 ============
869 */
MoveCVarsToDict(int flags) const870 const idDict* idCVarSystemLocal::MoveCVarsToDict( int flags ) const {
871 	moveCVarsToDict.Clear();
872 	for( int i = 0; i < cvars.Num(); i++ ) {
873 		idCVar *cvar = cvars[i];
874 		if ( cvar->GetFlags() & flags ) {
875 			moveCVarsToDict.Set( cvar->GetName(), cvar->GetString() );
876 		}
877 	}
878 	return &moveCVarsToDict;
879 }
880 
881 /*
882 ============
883 idCVarSystemLocal::SetCVarsFromDict
884 ============
885 */
SetCVarsFromDict(const idDict & dict)886 void idCVarSystemLocal::SetCVarsFromDict( const idDict &dict ) {
887 	idInternalCVar *internal;
888 
889 	for( int i = 0; i < dict.GetNumKeyVals(); i++ ) {
890 		const idKeyValue *kv = dict.GetKeyVal( i );
891 		internal = FindInternal( kv->GetKey() );
892 		if ( internal ) {
893 			internal->InternalServerSetString( kv->GetValue() );
894 		}
895 	}
896 }
897 
898 /*
899 ============
900 idCVarSystemLocal::Toggle_f
901 ============
902 */
Toggle_f(const idCmdArgs & args)903 void idCVarSystemLocal::Toggle_f( const idCmdArgs &args ) {
904 	int argc, i;
905 	float current, set;
906 	const char *text;
907 
908 	argc = args.Argc();
909 	if ( argc < 2 ) {
910 		common->Printf ("usage:\n"
911 			"   toggle <variable>  - toggles between 0 and 1\n"
912 			"   toggle <variable> <value> - toggles between 0 and <value>\n"
913 			"   toggle <variable> [string 1] [string 2]...[string n] - cycles through all strings\n");
914 		return;
915 	}
916 
917 	idInternalCVar *cvar = localCVarSystem.FindInternal( args.Argv( 1 ) );
918 
919 	if ( cvar == NULL ) {
920 		common->Warning( "Toggle_f: cvar \"%s\" not found", args.Argv( 1 ) );
921 		return;
922 	}
923 
924 	if ( argc > 3 ) {
925 		// cycle through multiple values
926 		text = cvar->GetString();
927 		for( i = 2; i < argc; i++ ) {
928 			if ( !idStr::Icmp( text, args.Argv( i ) ) ) {
929 				// point to next value
930 				i++;
931 				break;
932 			}
933 		}
934 		if ( i >= argc ) {
935 			i = 2;
936 		}
937 
938 		common->Printf( "set %s = %s\n", args.Argv(1), args.Argv( i ) );
939 		cvar->Set( va("%s", args.Argv( i ) ), false, false );
940 	} else {
941 		// toggle between 0 and 1
942 		current = cvar->GetFloat();
943 		if ( argc == 3 ) {
944 			set = atof( args.Argv( 2 ) );
945 		} else {
946 			set = 1.0f;
947 		}
948 		if ( current == 0.0f ) {
949 			current = set;
950 		} else {
951 			current = 0.0f;
952 		}
953 		common->Printf( "set %s = %f\n", args.Argv(1), current );
954 		cvar->Set( idStr( current ), false, false );
955 	}
956 }
957 
958 /*
959 ============
960 idCVarSystemLocal::Set_f
961 ============
962 */
Set_f(const idCmdArgs & args)963 void idCVarSystemLocal::Set_f( const idCmdArgs &args ) {
964 	const char *str;
965 
966 	str = args.Args( 2, args.Argc() - 1 );
967 	localCVarSystem.SetCVarString( args.Argv(1), str );
968 }
969 
970 /*
971 ============
972 idCVarSystemLocal::SetS_f
973 ============
974 */
SetS_f(const idCmdArgs & args)975 void idCVarSystemLocal::SetS_f( const idCmdArgs &args ) {
976 	idInternalCVar *cvar;
977 
978 	Set_f( args );
979 	cvar = localCVarSystem.FindInternal( args.Argv( 1 ) );
980 	if ( !cvar ) {
981 		return;
982 	}
983 	cvar->flags |= CVAR_SERVERINFO | CVAR_ARCHIVE;
984 }
985 
986 /*
987 ============
988 idCVarSystemLocal::SetU_f
989 ============
990 */
SetU_f(const idCmdArgs & args)991 void idCVarSystemLocal::SetU_f( const idCmdArgs &args ) {
992 	idInternalCVar *cvar;
993 
994 	Set_f( args );
995 	cvar = localCVarSystem.FindInternal( args.Argv( 1 ) );
996 	if ( !cvar ) {
997 		return;
998 	}
999 	cvar->flags |= CVAR_USERINFO | CVAR_ARCHIVE;
1000 }
1001 
1002 /*
1003 ============
1004 idCVarSystemLocal::SetT_f
1005 ============
1006 */
SetT_f(const idCmdArgs & args)1007 void idCVarSystemLocal::SetT_f( const idCmdArgs &args ) {
1008 	idInternalCVar *cvar;
1009 
1010 	Set_f( args );
1011 	cvar = localCVarSystem.FindInternal( args.Argv( 1 ) );
1012 	if ( !cvar ) {
1013 		return;
1014 	}
1015 	cvar->flags |= CVAR_TOOL;
1016 }
1017 
1018 /*
1019 ============
1020 idCVarSystemLocal::SetA_f
1021 ============
1022 */
SetA_f(const idCmdArgs & args)1023 void idCVarSystemLocal::SetA_f( const idCmdArgs &args ) {
1024 	idInternalCVar *cvar;
1025 
1026 	Set_f( args );
1027 	cvar = localCVarSystem.FindInternal( args.Argv( 1 ) );
1028 	if ( !cvar ) {
1029 		return;
1030 	}
1031 
1032 	// FIXME: enable this for ship, so mods can store extra data
1033 	// but during development we don't want obsolete cvars to continue
1034 	// to be saved
1035 //	cvar->flags |= CVAR_ARCHIVE;
1036 }
1037 
1038 /*
1039 ============
1040 idCVarSystemLocal::Reset_f
1041 ============
1042 */
Reset_f(const idCmdArgs & args)1043 void idCVarSystemLocal::Reset_f( const idCmdArgs &args ) {
1044 	idInternalCVar *cvar;
1045 
1046 	if ( args.Argc() != 2 ) {
1047 		common->Printf ("usage: reset <variable>\n");
1048 		return;
1049 	}
1050 	cvar = localCVarSystem.FindInternal( args.Argv( 1 ) );
1051 	if ( !cvar ) {
1052 		return;
1053 	}
1054 
1055 	cvar->Reset();
1056 }
1057 
1058 /*
1059 ============
1060 idCVarSystemLocal::ListByFlags
1061 ============
1062 */
1063 // NOTE: the const wonkyness is required to make msvc happy
1064 template<>
idListSortCompare(const idInternalCVar * const * a,const idInternalCVar * const * b)1065 ID_INLINE int idListSortCompare( const idInternalCVar * const *a, const idInternalCVar * const *b ) {
1066 	return idStr::Icmp( (*a)->GetName(), (*b)->GetName() );
1067 }
1068 
ListByFlags(const idCmdArgs & args,cvarFlags_t flags)1069 void idCVarSystemLocal::ListByFlags( const idCmdArgs &args, cvarFlags_t flags ) {
1070 	int i, argNum;
1071 	idStr match, indent, string;
1072 	const idInternalCVar *cvar;
1073 	idList<const idInternalCVar *>cvarList;
1074 
1075 	enum {
1076 		SHOW_VALUE,
1077 		SHOW_DESCRIPTION,
1078 		SHOW_TYPE,
1079 		SHOW_FLAGS
1080 	} show;
1081 
1082 	argNum = 1;
1083 	show = SHOW_VALUE;
1084 
1085 	if ( idStr::Icmp( args.Argv( argNum ), "-" ) == 0 || idStr::Icmp( args.Argv( argNum ), "/" ) == 0 ) {
1086 		if ( idStr::Icmp( args.Argv( argNum + 1 ), "help" ) == 0 || idStr::Icmp( args.Argv( argNum + 1 ), "?" ) == 0 ) {
1087 			argNum = 3;
1088 			show = SHOW_DESCRIPTION;
1089 		} else if ( idStr::Icmp( args.Argv( argNum + 1 ), "type" ) == 0 || idStr::Icmp( args.Argv( argNum + 1 ), "range" ) == 0 ) {
1090 			argNum = 3;
1091 			show = SHOW_TYPE;
1092 		} else if ( idStr::Icmp( args.Argv( argNum + 1 ), "flags" ) == 0 ) {
1093 			argNum = 3;
1094 			show = SHOW_FLAGS;
1095 		}
1096 	}
1097 
1098 	if ( args.Argc() > argNum ) {
1099 		match = args.Args( argNum, -1 );
1100 		match.Replace( " ", "" );
1101 	} else {
1102 		match = "";
1103 	}
1104 
1105 	for ( i = 0; i < localCVarSystem.cvars.Num(); i++ ) {
1106 		cvar = localCVarSystem.cvars[i];
1107 
1108 		if ( !( cvar->GetFlags() & flags ) ) {
1109 			continue;
1110 		}
1111 
1112 		if ( match.Length() && !cvar->nameString.Filter( match, false ) ) {
1113 			continue;
1114 		}
1115 
1116 		cvarList.Append( cvar );
1117 	}
1118 
1119 	cvarList.Sort();
1120 
1121 	switch( show ) {
1122 		case SHOW_VALUE: {
1123 			for ( i = 0; i < cvarList.Num(); i++ ) {
1124 				cvar = cvarList[i];
1125 				common->Printf( FORMAT_STRING S_COLOR_WHITE "\"%s\"\n", cvar->nameString.c_str(), cvar->valueString.c_str() );
1126 			}
1127 			break;
1128 		}
1129 		case SHOW_DESCRIPTION: {
1130 			indent.Fill( ' ', NUM_NAME_CHARS );
1131 			indent.Insert( "\n", 0 );
1132 
1133 			for ( i = 0; i < cvarList.Num(); i++ ) {
1134 				cvar = cvarList[i];
1135 				common->Printf( FORMAT_STRING S_COLOR_WHITE "%s\n", cvar->nameString.c_str(), CreateColumn( cvar->GetDescription(), NUM_DESCRIPTION_CHARS, indent, string ) );
1136 			}
1137 			break;
1138 		}
1139 		case SHOW_TYPE: {
1140 			for ( i = 0; i < cvarList.Num(); i++ ) {
1141 				cvar = cvarList[i];
1142 				if ( cvar->GetFlags() & CVAR_BOOL ) {
1143 					common->Printf( FORMAT_STRING S_COLOR_CYAN "bool\n", cvar->GetName() );
1144 				} else if ( cvar->GetFlags() & CVAR_INTEGER ) {
1145 					if ( cvar->GetMinValue() < cvar->GetMaxValue() ) {
1146 						common->Printf( FORMAT_STRING S_COLOR_GREEN "int " S_COLOR_WHITE "[%d, %d]\n", cvar->GetName(), (int) cvar->GetMinValue(), (int) cvar->GetMaxValue() );
1147 					} else {
1148 						common->Printf( FORMAT_STRING S_COLOR_GREEN "int\n", cvar->GetName() );
1149 					}
1150 				} else if ( cvar->GetFlags() & CVAR_FLOAT ) {
1151 					if ( cvar->GetMinValue() < cvar->GetMaxValue() ) {
1152 						common->Printf( FORMAT_STRING S_COLOR_RED "float " S_COLOR_WHITE "[%s, %s]\n", cvar->GetName(), idStr( cvar->GetMinValue() ).c_str(), idStr( cvar->GetMaxValue() ).c_str() );
1153 					} else {
1154 						common->Printf( FORMAT_STRING S_COLOR_RED "float\n", cvar->GetName() );
1155 					}
1156 				} else if ( cvar->GetValueStrings() ) {
1157 					common->Printf( FORMAT_STRING S_COLOR_WHITE "string " S_COLOR_WHITE "[", cvar->GetName() );
1158 					for ( int j = 0; cvar->GetValueStrings()[j] != NULL; j++ ) {
1159 						if ( j ) {
1160 							common->Printf( S_COLOR_WHITE ", %s", cvar->GetValueStrings()[j] );
1161 						} else {
1162 							common->Printf( S_COLOR_WHITE "%s", cvar->GetValueStrings()[j] );
1163 						}
1164 					}
1165 					common->Printf( S_COLOR_WHITE "]\n" );
1166 				} else {
1167 					common->Printf( FORMAT_STRING S_COLOR_WHITE "string\n", cvar->GetName() );
1168 				}
1169 			}
1170 			break;
1171 		}
1172 		case SHOW_FLAGS: {
1173 			for ( i = 0; i < cvarList.Num(); i++ ) {
1174 				cvar = cvarList[i];
1175 				common->Printf( FORMAT_STRING, cvar->GetName() );
1176 				string = "";
1177 				if ( cvar->GetFlags() & CVAR_BOOL ) {
1178 					string += S_COLOR_CYAN "B ";
1179 				} else if ( cvar->GetFlags() & CVAR_INTEGER ) {
1180 					string += S_COLOR_GREEN "I ";
1181 				} else if ( cvar->GetFlags() & CVAR_FLOAT ) {
1182 					string += S_COLOR_RED "F ";
1183 				} else {
1184 					string += S_COLOR_WHITE "S ";
1185 				}
1186 				if ( cvar->GetFlags() & CVAR_SYSTEM ) {
1187 					string += S_COLOR_WHITE "SYS  ";
1188 				} else if ( cvar->GetFlags() & CVAR_RENDERER ) {
1189 					string += S_COLOR_WHITE "RNDR ";
1190 				} else if ( cvar->GetFlags() & CVAR_SOUND ) {
1191 					string += S_COLOR_WHITE "SND  ";
1192 				} else if ( cvar->GetFlags() & CVAR_GUI ) {
1193 					string += S_COLOR_WHITE "GUI  ";
1194 				} else if ( cvar->GetFlags() & CVAR_GAME ) {
1195 					string += S_COLOR_WHITE "GAME ";
1196 				} else if ( cvar->GetFlags() & CVAR_TOOL ) {
1197 					string += S_COLOR_WHITE "TOOL ";
1198 				} else {
1199 					string += S_COLOR_WHITE "     ";
1200 				}
1201 				string += ( cvar->GetFlags() & CVAR_USERINFO ) ?	"UI "	: "   ";
1202 				string += ( cvar->GetFlags() & CVAR_SERVERINFO ) ?	"SI "	: "   ";
1203 				string += ( cvar->GetFlags() & CVAR_STATIC ) ?		"ST "	: "   ";
1204 				string += ( cvar->GetFlags() & CVAR_CHEAT ) ?		"CH "	: "   ";
1205 				string += ( cvar->GetFlags() & CVAR_INIT ) ?		"IN "	: "   ";
1206 				string += ( cvar->GetFlags() & CVAR_ROM ) ?			"RO "	: "   ";
1207 				string += ( cvar->GetFlags() & CVAR_ARCHIVE ) ?		"AR "	: "   ";
1208 				string += ( cvar->GetFlags() & CVAR_MODIFIED ) ?	"MO "	: "   ";
1209 				string += "\n";
1210 				common->Printf( string );
1211 			}
1212 			break;
1213 		}
1214 	}
1215 
1216 	common->Printf( "\n%i cvars listed\n\n", cvarList.Num() );
1217 	common->Printf(	"listCvar [search string]          = list cvar values\n"
1218 				"listCvar -help [search string]    = list cvar descriptions\n"
1219 				"listCvar -type [search string]    = list cvar types\n"
1220 				"listCvar -flags [search string]   = list cvar flags\n" );
1221 }
1222 
1223 /*
1224 ============
1225 idCVarSystemLocal::List_f
1226 ============
1227 */
List_f(const idCmdArgs & args)1228 void idCVarSystemLocal::List_f( const idCmdArgs &args ) {
1229 	ListByFlags( args, CVAR_ALL );
1230 }
1231 
1232 /*
1233 ============
1234 idCVarSystemLocal::Restart_f
1235 ============
1236 */
Restart_f(const idCmdArgs & args)1237 void idCVarSystemLocal::Restart_f( const idCmdArgs &args ) {
1238 	int i, hash;
1239 	idInternalCVar *cvar;
1240 
1241 	for ( i = 0; i < localCVarSystem.cvars.Num(); i++ ) {
1242 		cvar = localCVarSystem.cvars[i];
1243 
1244 		// don't mess with rom values
1245 		if ( cvar->flags & ( CVAR_ROM | CVAR_INIT ) ) {
1246 			continue;
1247 		}
1248 
1249 		// throw out any variables the user created
1250 		if ( !( cvar->flags & CVAR_STATIC ) ) {
1251 			hash = localCVarSystem.cvarHash.GenerateKey( cvar->nameString, false );
1252 			delete cvar;
1253 			localCVarSystem.cvars.RemoveIndex( i );
1254 			localCVarSystem.cvarHash.RemoveIndex( hash, i );
1255 			i--;
1256 			continue;
1257 		}
1258 
1259 		cvar->Reset();
1260 	}
1261 }
1262