1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 
19 
20 */
21 // cvar.c -- dynamic variable tracking
22 
23 #include "qwsvdef.h"
24 
25 
26 static cvar_t	*cvar_hash[32];
27 static cvar_t	*cvar_vars;
28 static char	*cvar_null_string = "";
29 
30 /*
31 ============
32 Cvar_Next
33 
34 Use this to walk through all vars.
35 ============
36 */
Cvar_Next(cvar_t * var)37 cvar_t *Cvar_Next (cvar_t *var)
38 {
39 	if (var)
40 		return var->next;
41 	else
42 		return cvar_vars;
43 }
44 
45 /*
46 ============
47 Cvar_GetFlags
48 ============
49 */
Cvar_GetFlags(cvar_t * var)50 int Cvar_GetFlags (cvar_t *var)
51 {
52 	return var->flags;
53 }
54 
55 /*
56 ============
57 Cvar_Find
58 ============
59 */
Cvar_Find(const char * var_name)60 cvar_t *Cvar_Find (const char *var_name)
61 {
62 	cvar_t *var;
63 	int key;
64 
65 	key = Com_HashKey (var_name);
66 
67 	for (var=cvar_hash[key] ; var ; var=var->hash_next)
68 		if (!strcasecmp (var_name, var->name))
69 			return var;
70 
71 	return NULL;
72 }
73 
74 /*
75 ============
76 Cvar_Value
77 ============
78 */
Cvar_Value(const char * var_name)79 float Cvar_Value (const char *var_name)
80 {
81 	cvar_t	*var;
82 
83 	var = Cvar_Find (var_name);
84 	if (!var)
85 		return 0;
86 	return Q_atof (var->string);
87 }
88 
89 
90 /*
91 ============
92 Cvar_String
93 ============
94 */
Cvar_String(const char * var_name)95 char *Cvar_String (const char *var_name)
96 {
97 	cvar_t *var;
98 
99 	var = Cvar_Find (var_name);
100 	if (!var)
101 		return cvar_null_string;
102 	return var->string;
103 }
104 
105 void SV_SendServerInfoChange(char *key, char *value);
106 
Cvar_ServerInfoValue(char * key,char * value)107 char* Cvar_ServerInfoValue(char* key, char* value)
108 {
109 	// force serverinfo "0" vars to be "".
110 	// meag: deathmatch is a special case as clients default 'not in serverinfo' to non-coop
111 	if (!strcmp(value, "0") && strcmp(key, "deathmatch")) {
112 		return "";
113 	}
114 	return value;
115 }
116 
117 /*
118 ============
119 Cvar_Set
120 ============
121 */
Cvar_Set(cvar_t * var,char * value)122 void Cvar_Set (cvar_t *var, char *value)
123 {
124 	static qbool changing = false;
125 	char *tmp;
126 
127 	if (!var)
128 		return;
129 
130 	// force serverinfo "0" vars to be "".
131 	if ((var->flags & CVAR_SERVERINFO) && !strcmp(value, "0"))
132 		value = "";
133 
134 	if (var->flags & CVAR_ROM)
135 		return;
136 
137 	if (var->OnChange && !changing)
138 	{
139 		qbool cancel = false;
140 
141 		changing = true;
142 		var->OnChange(var, value, &cancel);
143 		changing = false;
144 
145 		if (cancel)
146 			return; // change rejected.
147 	}
148 
149 	// dup string first (before free) since 'value' and 'var->string' can point at the same memory area.
150 	tmp = Q_strdup(value);
151 	// free the old value string
152 	Q_free (var->string);
153 	// assign new value.
154 	var->string = tmp;
155 	var->value = Q_atof (var->string);
156 
157 	if (var->flags & CVAR_SERVERINFO)
158 	{
159 		SV_ServerinfoChanged (var->name, var->string);
160 	}
161 }
162 
163 /*
164 ============
165 Cvar_SetROM
166 ============
167 */
Cvar_SetROM(cvar_t * var,char * value)168 void Cvar_SetROM (cvar_t *var, char *value)
169 {
170 	int saved_flags;
171 
172 	if (!var)
173 		return;
174 
175 	saved_flags = var->flags;
176 	var->flags &= ~CVAR_ROM;
177 	Cvar_Set (var, value);
178 	var->flags = saved_flags;
179 }
180 
181 /*
182 ============
183 Cvar_SetByName
184 ============
185 */
Cvar_SetByName(const char * var_name,char * value)186 void Cvar_SetByName (const char *var_name, char *value)
187 {
188 	cvar_t	*var;
189 
190 	var = Cvar_Find (var_name);
191 	if (!var)
192 	{	// there is an error in C code if this happens
193 		Con_DPrintf ("Cvar_Set: variable %s not found\n", var_name);
194 		return;
195 	}
196 
197 	Cvar_Set (var, value);
198 }
199 
200 /*
201 ============
202 Cvar_SetValue
203 ============
204 */
Cvar_SetValue(cvar_t * var,const float value)205 void Cvar_SetValue (cvar_t *var, const float value)
206 {
207 	char val[32];
208 	int	i;
209 
210 	snprintf (val, sizeof (val), "%f", value);
211 	for (i = strlen (val) - 1; i > 0 && val[i]=='0'; i--)
212 		val[i] = 0;
213 
214 	if (val[i] == '.')
215 		val[i] = 0;
216 
217 	Cvar_Set (var, val);
218 }
219 
220 /*
221 ============
222 Cvar_SetValueByName
223 ============
224 */
Cvar_SetValueByName(const char * var_name,const float value)225 void Cvar_SetValueByName (const char *var_name, const float value)
226 {
227 	char val[32];
228 
229 	snprintf (val, sizeof (val), "%.8g", value);
230 	Cvar_SetByName (var_name, val);
231 }
232 
233 /*
234 ============
235 Cvar_Register
236 
237 Adds a freestanding variable to the variable list.
238 ============
239 */
Cvar_Register(cvar_t * variable)240 void Cvar_Register (cvar_t *variable)
241 {
242 	char	*value;
243 	int		key;
244 
245 	// first check to see if it has already been defined
246 	if (Cvar_Find (variable->name))
247 	{
248 		Con_Printf ("Can't register variable %s, already defined\n", variable->name);
249 		return;
250 	}
251 
252 	// check for overlap with a command
253 	if (Cmd_Exists (variable->name))
254 	{
255 		Con_Printf ("Cvar_Register: %s is a command\n", variable->name);
256 		return;
257 	}
258 
259 	// link the variable in
260 	key = Com_HashKey (variable->name);
261 	variable->hash_next = cvar_hash[key];
262 	cvar_hash[key] = variable;
263 	variable->next = cvar_vars;
264 	cvar_vars = variable;
265 
266 	// set it through the function to be consistent
267 	value = variable->string;
268 	variable->string = Q_strdup("");
269 	Cvar_SetROM (variable, value);
270 }
271 
272 /*
273 ============
274 Cvar_Command
275 
276 Handles variable inspection and changing from the console
277 ============
278 */
Cvar_Command(void)279 qbool Cvar_Command (void)
280 {
281 	int		i, c;
282 	cvar_t		*v;
283 	char		string[1024];
284 
285 	// check variables
286 	v = Cvar_Find (Cmd_Argv(0));
287 	if (!v)
288 		return false;
289 
290 	// perform a variable print or set
291 	c = Cmd_Argc();
292 	if (c == 1)
293 	{
294 		Con_Printf ("\"%s\" is \"%s\"\n", v->name, v->string);
295 		return true;
296 	}
297 
298 	string[0] = 0;
299 	for (i=1 ; i < c ; i++)
300 	{
301 		if (i > 1)
302 			strlcat (string, " ", sizeof(string));
303 		strlcat (string, Cmd_Argv(i), sizeof(string));
304 	}
305 
306 	Cvar_Set (v, string);
307 	return true;
308 }
309 
310 /*
311 =============
312 Cvar_Toggle_f
313 =============
314 */
Cvar_Toggle_f(void)315 void Cvar_Toggle_f (void)
316 {
317 	cvar_t *var;
318 
319 	if (Cmd_Argc() != 2)
320 	{
321 		Con_Printf ("toggle <cvar> : toggle a cvar on/off\n");
322 		return;
323 	}
324 
325 	var = Cvar_Find (Cmd_Argv(1));
326 	if (!var)
327 	{
328 		Con_Printf ("Unknown variable \"%s\"\n", Cmd_Argv(1));
329 		return;
330 	}
331 
332 	Cvar_Set (var, var->value ? "0" : "1");
333 }
334 
335 /*
336 ===============
337 Cvar_CvarList_f
338 ===============
339 List all cvars
340 TODO: allow cvar name mask as a parameter, e.g. cvarlist cl_*
341 */
342 
Cvar_CvarCompare(const void * p1,const void * p2)343 static int Cvar_CvarCompare (const void *p1, const void *p2)
344 {
345 	return strcmp ((*((cvar_t **) p1))->name, (*((cvar_t **) p2))->name);
346 }
347 
Cvar_CvarList_f(void)348 static void Cvar_CvarList_f(void)
349 {
350 	cvar_t **sorted_cvars = NULL;
351 	cvar_t *var;
352 	const char *pattern;
353 	int i = 0;
354 	int num_cvars = 0;
355 	int pattern_matched = 0;
356 
357 	pattern = (Cmd_Argc() > 1) ? Cmd_Argv(1) : NULL;
358 
359 	for (var = cvar_vars; var; var = var->next) {
360 		num_cvars++;
361 	}
362 
363 	if (num_cvars > 0) {
364 		sorted_cvars = malloc(num_cvars * sizeof(cvar_t*));
365 		if (!sorted_cvars) {
366 			Sys_Error("Failed to allocate memory");
367 		}
368 	}
369 	else {
370 		Con_Printf("No cvars found\n");
371 		return;
372 	}
373 
374 	for (var = cvar_vars; var; var = var->next) {
375 		sorted_cvars[i++] = var;
376 	}
377 	qsort(sorted_cvars, num_cvars, sizeof(cvar_t *), Cvar_CvarCompare);
378 
379 	Con_Printf("List of cvars:\n");
380 	for (i = 0; i < num_cvars; i++) {
381 		var = sorted_cvars[i];
382 		if (pattern && !Q_glob_match(pattern, var->name)) {
383 			continue;
384 		}
385 
386 		Con_Printf("%c %s %s\n", var->flags & CVAR_SERVERINFO ? 's' : ' ', var->name, var->string);
387 		pattern_matched++;
388 	}
389 
390 	Con_Printf("------------\n%d/%d %svariables\n", pattern_matched, num_cvars, (pattern) ? "matching " : "");
391 	free(sorted_cvars);
392 }
393 
394 /*
395 ===========
396 Cvar_Create
397 ===========
398 */
Cvar_Create(const char * name,char * string,int cvarflags)399 cvar_t *Cvar_Create (const char *name, char *string, int cvarflags)
400 {
401 	cvar_t		*v;
402 	int			key;
403 
404 	v = Cvar_Find(name);
405 	if (v)
406 		return v;
407 	v = (cvar_t *) Q_malloc (sizeof(cvar_t));
408 	// Cvar doesn't exist, so we create it
409 	v->next = cvar_vars;
410 	cvar_vars = v;
411 
412 	key = Com_HashKey (name);
413 	v->hash_next = cvar_hash[key];
414 	cvar_hash[key] = v;
415 
416 	v->name = (char *) Q_strdup (name);
417 	v->string = (char *) Q_strdup (string);
418 	v->flags = cvarflags;
419 	v->value = Q_atof (v->string);
420 	v->OnChange = NULL;
421 
422 	return v;
423 }
424 
425 /*
426 ===========
427 Cvar_Delete
428 ===========
429 returns true if the cvar was found (and deleted)
430 */
Cvar_Delete(const char * name)431 qbool Cvar_Delete (const char *name)
432 {
433 	cvar_t	*var, *prev;
434 	int		key;
435 
436 	key = Com_HashKey (name);
437 
438 	prev = NULL;
439 	for (var = cvar_hash[key] ; var ; var=var->hash_next)
440 	{
441 		if (!strcasecmp(var->name, name))
442 		{
443 			// unlink from hash
444 			if (prev)
445 				prev->hash_next = var->next;
446 			else
447 				cvar_hash[key] = var->next;
448 			break;
449 		}
450 		prev = var;
451 	}
452 
453 	if (!var)
454 		return false;
455 
456 	prev = NULL;
457 	for (var = cvar_vars ; var ; var=var->next)
458 	{
459 		if (!strcasecmp(var->name, name))
460 		{
461 			// unlink from cvar list
462 			if (prev)
463 				prev->next = var->next;
464 			else
465 				cvar_vars = var->next;
466 
467 			// free
468 			Q_free (var->string);
469 			Q_free (var->name);
470 			Q_free (var);
471 			return true;
472 		}
473 		prev = var;
474 	}
475 
476 	Sys_Error ("Cvar list broken");
477 	return false;	// shut up compiler
478 }
479 
480 //DP_CON_SET
Cvar_Set_f(void)481 void Cvar_Set_f (void)
482 {
483 	cvar_t *var;
484 	char *var_name;
485 
486 	if (Cmd_Argc() != 3)
487 	{
488 		Con_Printf ("usage: set <cvar> <value>\n");
489 		return;
490 	}
491 
492 	var_name = Cmd_Argv (1);
493 	var = Cvar_Find (var_name);
494 
495 	if (var)
496 	{
497 		Cvar_Set (var, Cmd_Argv(2));
498 	}
499 	else
500 	{
501 		if (Cmd_Exists(var_name))
502 		{
503 			Con_Printf ("\"%s\" is a command\n", var_name);
504 			return;
505 		}
506 
507 #if 0
508 		// delete alias with the same name if it exists
509 		Cmd_DeleteAlias (var_name);
510 #endif
511 
512 		var = Cvar_Create (var_name, Cmd_Argv(2), CVAR_USER_CREATED);
513 	}
514 }
515 
Cvar_Inc_f(void)516 void Cvar_Inc_f (void)
517 {
518 	int		c;
519 	cvar_t	*var;
520 	float	delta;
521 
522 	c = Cmd_Argc();
523 	if (c != 2 && c != 3)
524 	{
525 		Con_Printf ("inc <cvar> [value]\n");
526 		return;
527 	}
528 
529 	var = Cvar_Find (Cmd_Argv(1));
530 	if (!var)
531 	{
532 		Con_Printf ("Unknown variable \"%s\"\n", Cmd_Argv(1));
533 		return;
534 	}
535 
536 	if (c == 3)
537 		delta = Q_atof (Cmd_Argv(2));
538 	else
539 		delta = 1;
540 
541 	Cvar_SetValue (var, var->value + delta);
542 }
543 
544 //#define CVAR_DEBUG
545 #ifdef CVAR_DEBUG
Cvar_Hash_Print_f(void)546 static void Cvar_Hash_Print_f (void)
547 {
548 	int		i, count;
549 	cvar_t	*cvar;
550 
551 	Con_Printf ("Cvar hash:\n");
552 	for (i = 0; i<32; i++)
553 	{
554 		count = 0;
555 		for (cvar = cvar_hash[i]; cvar; cvar=cvar->hash_next, count++);
556 		Con_Printf ("%i: %i\n", i, count);
557 	}
558 
559 }
560 #endif
561 
Cvar_Init(void)562 void Cvar_Init (void)
563 {
564 	Cmd_AddCommand ("cvarlist", Cvar_CvarList_f);
565 	Cmd_AddCommand ("cvardump", Cvar_CvarList_f);
566 	Cmd_AddCommand ("toggle", Cvar_Toggle_f);
567 	Cmd_AddCommand ("set", Cvar_Set_f); //DP_CON_SET
568 	Cmd_AddCommand ("inc", Cvar_Inc_f);
569 
570 #ifdef CVAR_DEBUG
571 	Cmd_AddCommand ("cvar_hash_print", Cvar_Hash_Print_f);
572 #endif
573 }
574