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