1 /*
2 ** c_cvars.cpp
3 ** Defines all the different console variable types
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 1998-2006 Randy Heit
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 **    notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 **    notice, this list of conditions and the following disclaimer in the
17 **    documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 **    derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 */
34 
35 #include <string.h>
36 #include <stdio.h>
37 #include <assert.h>
38 
39 #include "cmdlib.h"
40 #include "configfile.h"
41 #include "c_console.h"
42 #include "c_dispatch.h"
43 
44 #include "doomstat.h"
45 #include "c_cvars.h"
46 #include "d_player.h"
47 
48 #include "d_netinf.h"
49 
50 #include "i_system.h"
51 #include "v_palette.h"
52 #include "v_video.h"
53 #include "colormatcher.h"
54 
55 struct FLatchedValue
56 {
57 	FBaseCVar *Variable;
58 	UCVarValue Value;
59 	ECVarType Type;
60 };
61 
62 static TArray<FLatchedValue> LatchedValues;
63 
64 bool FBaseCVar::m_DoNoSet = false;
65 bool FBaseCVar::m_UseCallback = false;
66 
67 FBaseCVar *CVars = NULL;
68 
69 int cvar_defflags;
70 
FBaseCVar(const FBaseCVar & var)71 FBaseCVar::FBaseCVar (const FBaseCVar &var)
72 {
73 	I_FatalError ("Use of cvar copy constructor");
74 }
75 
FBaseCVar(const char * var_name,DWORD flags,void (* callback)(FBaseCVar &))76 FBaseCVar::FBaseCVar (const char *var_name, DWORD flags, void (*callback)(FBaseCVar &))
77 {
78 	FBaseCVar *var;
79 
80 	var = FindCVar (var_name, NULL);
81 
82 	m_Callback = callback;
83 	Flags = 0;
84 	Name = NULL;
85 
86 	if (var_name)
87 	{
88 		C_AddTabCommand (var_name);
89 		Name = copystring (var_name);
90 		m_Next = CVars;
91 		CVars = this;
92 	}
93 
94 	if (var)
95 	{
96 		ECVarType type;
97 		UCVarValue value;
98 
99 		value = var->GetFavoriteRep (&type);
100 		ForceSet (value, type);
101 
102 		if (var->Flags & CVAR_AUTO)
103 			delete var;
104 		else
105 			var->~FBaseCVar();
106 
107 		Flags = flags;
108 	}
109 	else
110 	{
111 		Flags = flags | CVAR_ISDEFAULT;
112 	}
113 }
114 
~FBaseCVar()115 FBaseCVar::~FBaseCVar ()
116 {
117 	if (Name)
118 	{
119 		FBaseCVar *var, *prev;
120 
121 		var = FindCVar (Name, &prev);
122 
123 		if (var == this)
124 		{
125 			if (prev)
126 				prev->m_Next = m_Next;
127 			else
128 				CVars = m_Next;
129 		}
130 		C_RemoveTabCommand(Name);
131 		delete[] Name;
132 	}
133 }
134 
ForceSet(UCVarValue value,ECVarType type,bool nouserinfosend)135 void FBaseCVar::ForceSet (UCVarValue value, ECVarType type, bool nouserinfosend)
136 {
137 	DoSet (value, type);
138 	if ((Flags & CVAR_USERINFO) && !nouserinfosend && !(Flags & CVAR_IGNORE))
139 		D_UserInfoChanged (this);
140 	if (m_UseCallback)
141 		Callback ();
142 
143 	Flags &= ~CVAR_ISDEFAULT;
144 }
145 
SetGenericRep(UCVarValue value,ECVarType type)146 void FBaseCVar::SetGenericRep (UCVarValue value, ECVarType type)
147 {
148 	if ((Flags & CVAR_NOSET) && m_DoNoSet)
149 	{
150 		return;
151 	}
152 	else if ((Flags & CVAR_LATCH) && gamestate != GS_FULLCONSOLE && gamestate != GS_STARTUP)
153 	{
154 		FLatchedValue latch;
155 
156 		latch.Variable = this;
157 		latch.Type = type;
158 		if (type != CVAR_String)
159 			latch.Value = value;
160 		else
161 			latch.Value.String = copystring(value.String);
162 		LatchedValues.Push (latch);
163 	}
164 	else if ((Flags & CVAR_SERVERINFO) && gamestate != GS_STARTUP && !demoplayback)
165 	{
166 		if (netgame && !players[consoleplayer].settings_controller)
167 		{
168 			Printf ("Only setting controllers can change %s\n", Name);
169 			return;
170 		}
171 		D_SendServerInfoChange (this, value, type);
172 	}
173 	else
174 	{
175 		ForceSet (value, type);
176 	}
177 }
178 
ToBool(UCVarValue value,ECVarType type)179 bool FBaseCVar::ToBool (UCVarValue value, ECVarType type)
180 {
181 	switch (type)
182 	{
183 	case CVAR_Bool:
184 		return value.Bool;
185 
186 	case CVAR_Int:
187 		return !!value.Int;
188 
189 	case CVAR_Float:
190 		return value.Float != 0.f;
191 
192 	case CVAR_String:
193 		if (stricmp (value.String, "true") == 0)
194 			return true;
195 		else if (stricmp (value.String, "false") == 0)
196 			return false;
197 		else
198 			return !!strtol (value.String, NULL, 0);
199 
200 	case CVAR_GUID:
201 		return false;
202 
203 	default:
204 		return false;
205 	}
206 }
207 
ToInt(UCVarValue value,ECVarType type)208 int FBaseCVar::ToInt (UCVarValue value, ECVarType type)
209 {
210 	int res;
211 #if __GNUC__ <= 2
212 	float tmp;
213 #endif
214 
215 	switch (type)
216 	{
217 	case CVAR_Bool:			res = (int)value.Bool; break;
218 	case CVAR_Int:			res = value.Int; break;
219 #if __GNUC__ <= 2
220 	case CVAR_Float:		tmp = value.Float; res = (int)tmp; break;
221 #else
222 	case CVAR_Float:		res = (int)value.Float; break;
223 #endif
224 	case CVAR_String:
225 		{
226 			if (stricmp (value.String, "true") == 0)
227 				res = 1;
228 			else if (stricmp (value.String, "false") == 0)
229 				res = 0;
230 			else
231 				res = strtol (value.String, NULL, 0);
232 			break;
233 		}
234 	case CVAR_GUID:			res = 0; break;
235 	default:				res = 0; break;
236 	}
237 	return res;
238 }
239 
ToFloat(UCVarValue value,ECVarType type)240 float FBaseCVar::ToFloat (UCVarValue value, ECVarType type)
241 {
242 	switch (type)
243 	{
244 	case CVAR_Bool:
245 		return (float)value.Bool;
246 
247 	case CVAR_Int:
248 		return (float)value.Int;
249 
250 	case CVAR_Float:
251 		return value.Float;
252 
253 	case CVAR_String:
254 		return (float)strtod (value.String, NULL);
255 
256 	case CVAR_GUID:
257 		return 0.f;
258 
259 	default:
260 		return 0.f;
261 	}
262 }
263 
264 static char cstrbuf[40];
265 static GUID cGUID;
266 static char truestr[] = "true";
267 static char falsestr[] = "false";
268 
ToString(UCVarValue value,ECVarType type)269 const char *FBaseCVar::ToString (UCVarValue value, ECVarType type)
270 {
271 	switch (type)
272 	{
273 	case CVAR_Bool:
274 		return value.Bool ? truestr : falsestr;
275 
276 	case CVAR_String:
277 		return value.String;
278 
279 	case CVAR_Int:
280 		mysnprintf (cstrbuf, countof(cstrbuf), "%i", value.Int);
281 		break;
282 
283 	case CVAR_Float:
284 		mysnprintf (cstrbuf, countof(cstrbuf), "%g", value.Float);
285 		break;
286 
287 	case CVAR_GUID:
288 		FormatGUID (cstrbuf, countof(cstrbuf), *value.pGUID);
289 		break;
290 
291 	default:
292 		strcpy (cstrbuf, "<huh?>");
293 		break;
294 	}
295 	return cstrbuf;
296 }
297 
ToGUID(UCVarValue value,ECVarType type)298 const GUID *FBaseCVar::ToGUID (UCVarValue value, ECVarType type)
299 {
300 	UCVarValue trans;
301 
302 	switch (type)
303 	{
304 	case CVAR_String:
305 		trans = FromString (value.String, CVAR_GUID);
306 		return trans.pGUID;
307 
308 	case CVAR_GUID:
309 		return value.pGUID;
310 
311 	default:
312 		return NULL;
313 	}
314 }
315 
FromBool(bool value,ECVarType type)316 UCVarValue FBaseCVar::FromBool (bool value, ECVarType type)
317 {
318 	UCVarValue ret;
319 
320 	switch (type)
321 	{
322 	case CVAR_Bool:
323 		ret.Bool = value;
324 		break;
325 
326 	case CVAR_Int:
327 		ret.Int = value;
328 		break;
329 
330 	case CVAR_Float:
331 		ret.Float = value;
332 		break;
333 
334 	case CVAR_String:
335 		ret.String = value ? truestr : falsestr;
336 		break;
337 
338 	case CVAR_GUID:
339 		ret.pGUID = NULL;
340 		break;
341 
342 	default:
343 		break;
344 	}
345 
346 	return ret;
347 }
348 
FromInt(int value,ECVarType type)349 UCVarValue FBaseCVar::FromInt (int value, ECVarType type)
350 {
351 	UCVarValue ret;
352 
353 	switch (type)
354 	{
355 	case CVAR_Bool:
356 		ret.Bool = value != 0;
357 		break;
358 
359 	case CVAR_Int:
360 		ret.Int = value;
361 		break;
362 
363 	case CVAR_Float:
364 		ret.Float = (float)value;
365 		break;
366 
367 	case CVAR_String:
368 		mysnprintf (cstrbuf, countof(cstrbuf), "%i", value);
369 		ret.String = cstrbuf;
370 		break;
371 
372 	case CVAR_GUID:
373 		ret.pGUID = NULL;
374 		break;
375 
376 	default:
377 		break;
378 	}
379 
380 	return ret;
381 }
382 
FromFloat(float value,ECVarType type)383 UCVarValue FBaseCVar::FromFloat (float value, ECVarType type)
384 {
385 	UCVarValue ret;
386 
387 	switch (type)
388 	{
389 	case CVAR_Bool:
390 		ret.Bool = value != 0.f;
391 		break;
392 
393 	case CVAR_Int:
394 		ret.Int = (int)value;
395 		break;
396 
397 	case CVAR_Float:
398 		ret.Float = value;
399 		break;
400 
401 	case CVAR_String:
402 		mysnprintf (cstrbuf, countof(cstrbuf), "%g", value);
403 		ret.String = cstrbuf;
404 		break;
405 
406 	case CVAR_GUID:
407 		ret.pGUID = NULL;
408 		break;
409 
410 	default:
411 		break;
412 	}
413 
414 	return ret;
415 }
416 
HexToByte(const char * hex)417 static BYTE HexToByte (const char *hex)
418 {
419 	BYTE v = 0;
420 	for (int i = 0; i < 2; ++i)
421 	{
422 		v <<= 4;
423 		if (hex[i] >= '0' && hex[i] <= '9')
424 		{
425 			v += hex[i] - '0';
426 		}
427 		else if (hex[i] >= 'A' && hex[i] <= 'F')
428 		{
429 			v += hex[i] - 'A';
430 		}
431 		else // The string is already verified to contain valid hexits
432 		{
433 			v += hex[i] - 'a';
434 		}
435 	}
436 	return v;
437 }
438 
FromString(const char * value,ECVarType type)439 UCVarValue FBaseCVar::FromString (const char *value, ECVarType type)
440 {
441 	UCVarValue ret;
442 	int i;
443 
444 	switch (type)
445 	{
446 	case CVAR_Bool:
447 		if (stricmp (value, "true") == 0)
448 			ret.Bool = true;
449 		else if (stricmp (value, "false") == 0)
450 			ret.Bool = false;
451 		else
452 			ret.Bool = strtol (value, NULL, 0) != 0;
453 		break;
454 
455 	case CVAR_Int:
456 		if (stricmp (value, "true") == 0)
457 			ret.Int = 1;
458 		else if (stricmp (value, "false") == 0)
459 			ret.Int = 0;
460 		else
461 			ret.Int = strtol (value, NULL, 0);
462 		break;
463 
464 	case CVAR_Float:
465 		ret.Float = (float)strtod (value, NULL);
466 		break;
467 
468 	case CVAR_String:
469 		ret.String = const_cast<char *>(value);
470 		break;
471 
472 	case CVAR_GUID:
473 		// {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
474 		// 01234567890123456789012345678901234567
475 		// 0         1         2         3
476 
477 		ret.pGUID = NULL;
478 		if (value == NULL)
479 		{
480 			break;
481 		}
482 		for (i = 0; value[i] != 0 && i < 38; i++)
483 		{
484 			switch (i)
485 			{
486 			case 0:
487 				if (value[i] != '{')
488 					break;
489 			case 9:
490 			case 14:
491 			case 19:
492 			case 24:
493 				if (value[i] != '-')
494 					break;
495 			case 37:
496 				if (value[i] != '}')
497 					break;
498 			default:
499 				if (value[i] < '0' ||
500 					(value[i] > '9' && value[i] < 'A') ||
501 					(value[i] > 'F' && value[i] < 'a') ||
502 					value[i] > 'f')
503 					break;
504 			}
505 		}
506 		if (i == 38 && value[i] == 0)
507 		{
508 			cGUID.Data1 = strtoul (value + 1, NULL, 16);
509 			cGUID.Data2 = (WORD)strtoul (value + 10, NULL, 16);
510 			cGUID.Data3 = (WORD)strtoul (value + 15, NULL, 16);
511 			cGUID.Data4[0] = HexToByte (value + 20);
512 			cGUID.Data4[1] = HexToByte (value + 22);
513 			cGUID.Data4[2] = HexToByte (value + 25);
514 			cGUID.Data4[3] = HexToByte (value + 27);
515 			cGUID.Data4[4] = HexToByte (value + 29);
516 			cGUID.Data4[5] = HexToByte (value + 31);
517 			cGUID.Data4[6] = HexToByte (value + 33);
518 			cGUID.Data4[7] = HexToByte (value + 35);
519 			ret.pGUID = &cGUID;
520 		}
521 		break;
522 
523 	default:
524 		break;
525 	}
526 
527 	return ret;
528 }
529 
FromGUID(const GUID & guid,ECVarType type)530 UCVarValue FBaseCVar::FromGUID (const GUID &guid, ECVarType type)
531 {
532 	UCVarValue ret;
533 
534 	switch (type)
535 	{
536 	case CVAR_Bool:
537 		ret.Bool = false;
538 		break;
539 
540 	case CVAR_Int:
541 		ret.Int = 0;
542 		break;
543 
544 	case CVAR_Float:
545 		ret.Float = 0.f;
546 		break;
547 
548 	case CVAR_String:
549 		ret.pGUID = &guid;
550 		ret.String = ToString (ret, CVAR_GUID);
551 		break;
552 
553 	case CVAR_GUID:
554 		ret.pGUID = &guid;
555 		break;
556 
557 	default:
558 		break;
559 	}
560 
561 	return ret;
562 }
cvar_set(const char * var_name,const char * val)563 FBaseCVar *cvar_set (const char *var_name, const char *val)
564 {
565 	FBaseCVar *var;
566 
567 	if ( (var = FindCVar (var_name, NULL)) )
568 	{
569 		UCVarValue value;
570 		value.String = const_cast<char *>(val);
571 		var->SetGenericRep (value, CVAR_String);
572 	}
573 
574 	return var;
575 }
576 
cvar_forceset(const char * var_name,const char * val)577 FBaseCVar *cvar_forceset (const char *var_name, const char *val)
578 {
579 	FBaseCVar *var;
580 	UCVarValue vval;
581 
582 	if ( (var = FindCVar (var_name, NULL)) )
583 	{
584 		vval.String = const_cast<char *>(val);
585 		var->ForceSet (vval, CVAR_String);
586 	}
587 
588 	return var;
589 }
590 
EnableNoSet()591 void FBaseCVar::EnableNoSet ()
592 {
593 	m_DoNoSet = true;
594 }
595 
EnableCallbacks()596 void FBaseCVar::EnableCallbacks ()
597 {
598 	m_UseCallback = true;
599 	FBaseCVar *cvar = CVars;
600 
601 	while (cvar)
602 	{
603 		if (!(cvar->Flags & CVAR_NOINITCALL))
604 		{
605 			cvar->Callback ();
606 		}
607 		cvar = cvar->m_Next;
608 	}
609 }
610 
DisableCallbacks()611 void FBaseCVar::DisableCallbacks ()
612 {
613 	m_UseCallback = false;
614 }
615 
616 //
617 // Boolean cvar implementation
618 //
619 
FBoolCVar(const char * name,bool def,DWORD flags,void (* callback)(FBoolCVar &))620 FBoolCVar::FBoolCVar (const char *name, bool def, DWORD flags, void (*callback)(FBoolCVar &))
621 : FBaseCVar (name, flags, reinterpret_cast<void (*)(FBaseCVar &)>(callback))
622 {
623 	DefaultValue = def;
624 	if (Flags & CVAR_ISDEFAULT)
625 		Value = def;
626 }
627 
GetRealType() const628 ECVarType FBoolCVar::GetRealType () const
629 {
630 	return CVAR_Bool;
631 }
632 
GetGenericRep(ECVarType type) const633 UCVarValue FBoolCVar::GetGenericRep (ECVarType type) const
634 {
635 	return FromBool (Value, type);
636 }
637 
GetFavoriteRep(ECVarType * type) const638 UCVarValue FBoolCVar::GetFavoriteRep (ECVarType *type) const
639 {
640 	UCVarValue ret;
641 	*type = CVAR_Bool;
642 	ret.Bool = Value;
643 	return ret;
644 }
645 
GetGenericRepDefault(ECVarType type) const646 UCVarValue FBoolCVar::GetGenericRepDefault (ECVarType type) const
647 {
648 	return FromBool (DefaultValue, type);
649 }
650 
GetFavoriteRepDefault(ECVarType * type) const651 UCVarValue FBoolCVar::GetFavoriteRepDefault (ECVarType *type) const
652 {
653 	UCVarValue ret;
654 	*type = CVAR_Bool;
655 	ret.Bool = DefaultValue;
656 	return ret;
657 }
658 
SetGenericRepDefault(UCVarValue value,ECVarType type)659 void FBoolCVar::SetGenericRepDefault (UCVarValue value, ECVarType type)
660 {
661 	DefaultValue = ToBool (value, type);
662 	if (Flags & CVAR_ISDEFAULT)
663 	{
664 		SetGenericRep (value, type);
665 		Flags |= CVAR_ISDEFAULT;
666 	}
667 }
668 
DoSet(UCVarValue value,ECVarType type)669 void FBoolCVar::DoSet (UCVarValue value, ECVarType type)
670 {
671 	Value = ToBool (value, type);
672 }
673 
674 //
675 // Integer cvar implementation
676 //
677 
FIntCVar(const char * name,int def,DWORD flags,void (* callback)(FIntCVar &))678 FIntCVar::FIntCVar (const char *name, int def, DWORD flags, void (*callback)(FIntCVar &))
679 : FBaseCVar (name, flags, reinterpret_cast<void (*)(FBaseCVar &)>(callback))
680 {
681 	DefaultValue = def;
682 	if (Flags & CVAR_ISDEFAULT)
683 		Value = def;
684 }
685 
GetRealType() const686 ECVarType FIntCVar::GetRealType () const
687 {
688 	return CVAR_Int;
689 }
690 
GetGenericRep(ECVarType type) const691 UCVarValue FIntCVar::GetGenericRep (ECVarType type) const
692 {
693 	return FromInt (Value, type);
694 }
695 
GetFavoriteRep(ECVarType * type) const696 UCVarValue FIntCVar::GetFavoriteRep (ECVarType *type) const
697 {
698 	UCVarValue ret;
699 	*type = CVAR_Int;
700 	ret.Int = Value;
701 	return ret;
702 }
703 
GetGenericRepDefault(ECVarType type) const704 UCVarValue FIntCVar::GetGenericRepDefault (ECVarType type) const
705 {
706 	return FromInt (DefaultValue, type);
707 }
708 
GetFavoriteRepDefault(ECVarType * type) const709 UCVarValue FIntCVar::GetFavoriteRepDefault (ECVarType *type) const
710 {
711 	UCVarValue ret;
712 	*type = CVAR_Int;
713 	ret.Int = DefaultValue;
714 	return ret;
715 }
716 
SetGenericRepDefault(UCVarValue value,ECVarType type)717 void FIntCVar::SetGenericRepDefault (UCVarValue value, ECVarType type)
718 {
719 	DefaultValue = ToInt (value, type);
720 	if (Flags & CVAR_ISDEFAULT)
721 	{
722 		SetGenericRep (value, type);
723 		Flags |= CVAR_ISDEFAULT;
724 	}
725 }
726 
DoSet(UCVarValue value,ECVarType type)727 void FIntCVar::DoSet (UCVarValue value, ECVarType type)
728 {
729 	Value = ToInt (value, type);
730 }
731 
732 //
733 // Floating point cvar implementation
734 //
735 
FFloatCVar(const char * name,float def,DWORD flags,void (* callback)(FFloatCVar &))736 FFloatCVar::FFloatCVar (const char *name, float def, DWORD flags, void (*callback)(FFloatCVar &))
737 : FBaseCVar (name, flags, reinterpret_cast<void (*)(FBaseCVar &)>(callback))
738 {
739 	DefaultValue = def;
740 	if (Flags & CVAR_ISDEFAULT)
741 		Value = def;
742 }
743 
GetRealType() const744 ECVarType FFloatCVar::GetRealType () const
745 {
746 	return CVAR_Float;
747 }
748 
GetGenericRep(ECVarType type) const749 UCVarValue FFloatCVar::GetGenericRep (ECVarType type) const
750 {
751 	return FromFloat (Value, type);
752 }
753 
GetFavoriteRep(ECVarType * type) const754 UCVarValue FFloatCVar::GetFavoriteRep (ECVarType *type) const
755 {
756 	UCVarValue ret;
757 	*type = CVAR_Float;
758 	ret.Float = Value;
759 	return ret;
760 }
761 
GetGenericRepDefault(ECVarType type) const762 UCVarValue FFloatCVar::GetGenericRepDefault (ECVarType type) const
763 {
764 	return FromFloat (DefaultValue, type);
765 }
766 
GetFavoriteRepDefault(ECVarType * type) const767 UCVarValue FFloatCVar::GetFavoriteRepDefault (ECVarType *type) const
768 {
769 	UCVarValue ret;
770 	*type = CVAR_Float;
771 	ret.Float = DefaultValue;
772 	return ret;
773 }
774 
SetGenericRepDefault(UCVarValue value,ECVarType type)775 void FFloatCVar::SetGenericRepDefault (UCVarValue value, ECVarType type)
776 {
777 	DefaultValue = ToFloat (value, type);
778 	if (Flags & CVAR_ISDEFAULT)
779 	{
780 		SetGenericRep (value, type);
781 		Flags |= CVAR_ISDEFAULT;
782 	}
783 }
784 
DoSet(UCVarValue value,ECVarType type)785 void FFloatCVar::DoSet (UCVarValue value, ECVarType type)
786 {
787 	Value = ToFloat (value, type);
788 }
789 
790 //
791 // String cvar implementation
792 //
793 
FStringCVar(const char * name,const char * def,DWORD flags,void (* callback)(FStringCVar &))794 FStringCVar::FStringCVar (const char *name, const char *def, DWORD flags, void (*callback)(FStringCVar &))
795 : FBaseCVar (name, flags, reinterpret_cast<void (*)(FBaseCVar &)>(callback))
796 {
797 	DefaultValue = copystring (def);
798 	if (Flags & CVAR_ISDEFAULT)
799 		Value = copystring (def);
800 	else
801 		Value = NULL;
802 }
803 
~FStringCVar()804 FStringCVar::~FStringCVar ()
805 {
806 	if (Value != NULL)
807 	{
808 		delete[] Value;
809 	}
810 	delete[] DefaultValue;
811 }
812 
GetRealType() const813 ECVarType FStringCVar::GetRealType () const
814 {
815 	return CVAR_String;
816 }
817 
GetGenericRep(ECVarType type) const818 UCVarValue FStringCVar::GetGenericRep (ECVarType type) const
819 {
820 	return FromString (Value, type);
821 }
822 
GetFavoriteRep(ECVarType * type) const823 UCVarValue FStringCVar::GetFavoriteRep (ECVarType *type) const
824 {
825 	UCVarValue ret;
826 	*type = CVAR_String;
827 	ret.String = Value;
828 	return ret;
829 }
830 
GetGenericRepDefault(ECVarType type) const831 UCVarValue FStringCVar::GetGenericRepDefault (ECVarType type) const
832 {
833 	return FromString (DefaultValue, type);
834 }
835 
GetFavoriteRepDefault(ECVarType * type) const836 UCVarValue FStringCVar::GetFavoriteRepDefault (ECVarType *type) const
837 {
838 	UCVarValue ret;
839 	*type = CVAR_String;
840 	ret.String = DefaultValue;
841 	return ret;
842 }
843 
SetGenericRepDefault(UCVarValue value,ECVarType type)844 void FStringCVar::SetGenericRepDefault (UCVarValue value, ECVarType type)
845 {
846 	ReplaceString(&DefaultValue, ToString(value, type));
847 	if (Flags & CVAR_ISDEFAULT)
848 	{
849 		SetGenericRep (value, type);
850 		Flags |= CVAR_ISDEFAULT;
851 	}
852 }
853 
DoSet(UCVarValue value,ECVarType type)854 void FStringCVar::DoSet (UCVarValue value, ECVarType type)
855 {
856 	ReplaceString (&Value, ToString (value, type));
857 }
858 
859 //
860 // Color cvar implementation
861 //
862 
FColorCVar(const char * name,int def,DWORD flags,void (* callback)(FColorCVar &))863 FColorCVar::FColorCVar (const char *name, int def, DWORD flags, void (*callback)(FColorCVar &))
864 : FIntCVar (name, def, flags, reinterpret_cast<void (*)(FIntCVar &)>(callback))
865 {
866 }
867 
GetRealType() const868 ECVarType FColorCVar::GetRealType () const
869 {
870 	return CVAR_Color;
871 }
872 
GetGenericRep(ECVarType type) const873 UCVarValue FColorCVar::GetGenericRep (ECVarType type) const
874 {
875 	return FromInt2 (Value, type);
876 }
877 
GetGenericRepDefault(ECVarType type) const878 UCVarValue FColorCVar::GetGenericRepDefault (ECVarType type) const
879 {
880 	return FromInt2 (DefaultValue, type);
881 }
882 
SetGenericRepDefault(UCVarValue value,ECVarType type)883 void FColorCVar::SetGenericRepDefault (UCVarValue value, ECVarType type)
884 {
885 	DefaultValue = ToInt2 (value, type);
886 	if (Flags & CVAR_ISDEFAULT)
887 	{
888 		SetGenericRep (value, type);
889 		Flags |= CVAR_ISDEFAULT;
890 	}
891 }
892 
DoSet(UCVarValue value,ECVarType type)893 void FColorCVar::DoSet (UCVarValue value, ECVarType type)
894 {
895 	Value = ToInt2 (value, type);
896 	if (screen)
897 		Index = ColorMatcher.Pick (RPART(Value), GPART(Value), BPART(Value));
898 }
899 
FromInt2(int value,ECVarType type)900 UCVarValue FColorCVar::FromInt2 (int value, ECVarType type)
901 {
902 	if (type == CVAR_String)
903 	{
904 		UCVarValue ret;
905 		mysnprintf (cstrbuf, countof(cstrbuf), "%02x %02x %02x",
906 			RPART(value), GPART(value), BPART(value));
907 		ret.String = cstrbuf;
908 		return ret;
909 	}
910 	return FromInt (value, type);
911 }
912 
ToInt2(UCVarValue value,ECVarType type)913 int FColorCVar::ToInt2 (UCVarValue value, ECVarType type)
914 {
915 	int ret;
916 
917 	if (type == CVAR_String)
918 	{
919 		FString string;
920 		// Only allow named colors after the screen exists (i.e. after
921 		// we've got some lumps loaded, so X11R6RGB can be read). Since
922 		// the only time this might be called before that is when loading
923 		// zdoom.ini, this shouldn't be a problem.
924 		if (screen && !(string = V_GetColorStringByName (value.String)).IsEmpty() )
925 		{
926 			ret = V_GetColorFromString (NULL, string);
927 		}
928 		else
929 		{
930 			ret = V_GetColorFromString (NULL, value.String);
931 		}
932 	}
933 	else
934 	{
935 		ret = ToInt (value, type);
936 	}
937 	return ret;
938 }
939 
940 //
941 // GUID cvar implementation
942 //
943 
FGUIDCVar(const char * name,const GUID * def,DWORD flags,void (* callback)(FGUIDCVar &))944 FGUIDCVar::FGUIDCVar (const char *name, const GUID *def, DWORD flags, void (*callback)(FGUIDCVar &))
945 : FBaseCVar (name, flags, reinterpret_cast<void (*)(FBaseCVar &)>(callback))
946 {
947 	if (def != NULL)
948 	{
949 		DefaultValue = *def;
950 		if (Flags & CVAR_ISDEFAULT)
951 			Value = *def;
952 	}
953 	else
954 	{
955 		memset (&Value, 0, sizeof(DefaultValue));
956 		memset (&DefaultValue, 0, sizeof(DefaultValue));
957 	}
958 }
959 
GetRealType() const960 ECVarType FGUIDCVar::GetRealType () const
961 {
962 	return CVAR_GUID;
963 }
964 
GetGenericRep(ECVarType type) const965 UCVarValue FGUIDCVar::GetGenericRep (ECVarType type) const
966 {
967 	return FromGUID (Value, type);
968 }
969 
GetFavoriteRep(ECVarType * type) const970 UCVarValue FGUIDCVar::GetFavoriteRep (ECVarType *type) const
971 {
972 	UCVarValue ret;
973 	*type = CVAR_GUID;
974 	ret.pGUID = &Value;
975 	return ret;
976 }
977 
GetGenericRepDefault(ECVarType type) const978 UCVarValue FGUIDCVar::GetGenericRepDefault (ECVarType type) const
979 {
980 	return FromGUID (DefaultValue, type);
981 }
982 
GetFavoriteRepDefault(ECVarType * type) const983 UCVarValue FGUIDCVar::GetFavoriteRepDefault (ECVarType *type) const
984 {
985 	UCVarValue ret;
986 	*type = CVAR_GUID;
987 	ret.pGUID = &DefaultValue;
988 	return ret;
989 }
990 
SetGenericRepDefault(UCVarValue value,ECVarType type)991 void FGUIDCVar::SetGenericRepDefault (UCVarValue value, ECVarType type)
992 {
993 	const GUID *guid = ToGUID (value, type);
994 	if (guid != NULL)
995 	{
996 		Value = *guid;
997 		if (Flags & CVAR_ISDEFAULT)
998 		{
999 			SetGenericRep (value, type);
1000 			Flags |= CVAR_ISDEFAULT;
1001 		}
1002 	}
1003 }
1004 
DoSet(UCVarValue value,ECVarType type)1005 void FGUIDCVar::DoSet (UCVarValue value, ECVarType type)
1006 {
1007 	const GUID *guid = ToGUID (value, type);
1008 	if (guid != NULL)
1009 	{
1010 		Value = *guid;
1011 	}
1012 }
1013 
1014 //
1015 // More base cvar stuff
1016 //
1017 
ResetColors()1018 void FBaseCVar::ResetColors ()
1019 {
1020 	FBaseCVar *var = CVars;
1021 
1022 	while (var)
1023 	{
1024 		if (var->GetRealType () == CVAR_Color)
1025 		{
1026 			var->DoSet (var->GetGenericRep (CVAR_Int), CVAR_Int);
1027 		}
1028 		var = var->m_Next;
1029 	}
1030 }
1031 
ResetToDefault()1032 void FBaseCVar::ResetToDefault ()
1033 {
1034 	if (!(Flags & CVAR_ISDEFAULT))
1035 	{
1036 		UCVarValue val;
1037 		ECVarType type;
1038 
1039 		val = GetFavoriteRepDefault (&type);
1040 		SetGenericRep (val, type);
1041 		Flags |= CVAR_ISDEFAULT;
1042 	}
1043 }
1044 
1045 //
1046 // Flag cvar implementation
1047 //
1048 // This type of cvar is not a "real" cvar. Instead, it gets and sets
1049 // the value of a FIntCVar, modifying it bit-by-bit. As such, it has
1050 // no default, and is not written to the .cfg or transferred around
1051 // the network. The "host" cvar is responsible for that.
1052 //
1053 
FFlagCVar(const char * name,FIntCVar & realvar,DWORD bitval)1054 FFlagCVar::FFlagCVar (const char *name, FIntCVar &realvar, DWORD bitval)
1055 : FBaseCVar (name, 0, NULL),
1056 ValueVar (realvar),
1057 BitVal (bitval)
1058 {
1059 	int bit;
1060 
1061 	Flags &= ~CVAR_ISDEFAULT;
1062 
1063 	assert (bitval != 0);
1064 
1065 	bit = 0;
1066 	while ((bitval >>= 1) != 0)
1067 	{
1068 		++bit;
1069 	}
1070 	BitNum = bit;
1071 
1072 	assert ((1u << BitNum) == BitVal);
1073 }
1074 
GetRealType() const1075 ECVarType FFlagCVar::GetRealType () const
1076 {
1077 	return CVAR_Dummy;
1078 }
1079 
GetGenericRep(ECVarType type) const1080 UCVarValue FFlagCVar::GetGenericRep (ECVarType type) const
1081 {
1082 	return FromBool ((ValueVar & BitVal) != 0, type);
1083 }
1084 
GetFavoriteRep(ECVarType * type) const1085 UCVarValue FFlagCVar::GetFavoriteRep (ECVarType *type) const
1086 {
1087 	UCVarValue ret;
1088 	*type = CVAR_Bool;
1089 	ret.Bool = (ValueVar & BitVal) != 0;
1090 	return ret;
1091 }
1092 
GetGenericRepDefault(ECVarType type) const1093 UCVarValue FFlagCVar::GetGenericRepDefault (ECVarType type) const
1094 {
1095 	ECVarType dummy;
1096 	UCVarValue def;
1097 	def = ValueVar.GetFavoriteRepDefault (&dummy);
1098 	return FromBool ((def.Int & BitVal) != 0, type);
1099 }
1100 
GetFavoriteRepDefault(ECVarType * type) const1101 UCVarValue FFlagCVar::GetFavoriteRepDefault (ECVarType *type) const
1102 {
1103 	ECVarType dummy;
1104 	UCVarValue def;
1105 	def = ValueVar.GetFavoriteRepDefault (&dummy);
1106 	def.Bool = (def.Int & BitVal) != 0;
1107 	*type = CVAR_Bool;
1108 	return def;
1109 }
1110 
SetGenericRepDefault(UCVarValue value,ECVarType type)1111 void FFlagCVar::SetGenericRepDefault (UCVarValue value, ECVarType type)
1112 {
1113 	bool newdef = ToBool (value, type);
1114 	ECVarType dummy;
1115 	UCVarValue def;
1116 	def = ValueVar.GetFavoriteRepDefault (&dummy);
1117 	if (newdef)
1118 		def.Int |= BitVal;
1119 	else
1120 		def.Int &= ~BitVal;
1121 	ValueVar.SetGenericRepDefault (def, CVAR_Int);
1122 }
1123 
DoSet(UCVarValue value,ECVarType type)1124 void FFlagCVar::DoSet (UCVarValue value, ECVarType type)
1125 {
1126 	bool newval = ToBool (value, type);
1127 
1128 	// Server cvars that get changed by this need to use a special message, because
1129 	// changes are not processed until the next net update. This is a problem with
1130 	// exec scripts because all flags will base their changes off of the value of
1131 	// the "master" cvar at the time the script was run, overriding any changes
1132 	// another flag might have made to the same cvar earlier in the script.
1133 	if ((ValueVar.GetFlags() & CVAR_SERVERINFO) && gamestate != GS_STARTUP && !demoplayback)
1134 	{
1135 		if (netgame && !players[consoleplayer].settings_controller)
1136 		{
1137 			Printf ("Only setting controllers can change %s\n", Name);
1138 			return;
1139 		}
1140 		D_SendServerFlagChange (&ValueVar, BitNum, newval);
1141 	}
1142 	else
1143 	{
1144 		int val = *ValueVar;
1145 		if (newval)
1146 			val |= BitVal;
1147 		else
1148 			val &= ~BitVal;
1149 		ValueVar = val;
1150 	}
1151 }
1152 
1153 //
1154 // Mask cvar implementation
1155 //
1156 // Similar to FFlagCVar but can have multiple bits
1157 //
1158 
FMaskCVar(const char * name,FIntCVar & realvar,DWORD bitval)1159 FMaskCVar::FMaskCVar (const char *name, FIntCVar &realvar, DWORD bitval)
1160 : FBaseCVar (name, 0, NULL),
1161 ValueVar (realvar),
1162 BitVal (bitval)
1163 {
1164 	int bit;
1165 
1166 	Flags &= ~CVAR_ISDEFAULT;
1167 
1168 	assert (bitval != 0);
1169 
1170 	bit = 0;
1171 	while ((bitval & 1) == 0)
1172 	{
1173 		++bit;
1174 		bitval >>= 1;
1175 	}
1176 	BitNum = bit;
1177 }
1178 
GetRealType() const1179 ECVarType FMaskCVar::GetRealType () const
1180 {
1181 	return CVAR_Dummy;
1182 }
1183 
GetGenericRep(ECVarType type) const1184 UCVarValue FMaskCVar::GetGenericRep (ECVarType type) const
1185 {
1186 	return FromInt ((ValueVar & BitVal) >> BitNum, type);
1187 }
1188 
GetFavoriteRep(ECVarType * type) const1189 UCVarValue FMaskCVar::GetFavoriteRep (ECVarType *type) const
1190 {
1191 	UCVarValue ret;
1192 	*type = CVAR_Int;
1193 	ret.Int = (ValueVar & BitVal) >> BitNum;
1194 	return ret;
1195 }
1196 
GetGenericRepDefault(ECVarType type) const1197 UCVarValue FMaskCVar::GetGenericRepDefault (ECVarType type) const
1198 {
1199 	ECVarType dummy;
1200 	UCVarValue def;
1201 	def = ValueVar.GetFavoriteRepDefault (&dummy);
1202 	return FromInt ((def.Int & BitVal) >> BitNum, type);
1203 }
1204 
GetFavoriteRepDefault(ECVarType * type) const1205 UCVarValue FMaskCVar::GetFavoriteRepDefault (ECVarType *type) const
1206 {
1207 	ECVarType dummy;
1208 	UCVarValue def;
1209 	def = ValueVar.GetFavoriteRepDefault (&dummy);
1210 	def.Int = (def.Int & BitVal) >> BitNum;
1211 	*type = CVAR_Int;
1212 	return def;
1213 }
1214 
SetGenericRepDefault(UCVarValue value,ECVarType type)1215 void FMaskCVar::SetGenericRepDefault (UCVarValue value, ECVarType type)
1216 {
1217 	int val = ToInt(value, type) << BitNum;
1218 	ECVarType dummy;
1219 	UCVarValue def;
1220 	def = ValueVar.GetFavoriteRepDefault (&dummy);
1221 	def.Int &= ~BitVal;
1222 	def.Int |= val;
1223 	ValueVar.SetGenericRepDefault (def, CVAR_Int);
1224 }
1225 
DoSet(UCVarValue value,ECVarType type)1226 void FMaskCVar::DoSet (UCVarValue value, ECVarType type)
1227 {
1228 	int val = ToInt(value, type) << BitNum;
1229 
1230 	// Server cvars that get changed by this need to use a special message, because
1231 	// changes are not processed until the next net update. This is a problem with
1232 	// exec scripts because all flags will base their changes off of the value of
1233 	// the "master" cvar at the time the script was run, overriding any changes
1234 	// another flag might have made to the same cvar earlier in the script.
1235 	if ((ValueVar.GetFlags() & CVAR_SERVERINFO) && gamestate != GS_STARTUP && !demoplayback)
1236 	{
1237 		if (netgame && !players[consoleplayer].settings_controller)
1238 		{
1239 			Printf ("Only setting controllers can change %s\n", Name);
1240 			return;
1241 		}
1242 		// Ugh...
1243 		for(int i = 0; i < 32; i++)
1244 		{
1245 			if (BitVal & (1<<i))
1246 			{
1247 				D_SendServerFlagChange (&ValueVar, i, !!(val & (1<<i)));
1248 			}
1249 		}
1250 	}
1251 	else
1252 	{
1253 		int vval = *ValueVar;
1254 		vval &= ~BitVal;
1255 		vval |= val;
1256 		ValueVar = vval;
1257 	}
1258 }
1259 
1260 
1261 ////////////////////////////////////////////////////////////////////////
sortcvars(const void * a,const void * b)1262 static int STACK_ARGS sortcvars (const void *a, const void *b)
1263 {
1264 	return strcmp (((*(FBaseCVar **)a))->GetName(), ((*(FBaseCVar **)b))->GetName());
1265 }
1266 
FilterCompactCVars(TArray<FBaseCVar * > & cvars,DWORD filter)1267 void FilterCompactCVars (TArray<FBaseCVar *> &cvars, DWORD filter)
1268 {
1269 	// Accumulate all cvars that match the filter flags.
1270 	for (FBaseCVar *cvar = CVars; cvar != NULL; cvar = cvar->m_Next)
1271 	{
1272 		if ((cvar->Flags & filter) && !(cvar->Flags & CVAR_IGNORE))
1273 			cvars.Push(cvar);
1274 	}
1275 	// Now sort them, so they're in a deterministic order and not whatever
1276 	// order the linker put them in.
1277 	if (cvars.Size() > 0)
1278 	{
1279 		qsort(&cvars[0], cvars.Size(), sizeof(FBaseCVar *), sortcvars);
1280 	}
1281 }
1282 
C_WriteCVars(BYTE ** demo_p,DWORD filter,bool compact)1283 void C_WriteCVars (BYTE **demo_p, DWORD filter, bool compact)
1284 {
1285 	FString dump = C_GetMassCVarString(filter, compact);
1286 	size_t dumplen = dump.Len() + 1;	// include terminating \0
1287 	memcpy(*demo_p, dump.GetChars(), dumplen);
1288 	*demo_p += dumplen;
1289 }
1290 
C_GetMassCVarString(DWORD filter,bool compact)1291 FString C_GetMassCVarString (DWORD filter, bool compact)
1292 {
1293 	FBaseCVar *cvar;
1294 	FString dump;
1295 
1296 	if (compact)
1297 	{
1298 		TArray<FBaseCVar *> cvars;
1299 		dump.AppendFormat("\\\\%ux", filter);
1300 		FilterCompactCVars(cvars, filter);
1301 		while (cvars.Pop (cvar))
1302 		{
1303 			UCVarValue val = cvar->GetGenericRep(CVAR_String);
1304 			dump << '\\' << val.String;
1305 		}
1306 	}
1307 	else
1308 	{
1309 		for (cvar = CVars; cvar != NULL; cvar = cvar->m_Next)
1310 		{
1311 			if ((cvar->Flags & filter) && !(cvar->Flags & (CVAR_NOSAVE|CVAR_IGNORE)))
1312 			{
1313 				UCVarValue val = cvar->GetGenericRep(CVAR_String);
1314 				dump << '\\' << cvar->GetName() << '\\' << val.String;
1315 			}
1316 		}
1317 	}
1318 	return dump;
1319 }
1320 
C_ReadCVars(BYTE ** demo_p)1321 void C_ReadCVars (BYTE **demo_p)
1322 {
1323 	char *ptr = *((char **)demo_p);
1324 	char *breakpt;
1325 
1326 	if (*ptr++ != '\\')
1327 		return;
1328 
1329 	if (*ptr == '\\')
1330 	{       // compact mode
1331 		TArray<FBaseCVar *> cvars;
1332 		FBaseCVar *cvar;
1333 		DWORD filter;
1334 
1335 		ptr++;
1336 		breakpt = strchr (ptr, '\\');
1337 		*breakpt = 0;
1338 		filter = strtoul (ptr, NULL, 16);
1339 		*breakpt = '\\';
1340 		ptr = breakpt + 1;
1341 
1342 		FilterCompactCVars (cvars, filter);
1343 
1344 		while (cvars.Pop (cvar))
1345 		{
1346 			UCVarValue val;
1347 			breakpt = strchr (ptr, '\\');
1348 			if (breakpt)
1349 				*breakpt = 0;
1350 			val.String = ptr;
1351 			cvar->ForceSet (val, CVAR_String);
1352 			if (breakpt)
1353 			{
1354 				*breakpt = '\\';
1355 				ptr = breakpt + 1;
1356 			}
1357 			else
1358 				break;
1359 		}
1360 	}
1361 	else
1362 	{
1363 		char *value;
1364 
1365 		while ( (breakpt = strchr (ptr, '\\')) )
1366 		{
1367 			*breakpt = 0;
1368 			value = breakpt + 1;
1369 			if ( (breakpt = strchr (value, '\\')) )
1370 				*breakpt = 0;
1371 
1372 			cvar_set (ptr, value);
1373 
1374 			*(value - 1) = '\\';
1375 			if (breakpt)
1376 			{
1377 				*breakpt = '\\';
1378 				ptr = breakpt + 1;
1379 			}
1380 			else
1381 			{
1382 				break;
1383 			}
1384 		}
1385 	}
1386 	*demo_p += strlen (*((char **)demo_p)) + 1;
1387 }
1388 
1389 struct FCVarBackup
1390 {
1391 	FString Name, String;
1392 };
1393 static TArray<FCVarBackup> CVarBackups;
1394 
C_BackupCVars(void)1395 void C_BackupCVars (void)
1396 {
1397 	assert(CVarBackups.Size() == 0);
1398 	CVarBackups.Clear();
1399 
1400 	FCVarBackup backup;
1401 
1402 	for (FBaseCVar *cvar = CVars; cvar != NULL; cvar = cvar->m_Next)
1403 	{
1404 		if ((cvar->Flags & (CVAR_SERVERINFO|CVAR_DEMOSAVE)) && !(cvar->Flags & CVAR_LATCH))
1405 		{
1406 			backup.Name = cvar->GetName();
1407 			backup.String = cvar->GetGenericRep(CVAR_String).String;
1408 			CVarBackups.Push(backup);
1409 		}
1410 	}
1411 }
1412 
C_RestoreCVars(void)1413 void C_RestoreCVars (void)
1414 {
1415 	for (unsigned int i = 0; i < CVarBackups.Size(); ++i)
1416 	{
1417 		cvar_set(CVarBackups[i].Name, CVarBackups[i].String);
1418 	}
1419 	C_ForgetCVars();
1420 }
1421 
C_ForgetCVars(void)1422 void C_ForgetCVars (void)
1423 {
1424 	CVarBackups.Clear();
1425 }
1426 
FindCVar(const char * var_name,FBaseCVar ** prev)1427 FBaseCVar *FindCVar (const char *var_name, FBaseCVar **prev)
1428 {
1429 	FBaseCVar *var;
1430 	FBaseCVar *dummy;
1431 
1432 	if (var_name == NULL)
1433 		return NULL;
1434 
1435 	if (prev == NULL)
1436 		prev = &dummy;
1437 
1438 	var = CVars;
1439 	*prev = NULL;
1440 	while (var)
1441 	{
1442 		if (stricmp (var->GetName (), var_name) == 0)
1443 			break;
1444 		*prev = var;
1445 		var = var->m_Next;
1446 	}
1447 	return var;
1448 }
1449 
FindCVarSub(const char * var_name,int namelen)1450 FBaseCVar *FindCVarSub (const char *var_name, int namelen)
1451 {
1452 	FBaseCVar *var;
1453 
1454 	if (var_name == NULL)
1455 		return NULL;
1456 
1457 	var = CVars;
1458 	while (var)
1459 	{
1460 		const char *probename = var->GetName ();
1461 
1462 		if (strnicmp (probename, var_name, namelen) == 0 &&
1463 			probename[namelen] == 0)
1464 		{
1465 			break;
1466 		}
1467 		var = var->m_Next;
1468 	}
1469 	return var;
1470 }
1471 
1472 //===========================================================================
1473 //
1474 // C_CreateCVar
1475 //
1476 // Create a new cvar with the specified name and type. It should not already
1477 // exist.
1478 //
1479 //===========================================================================
1480 
C_CreateCVar(const char * var_name,ECVarType var_type,DWORD flags)1481 FBaseCVar *C_CreateCVar(const char *var_name, ECVarType var_type, DWORD flags)
1482 {
1483 	assert(FindCVar(var_name, NULL) == NULL);
1484 	flags |= CVAR_AUTO;
1485 	switch (var_type)
1486 	{
1487 	case CVAR_Bool:		return new FBoolCVar(var_name, 0, flags);
1488 	case CVAR_Int:		return new FIntCVar(var_name, 0, flags);
1489 	case CVAR_Float:	return new FFloatCVar(var_name, 0, flags);
1490 	case CVAR_String:	return new FStringCVar(var_name, NULL, flags);
1491 	case CVAR_Color:	return new FColorCVar(var_name, 0, flags);
1492 	default:			return NULL;
1493 	}
1494 }
1495 
UnlatchCVars(void)1496 void UnlatchCVars (void)
1497 {
1498 	FLatchedValue var;
1499 
1500 	while (LatchedValues.Pop (var))
1501 	{
1502 		DWORD oldflags = var.Variable->Flags;
1503 		var.Variable->Flags &= ~(CVAR_LATCH | CVAR_SERVERINFO);
1504 		var.Variable->SetGenericRep (var.Value, var.Type);
1505 		if (var.Type == CVAR_String)
1506 			delete[] var.Value.String;
1507 		var.Variable->Flags = oldflags;
1508 	}
1509 }
1510 
DestroyCVarsFlagged(DWORD flags)1511 void DestroyCVarsFlagged (DWORD flags)
1512 {
1513 	FBaseCVar *cvar = CVars;
1514 	FBaseCVar *next = cvar;
1515 
1516 	while(cvar)
1517 	{
1518 		next = cvar->m_Next;
1519 
1520 		if(cvar->Flags & flags)
1521 			delete cvar;
1522 
1523 		cvar = next;
1524 	}
1525 }
1526 
C_SetCVarsToDefaults(void)1527 void C_SetCVarsToDefaults (void)
1528 {
1529 	FBaseCVar *cvar = CVars;
1530 
1531 	while (cvar)
1532 	{
1533 		// Only default save-able cvars
1534 		if (cvar->Flags & CVAR_ARCHIVE)
1535 		{
1536 			UCVarValue val;
1537 			ECVarType type;
1538 			val = cvar->GetFavoriteRepDefault (&type);
1539 			cvar->SetGenericRep (val, type);
1540 		}
1541 		cvar = cvar->m_Next;
1542 	}
1543 }
1544 
C_ArchiveCVars(FConfigFile * f,uint32 filter)1545 void C_ArchiveCVars (FConfigFile *f, uint32 filter)
1546 {
1547 	FBaseCVar *cvar = CVars;
1548 
1549 	while (cvar)
1550 	{
1551 		if ((cvar->Flags &
1552 			(CVAR_GLOBALCONFIG|CVAR_ARCHIVE|CVAR_MOD|CVAR_AUTO|CVAR_USERINFO|CVAR_SERVERINFO|CVAR_NOSAVE))
1553 			== filter)
1554 		{
1555 			UCVarValue val;
1556 			val = cvar->GetGenericRep (CVAR_String);
1557 			f->SetValueForKey (cvar->GetName (), val.String);
1558 		}
1559 		cvar = cvar->m_Next;
1560 	}
1561 }
1562 
CmdSet(const char * newval)1563 void FBaseCVar::CmdSet (const char *newval)
1564 {
1565 	UCVarValue val;
1566 
1567 	// Casting away the const is safe in this case.
1568 	val.String = const_cast<char *>(newval);
1569 	SetGenericRep (val, CVAR_String);
1570 
1571 	if (GetFlags() & CVAR_NOSET)
1572 		Printf ("%s is write protected.\n", GetName());
1573 	else if (GetFlags() & CVAR_LATCH)
1574 		Printf ("%s will be changed for next game.\n", GetName());
1575 }
1576 
CCMD(set)1577 CCMD (set)
1578 {
1579 	if (argv.argc() != 3)
1580 	{
1581 		Printf ("usage: set <variable> <value>\n");
1582 	}
1583 	else
1584 	{
1585 		FBaseCVar *var;
1586 
1587 		var = FindCVar (argv[1], NULL);
1588 		if (var == NULL)
1589 			var = new FStringCVar (argv[1], NULL, CVAR_AUTO | CVAR_UNSETTABLE | cvar_defflags);
1590 
1591 		var->CmdSet (argv[2]);
1592 	}
1593 }
1594 
CCMD(unset)1595 CCMD (unset)
1596 {
1597 	if (argv.argc() != 2)
1598 	{
1599 		Printf ("usage: unset <variable>\n");
1600 	}
1601 	else
1602 	{
1603 		FBaseCVar *var = FindCVar (argv[1], NULL);
1604 		if (var != NULL)
1605 		{
1606 			if (var->GetFlags() & CVAR_UNSETTABLE)
1607 			{
1608 				delete var;
1609 			}
1610 			else
1611 			{
1612 				Printf ("Cannot unset %s\n", argv[1]);
1613 			}
1614 		}
1615 	}
1616 }
1617 
CCMD(get)1618 CCMD (get)
1619 {
1620 	FBaseCVar *var, *prev;
1621 
1622 	if (argv.argc() >= 2)
1623 	{
1624 		if ( (var = FindCVar (argv[1], &prev)) )
1625 		{
1626 			UCVarValue val;
1627 			val = var->GetGenericRep (CVAR_String);
1628 			Printf ("\"%s\" is \"%s\"\n", var->GetName(), val.String);
1629 		}
1630 		else
1631 		{
1632 			Printf ("\"%s\" is unset\n", argv[1]);
1633 		}
1634 	}
1635 	else
1636 	{
1637 		Printf ("get: need variable name\n");
1638 	}
1639 }
1640 
CCMD(toggle)1641 CCMD (toggle)
1642 {
1643 	FBaseCVar *var, *prev;
1644 	UCVarValue val;
1645 
1646 	if (argv.argc() > 1)
1647 	{
1648 		if ( (var = FindCVar (argv[1], &prev)) )
1649 		{
1650 			val = var->GetGenericRep (CVAR_Bool);
1651 			val.Bool = !val.Bool;
1652 			var->SetGenericRep (val, CVAR_Bool);
1653 			Printf ("\"%s\" is \"%s\"\n", var->GetName(),
1654 				val.Bool ? "true" : "false");
1655 		}
1656 	}
1657 }
1658 
ListVars(const char * filter,bool plain)1659 void FBaseCVar::ListVars (const char *filter, bool plain)
1660 {
1661 	FBaseCVar *var = CVars;
1662 	int count = 0;
1663 
1664 	while (var)
1665 	{
1666 		if (CheckWildcards (filter, var->GetName()))
1667 		{
1668 			DWORD flags = var->GetFlags();
1669 			if (plain)
1670 			{ // plain formatting does not include user-defined cvars
1671 				if (!(flags & CVAR_UNSETTABLE))
1672 				{
1673 					++count;
1674 					Printf ("%s : %s\n", var->GetName(), var->GetGenericRep(CVAR_String).String);
1675 				}
1676 			}
1677 			else
1678 			{
1679 				++count;
1680 				Printf ("%c%c%c%c%c %s = %s\n",
1681 					flags & CVAR_ARCHIVE ? 'A' : ' ',
1682 					flags & CVAR_USERINFO ? 'U' :
1683 						flags & CVAR_SERVERINFO ? 'S' :
1684 						flags & CVAR_AUTO ? 'C' : ' ',
1685 					flags & CVAR_NOSET ? '-' :
1686 						flags & CVAR_LATCH ? 'L' :
1687 						flags & CVAR_UNSETTABLE ? '*' : ' ',
1688 					flags & CVAR_MOD ? 'M' : ' ',
1689 					flags & CVAR_IGNORE ? 'X' : ' ',
1690 					var->GetName(),
1691 					var->GetGenericRep(CVAR_String).String);
1692 			}
1693 		}
1694 		var = var->m_Next;
1695 	}
1696 	Printf ("%d cvars\n", count);
1697 }
1698 
CCMD(cvarlist)1699 CCMD (cvarlist)
1700 {
1701 	if (argv.argc() == 1)
1702 	{
1703 		FBaseCVar::ListVars (NULL, false);
1704 	}
1705 	else
1706 	{
1707 		FBaseCVar::ListVars (argv[1], false);
1708 	}
1709 }
1710 
CCMD(cvarlistplain)1711 CCMD (cvarlistplain)
1712 {
1713 	FBaseCVar::ListVars (NULL, true);
1714 }
1715 
CCMD(archivecvar)1716 CCMD (archivecvar)
1717 {
1718 
1719 	if (argv.argc() == 1)
1720 	{
1721 		Printf ("Usage: archivecvar <cvar>\n");
1722 	}
1723 	else
1724 	{
1725 		FBaseCVar *var = FindCVar (argv[1], NULL);
1726 
1727 		if (var != NULL && (var->GetFlags() & CVAR_AUTO))
1728 		{
1729 			var->SetArchiveBit ();
1730 		}
1731 	}
1732 }
1733