1 /*
2 Copyright (C) 1996-2001 Id Software, Inc.
3 Copyright (C) 2002-2009 John Fitzgibbons and others
4 Copyright (C) 2010-2014 QuakeSpasm developers
5 
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 
15 See the GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 
21 */
22 // cvar.c -- dynamic variable tracking
23 
24 #include "quakedef.h"
25 
26 static cvar_t	*cvar_vars;
27 static char	cvar_null_string[] = "";
28 
29 //==============================================================================
30 //
31 //  USER COMMANDS
32 //
33 //==============================================================================
34 
35 void Cvar_Reset (const char *name); //johnfitz
36 
37 /*
38 ============
39 Cvar_List_f -- johnfitz
40 ============
41 */
Cvar_List_f(void)42 void Cvar_List_f (void)
43 {
44 	cvar_t	*cvar;
45 	const char 	*partial;
46 	int		len, count;
47 
48 	if (Cmd_Argc() > 1)
49 	{
50 		partial = Cmd_Argv (1);
51 		len = Q_strlen(partial);
52 	}
53 	else
54 	{
55 		partial = NULL;
56 		len = 0;
57 	}
58 
59 	count = 0;
60 	for (cvar = cvar_vars ; cvar ; cvar = cvar->next)
61 	{
62 		if (partial && Q_strncmp(partial, cvar->name, len))
63 		{
64 			continue;
65 		}
66 		Con_SafePrintf ("%s%s %s \"%s\"\n",
67 			(cvar->flags & CVAR_ARCHIVE) ? "*" : " ",
68 			(cvar->flags & CVAR_NOTIFY)  ? "s" : " ",
69 			cvar->name,
70 			cvar->string);
71 		count++;
72 	}
73 
74 	Con_SafePrintf ("%i cvars", count);
75 	if (partial)
76 	{
77 		Con_SafePrintf (" beginning with \"%s\"", partial);
78 	}
79 	Con_SafePrintf ("\n");
80 }
81 
82 /*
83 ============
84 Cvar_Inc_f -- johnfitz
85 ============
86 */
Cvar_Inc_f(void)87 void Cvar_Inc_f (void)
88 {
89 	switch (Cmd_Argc())
90 	{
91 	default:
92 	case 1:
93 		Con_Printf("inc <cvar> [amount] : increment cvar\n");
94 		break;
95 	case 2:
96 		Cvar_SetValue (Cmd_Argv(1), Cvar_VariableValue(Cmd_Argv(1)) + 1);
97 		break;
98 	case 3:
99 		Cvar_SetValue (Cmd_Argv(1), Cvar_VariableValue(Cmd_Argv(1)) + Q_atof(Cmd_Argv(2)));
100 		break;
101 	}
102 }
103 
104 /*
105 ============
106 Cvar_Toggle_f -- johnfitz
107 ============
108 */
Cvar_Toggle_f(void)109 void Cvar_Toggle_f (void)
110 {
111 	switch (Cmd_Argc())
112 	{
113 	default:
114 	case 1:
115 		Con_Printf("toggle <cvar> : toggle cvar\n");
116 		break;
117 	case 2:
118 		if (Cvar_VariableValue(Cmd_Argv(1)))
119 			Cvar_Set (Cmd_Argv(1), "0");
120 		else
121 			Cvar_Set (Cmd_Argv(1), "1");
122 		break;
123 	}
124 }
125 
126 /*
127 ============
128 Cvar_Cycle_f -- johnfitz
129 ============
130 */
Cvar_Cycle_f(void)131 void Cvar_Cycle_f (void)
132 {
133 	int i;
134 
135 	if (Cmd_Argc() < 3)
136 	{
137 		Con_Printf("cycle <cvar> <value list>: cycle cvar through a list of values\n");
138 		return;
139 	}
140 
141 	//loop through the args until you find one that matches the current cvar value.
142 	//yes, this will get stuck on a list that contains the same value twice.
143 	//it's not worth dealing with, and i'm not even sure it can be dealt with.
144 	for (i = 2; i < Cmd_Argc(); i++)
145 	{
146 		//zero is assumed to be a string, even though it could actually be zero.  The worst case
147 		//is that the first time you call this command, it won't match on zero when it should, but after that,
148 		//it will be comparing strings that all had the same source (the user) so it will work.
149 		if (Q_atof(Cmd_Argv(i)) == 0)
150 		{
151 			if (!strcmp(Cmd_Argv(i), Cvar_VariableString(Cmd_Argv(1))))
152 				break;
153 		}
154 		else
155 		{
156 			if (Q_atof(Cmd_Argv(i)) == Cvar_VariableValue(Cmd_Argv(1)))
157 				break;
158 		}
159 	}
160 
161 	if (i == Cmd_Argc())
162 		Cvar_Set (Cmd_Argv(1), Cmd_Argv(2)); // no match
163 	else if (i + 1 == Cmd_Argc())
164 		Cvar_Set (Cmd_Argv(1), Cmd_Argv(2)); // matched last value in list
165 	else
166 		Cvar_Set (Cmd_Argv(1), Cmd_Argv(i+1)); // matched earlier in list
167 }
168 
169 /*
170 ============
171 Cvar_Reset_f -- johnfitz
172 ============
173 */
Cvar_Reset_f(void)174 void Cvar_Reset_f (void)
175 {
176 	switch (Cmd_Argc())
177 	{
178 	default:
179 	case 1:
180 		Con_Printf ("reset <cvar> : reset cvar to default\n");
181 		break;
182 	case 2:
183 		Cvar_Reset (Cmd_Argv(1));
184 		break;
185 	}
186 }
187 
188 /*
189 ============
190 Cvar_ResetAll_f -- johnfitz
191 ============
192 */
Cvar_ResetAll_f(void)193 void Cvar_ResetAll_f (void)
194 {
195 	cvar_t	*var;
196 
197 	for (var = cvar_vars ; var ; var = var->next)
198 		Cvar_Reset (var->name);
199 }
200 
201 /*
202 ============
203 Cvar_ResetCfg_f -- QuakeSpasm
204 ============
205 */
Cvar_ResetCfg_f(void)206 void Cvar_ResetCfg_f (void)
207 {
208 	cvar_t	*var;
209 
210 	for (var = cvar_vars ; var ; var = var->next)
211 		if (var->flags & CVAR_ARCHIVE) Cvar_Reset (var->name);
212 }
213 
214 //==============================================================================
215 //
216 //  INIT
217 //
218 //==============================================================================
219 
220 /*
221 ============
222 Cvar_Init -- johnfitz
223 ============
224 */
225 
Cvar_Init(void)226 void Cvar_Init (void)
227 {
228 	Cmd_AddCommand ("cvarlist", Cvar_List_f);
229 	Cmd_AddCommand ("toggle", Cvar_Toggle_f);
230 	Cmd_AddCommand ("cycle", Cvar_Cycle_f);
231 	Cmd_AddCommand ("inc", Cvar_Inc_f);
232 	Cmd_AddCommand ("reset", Cvar_Reset_f);
233 	Cmd_AddCommand ("resetall", Cvar_ResetAll_f);
234 	Cmd_AddCommand ("resetcfg", Cvar_ResetCfg_f);
235 }
236 
237 //==============================================================================
238 //
239 //  CVAR FUNCTIONS
240 //
241 //==============================================================================
242 
243 /*
244 ============
245 Cvar_FindVar
246 ============
247 */
Cvar_FindVar(const char * var_name)248 cvar_t *Cvar_FindVar (const char *var_name)
249 {
250 	cvar_t	*var;
251 
252 	for (var = cvar_vars ; var ; var = var->next)
253 	{
254 		if (!Q_strcmp(var_name, var->name))
255 			return var;
256 	}
257 
258 	return NULL;
259 }
260 
Cvar_FindVarAfter(const char * prev_name,unsigned int with_flags)261 cvar_t *Cvar_FindVarAfter (const char *prev_name, unsigned int with_flags)
262 {
263 	cvar_t	*var;
264 
265 	if (*prev_name)
266 	{
267 		var = Cvar_FindVar (prev_name);
268 		if (!var)
269 			return NULL;
270 		var = var->next;
271 	}
272 	else
273 		var = cvar_vars;
274 
275 	// search for the next cvar matching the needed flags
276 	while (var)
277 	{
278 		if ((var->flags & with_flags) || !with_flags)
279 			break;
280 		var = var->next;
281 	}
282 	return var;
283 }
284 
285 /*
286 ============
287 Cvar_LockVar
288 ============
289 */
Cvar_LockVar(const char * var_name)290 void Cvar_LockVar (const char *var_name)
291 {
292 	cvar_t	*var = Cvar_FindVar (var_name);
293 	if (var)
294 		var->flags |= CVAR_LOCKED;
295 }
296 
Cvar_UnlockVar(const char * var_name)297 void Cvar_UnlockVar (const char *var_name)
298 {
299 	cvar_t	*var = Cvar_FindVar (var_name);
300 	if (var)
301 		var->flags &= ~CVAR_LOCKED;
302 }
303 
Cvar_UnlockAll(void)304 void Cvar_UnlockAll (void)
305 {
306 	cvar_t	*var;
307 
308 	for (var = cvar_vars ; var ; var = var->next)
309 	{
310 		var->flags &= ~CVAR_LOCKED;
311 	}
312 }
313 
314 /*
315 ============
316 Cvar_VariableValue
317 ============
318 */
Cvar_VariableValue(const char * var_name)319 float	Cvar_VariableValue (const char *var_name)
320 {
321 	cvar_t	*var;
322 
323 	var = Cvar_FindVar (var_name);
324 	if (!var)
325 		return 0;
326 	return Q_atof (var->string);
327 }
328 
329 
330 /*
331 ============
332 Cvar_VariableString
333 ============
334 */
Cvar_VariableString(const char * var_name)335 const char *Cvar_VariableString (const char *var_name)
336 {
337 	cvar_t *var;
338 
339 	var = Cvar_FindVar (var_name);
340 	if (!var)
341 		return cvar_null_string;
342 	return var->string;
343 }
344 
345 
346 /*
347 ============
348 Cvar_CompleteVariable
349 ============
350 */
Cvar_CompleteVariable(const char * partial)351 const char *Cvar_CompleteVariable (const char *partial)
352 {
353 	cvar_t	*cvar;
354 	int	len;
355 
356 	len = Q_strlen(partial);
357 	if (!len)
358 		return NULL;
359 
360 // check functions
361 	for (cvar = cvar_vars ; cvar ; cvar = cvar->next)
362 	{
363 		if (!Q_strncmp(partial, cvar->name, len))
364 			return cvar->name;
365 	}
366 
367 	return NULL;
368 }
369 
370 /*
371 ============
372 Cvar_Reset -- johnfitz
373 ============
374 */
Cvar_Reset(const char * name)375 void Cvar_Reset (const char *name)
376 {
377 	cvar_t	*var;
378 
379 	var = Cvar_FindVar (name);
380 	if (!var)
381 		Con_Printf ("variable \"%s\" not found\n", name);
382 	else
383 		Cvar_SetQuick (var, var->default_string);
384 }
385 
Cvar_SetQuick(cvar_t * var,const char * value)386 void Cvar_SetQuick (cvar_t *var, const char *value)
387 {
388 	if (var->flags & (CVAR_ROM|CVAR_LOCKED))
389 		return;
390 	if (!(var->flags & CVAR_REGISTERED))
391 		return;
392 
393 	if (!var->string)
394 		var->string = Z_Strdup (value);
395 	else
396 	{
397 		int	len;
398 
399 		if (!strcmp(var->string, value))
400 			return;	// no change
401 
402 		var->flags |= CVAR_CHANGED;
403 		len = Q_strlen (value);
404 		if (len != Q_strlen(var->string))
405 		{
406 			Z_Free ((void *)var->string);
407 			var->string = (char *) Z_Malloc (len + 1);
408 		}
409 		memcpy ((char *)var->string, value, len + 1);
410 	}
411 
412 	var->value = Q_atof (var->string);
413 
414 	//johnfitz -- save initial value for "reset" command
415 	if (!var->default_string)
416 		var->default_string = Z_Strdup (var->string);
417 	//johnfitz -- during initialization, update default too
418 	else if (!host_initialized)
419 	{
420 	//	Sys_Printf("changing default of %s: %s -> %s\n",
421 	//		   var->name, var->default_string, var->string);
422 		Z_Free ((void *)var->default_string);
423 		var->default_string = Z_Strdup (var->string);
424 	}
425 	//johnfitz
426 
427 	if (var->callback)
428 		var->callback (var);
429 }
430 
Cvar_SetValueQuick(cvar_t * var,const float value)431 void Cvar_SetValueQuick (cvar_t *var, const float value)
432 {
433 	char	val[32], *ptr = val;
434 
435 	if (value == (float)((int)value))
436 		q_snprintf (val, sizeof(val), "%i", (int)value);
437 	else
438 	{
439 		q_snprintf (val, sizeof(val), "%f", value);
440 		// kill trailing zeroes
441 		while (*ptr)
442 			ptr++;
443 		while (--ptr > val && *ptr == '0' && ptr[-1] != '.')
444 			*ptr = '\0';
445 	}
446 
447 	Cvar_SetQuick (var, val);
448 }
449 
450 /*
451 ============
452 Cvar_Set
453 ============
454 */
Cvar_Set(const char * var_name,const char * value)455 void Cvar_Set (const char *var_name, const char *value)
456 {
457 	cvar_t		*var;
458 
459 	var = Cvar_FindVar (var_name);
460 	if (!var)
461 	{	// there is an error in C code if this happens
462 		Con_Printf ("Cvar_Set: variable %s not found\n", var_name);
463 		return;
464 	}
465 
466 	Cvar_SetQuick (var, value);
467 }
468 
469 /*
470 ============
471 Cvar_SetValue
472 ============
473 */
Cvar_SetValue(const char * var_name,const float value)474 void Cvar_SetValue (const char *var_name, const float value)
475 {
476 	char	val[32], *ptr = val;
477 
478 	if (value == (float)((int)value))
479 		q_snprintf (val, sizeof(val), "%i", (int)value);
480 	else
481 	{
482 		q_snprintf (val, sizeof(val), "%f", value);
483 		// kill trailing zeroes
484 		while (*ptr)
485 			ptr++;
486 		while (--ptr > val && *ptr == '0' && ptr[-1] != '.')
487 			*ptr = '\0';
488 	}
489 
490 	Cvar_Set (var_name, val);
491 }
492 
493 /*
494 ============
495 Cvar_SetROM
496 ============
497 */
Cvar_SetROM(const char * var_name,const char * value)498 void Cvar_SetROM (const char *var_name, const char *value)
499 {
500 	cvar_t *var = Cvar_FindVar (var_name);
501 	if (var)
502 	{
503 		var->flags &= ~CVAR_ROM;
504 		Cvar_SetQuick (var, value);
505 		var->flags |= CVAR_ROM;
506 	}
507 }
508 
509 /*
510 ============
511 Cvar_SetValueROM
512 ============
513 */
Cvar_SetValueROM(const char * var_name,const float value)514 void Cvar_SetValueROM (const char *var_name, const float value)
515 {
516 	cvar_t *var = Cvar_FindVar (var_name);
517 	if (var)
518 	{
519 		var->flags &= ~CVAR_ROM;
520 		Cvar_SetValueQuick (var, value);
521 		var->flags |= CVAR_ROM;
522 	}
523 }
524 
525 /*
526 ============
527 Cvar_RegisterVariable
528 
529 Adds a freestanding variable to the variable list.
530 ============
531 */
Cvar_RegisterVariable(cvar_t * variable)532 void Cvar_RegisterVariable (cvar_t *variable)
533 {
534 	char	value[512];
535 	qboolean	set_rom;
536 	cvar_t	*cursor,*prev; //johnfitz -- sorted list insert
537 
538 // first check to see if it has already been defined
539 	if (Cvar_FindVar (variable->name))
540 	{
541 		Con_Printf ("Can't register variable %s, already defined\n", variable->name);
542 		return;
543 	}
544 
545 // check for overlap with a command
546 	if (Cmd_Exists (variable->name))
547 	{
548 		Con_Printf ("Cvar_RegisterVariable: %s is a command\n", variable->name);
549 		return;
550 	}
551 
552 // link the variable in
553 	//johnfitz -- insert each entry in alphabetical order
554 	if (cvar_vars == NULL ||
555 	    strcmp(variable->name, cvar_vars->name) < 0) // insert at front
556 	{
557 		variable->next = cvar_vars;
558 		cvar_vars = variable;
559 	}
560 	else //insert later
561 	{
562 		prev = cvar_vars;
563 		cursor = cvar_vars->next;
564 		while (cursor && (strcmp(variable->name, cursor->name) > 0))
565 		{
566 			prev = cursor;
567 			cursor = cursor->next;
568 		}
569 		variable->next = prev->next;
570 		prev->next = variable;
571 	}
572 	//johnfitz
573 	variable->flags |= CVAR_REGISTERED;
574 
575 // copy the value off, because future sets will Z_Free it
576 	q_strlcpy (value, variable->string, sizeof(value));
577 	variable->string = NULL;
578 	variable->default_string = NULL;
579 
580 	if (!(variable->flags & CVAR_CALLBACK))
581 		variable->callback = NULL;
582 
583 // set it through the function to be consistent
584 	set_rom = (variable->flags & CVAR_ROM);
585 	variable->flags &= ~CVAR_ROM;
586 	Cvar_SetQuick (variable, value);
587 	if (set_rom)
588 		variable->flags |= CVAR_ROM;
589 }
590 
591 /*
592 ============
593 Cvar_Create -- spike
594 
595 Creates a cvar if it does not already exist, otherwise does nothing.
596 Must not be used until after all other cvars are registered.
597 Cvar will be persistent.
598 ============
599 */
Cvar_Create(const char * name,const char * value)600 cvar_t *Cvar_Create (const char *name, const char *value)
601 {
602 	cvar_t *newvar;
603 	newvar = Cvar_FindVar(name);
604 	if (newvar)
605 		return newvar;	//already exists.
606 	if (Cmd_Exists (name))
607 		return NULL;	//error! panic! oh noes!
608 
609 	newvar = Z_Malloc(sizeof(cvar_t) + strlen(name)+1);
610 	newvar->name = (char*)(newvar+1);
611 	strcpy((char*)(newvar+1), name);
612 	newvar->flags = CVAR_USERDEFINED;
613 
614 	newvar->string = value;
615 	Cvar_RegisterVariable(newvar);
616 	return newvar;
617 }
618 
619 /*
620 ============
621 Cvar_SetCallback
622 
623 Set a callback function to the var
624 ============
625 */
Cvar_SetCallback(cvar_t * var,cvarcallback_t func)626 void Cvar_SetCallback (cvar_t *var, cvarcallback_t func)
627 {
628 	var->callback = func;
629 	if (func)
630 		var->flags |= CVAR_CALLBACK;
631 	else	var->flags &= ~CVAR_CALLBACK;
632 }
633 
634 /*
635 ============
636 Cvar_Command
637 
638 Handles variable inspection and changing from the console
639 ============
640 */
Cvar_Command(void)641 qboolean	Cvar_Command (void)
642 {
643 	cvar_t			*v;
644 
645 // check variables
646 	v = Cvar_FindVar (Cmd_Argv(0));
647 	if (!v)
648 		return false;
649 
650 // perform a variable print or set
651 	if (Cmd_Argc() == 1)
652 	{
653 		Con_Printf ("\"%s\" is \"%s\"\n", v->name, v->string);
654 		return true;
655 	}
656 
657 	Cvar_Set (v->name, Cmd_Argv(1));
658 	return true;
659 }
660 
661 
662 /*
663 ============
664 Cvar_WriteVariables
665 
666 Writes lines containing "set variable value" for all variables
667 with the archive flag set to true.
668 ============
669 */
Cvar_WriteVariables(FILE * f)670 void Cvar_WriteVariables (FILE *f)
671 {
672 	cvar_t	*var;
673 
674 	for (var = cvar_vars ; var ; var = var->next)
675 	{
676 		if (var->flags & CVAR_ARCHIVE)
677 			fprintf (f, "%s \"%s\"\n", var->name, var->string);
678 	}
679 }
680 
681