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