]> git.xonotic.org Git - xonotic/darkplaces.git/blob - cvar.c
cvar: Use a dedicated mempool in cvar_state_t for all cvars
[xonotic/darkplaces.git] / cvar.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3 Copyright (C) 2000-2021 DarkPlaces contributors
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
14 See the GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19
20 */
21 // cvar.c -- dynamic variable tracking
22
23 #include "quakedef.h"
24
25 const char *cvar_dummy_description = "custom cvar";
26 static const char *cvar_null_string = "";
27
28 cvar_state_t cvars_all;
29 cvar_state_t cvars_null;
30
31 /*
32 ============
33 Cvar_FindVar
34 ============
35 */
36 cvar_t *Cvar_FindVar(cvar_state_t *cvars, const char *var_name, int neededflags)
37 {
38         int hashindex;
39         cvar_t *hash;
40
41         // use hash lookup to minimize search time
42         hashindex = CRC_Block((const unsigned char *)var_name, strlen(var_name)) % CVAR_HASHSIZE;
43         for (hash = cvars->hashtable[hashindex];hash;hash = hash->hnext)
44                 if (!strcmp (var_name, hash->name) && (hash->flags & neededflags))
45                         return hash;
46         return NULL;
47 }
48
49 cvar_t *Cvar_FindVarAfter(cvar_state_t *cvars, const char *prev_var_name, int neededflags)
50 {
51         cvar_t *var;
52
53         if (*prev_var_name)
54         {
55                 var = Cvar_FindVar(cvars, prev_var_name, neededflags);
56                 if (!var)
57                         return NULL;
58                 var = List_Next_Entry(var->parent ? var->parent : var, list);
59         }
60         else
61                 var = cvars->vars;
62
63         // search for the next cvar matching the needed flags
64         List_For_Each_Entry_From(var, &cvars->vars->list, list)
65         {
66                 if (var->parent)
67                         continue;
68                 if (var->flags & neededflags)
69                         break;
70         }
71         return var;
72 }
73
74 /*
75 ============
76 Cvar_VariableValue
77 ============
78 */
79 float Cvar_VariableValueOr(cvar_state_t *cvars, const char *var_name, float def, int neededflags)
80 {
81         cvar_t *var;
82
83         var = Cvar_FindVar(cvars, var_name, neededflags);
84         if (!var)
85                 return def;
86         return atof (var->parent ? var->parent->string : var->string);
87 }
88
89 float Cvar_VariableValue(cvar_state_t *cvars, const char *var_name, int neededflags)
90 {
91         return Cvar_VariableValueOr(cvars, var_name, 0, neededflags);
92 }
93
94 /*
95 ============
96 Cvar_VariableString
97 ============
98 */
99 const char *Cvar_VariableStringOr(cvar_state_t *cvars, const char *var_name, const char *def, int neededflags)
100 {
101         cvar_t *var;
102
103         var = Cvar_FindVar(cvars, var_name, neededflags);
104         if (!var)
105                 return def;
106         return var->parent ? var->parent->string : var->string;
107 }
108
109 const char *Cvar_VariableString(cvar_state_t *cvars, const char *var_name, int neededflags)
110 {
111         return Cvar_VariableStringOr(cvars, var_name, cvar_null_string, neededflags);
112 }
113
114 /*
115 ============
116 Cvar_VariableDefString
117 ============
118 */
119 const char *Cvar_VariableDefString(cvar_state_t *cvars, const char *var_name, int neededflags)
120 {
121         cvar_t *var;
122
123         var = Cvar_FindVar(cvars, var_name, neededflags);
124         if (!var)
125                 return cvar_null_string;
126         return var->parent ? var->parent->defstring : var->defstring;
127 }
128
129 /*
130 ============
131 Cvar_VariableDescription
132 ============
133 */
134 const char *Cvar_VariableDescription(cvar_state_t *cvars, const char *var_name, int neededflags)
135 {
136         cvar_t *var;
137
138         var = Cvar_FindVar(cvars, var_name, neededflags);
139         if (!var)
140                 return cvar_null_string;
141         return var->description;
142 }
143
144
145 /*
146 ============
147 Cvar_CompleteVariable
148 ============
149 */
150 const char *Cvar_CompleteVariable(cvar_state_t *cvars, const char *partial, int neededflags)
151 {
152         cvar_t          *cvar;
153         size_t          len;
154
155         len = strlen(partial);
156
157         if (!len)
158                 return NULL;
159
160 // check functions
161         List_For_Each_Entry(cvar, &cvars->vars->list, list)
162                 if (!strncasecmp (partial,cvar->name, len) && (cvar->flags & neededflags))
163                         return cvar->name;
164
165         return NULL;
166 }
167
168 /*
169         CVar_CompleteCountPossible
170
171         New function for tab-completion system
172         Added by EvilTypeGuy
173         Thanks to Fett erich@heintz.com
174
175 */
176 int Cvar_CompleteCountPossible(cvar_state_t *cvars, const char *partial, int neededflags)
177 {
178         cvar_t  *cvar;
179         size_t  len;
180         int             h;
181
182         h = 0;
183         len = strlen(partial);
184
185         if (!len)
186                 return  0;
187
188         // Loop through the cvars and count all possible matches
189         List_For_Each_Entry(cvar, &cvars->vars->list, list)
190                 if (!strncasecmp(partial, cvar->name, len) && (cvar->flags & neededflags))
191                         h++;
192         return h;
193 }
194
195 /*
196         CVar_CompleteBuildList
197
198         New function for tab-completion system
199         Added by EvilTypeGuy
200         Thanks to Fett erich@heintz.com
201         Thanks to taniwha
202
203 */
204 const char **Cvar_CompleteBuildList(cvar_state_t *cvars, const char *partial, int neededflags)
205 {
206         const cvar_t *cvar;
207         size_t len = 0;
208         size_t bpos = 0;
209         size_t sizeofbuf = (Cvar_CompleteCountPossible(cvars, partial, neededflags) + 1) * sizeof(const char *);
210         const char **buf;
211
212         len = strlen(partial);
213         buf = (const char **)Mem_Alloc(tempmempool, sizeofbuf + sizeof(const char *));
214         // Loop through the alias list and print all matches
215         List_For_Each_Entry(cvar, &cvars->vars->list, list)
216                 if (!strncasecmp(partial, cvar->name, len) && (cvar->flags & neededflags))
217                         buf[bpos++] = cvar->name;
218
219         buf[bpos] = NULL;
220         return buf;
221 }
222
223 void Cvar_PrintHelp(cvar_t *cvar, const char *name, qbool full)
224 {
225         // Aliases are purple, cvars are yellow
226         if (strcmp(cvar->name, name))
227                 Con_Printf("^6");
228         else
229                 Con_Printf("^3");
230         Con_Printf("%s^7 is \"%s\" [\"%s\"]", name, ((cvar->flags & CF_PRIVATE) ? "********"/*hunter2*/ : cvar->string), cvar->defstring);
231         if (strcmp(cvar->name, name))
232                 Con_Printf(" (also ^3%s^7)", cvar->name);
233         if (full)
234                 Con_Printf(" %s", cvar->description);
235         Con_Printf("\n");
236 }
237
238 // written by LadyHavoc
239 void Cvar_CompleteCvarPrint(cvar_state_t *cvars, const char *partial, int neededflags)
240 {
241         cvar_t *cvar;
242         size_t len = strlen(partial);
243         // Loop through the command list and print all matches
244         List_For_Each_Entry(cvar, &cvars->vars->list, list)
245                 if (!strncasecmp(partial, cvar->name, len) && (cvar->flags & neededflags))
246                         Cvar_PrintHelp(cvar->parent ? cvar->parent : cvar, cvar->name, true);
247 }
248
249 // check if a cvar is held by some progs
250 static qbool Cvar_IsAutoCvar(cvar_t *var)
251 {
252         int i;
253         prvm_prog_t *prog;
254         for (i = 0;i < PRVM_PROG_MAX;i++)
255         {
256                 prog = &prvm_prog_list[i];
257                 if (prog->loaded && var->globaldefindex[i] >= 0)
258                         return true;
259         }
260         return false;
261 }
262
263 // we assume that prog is already set to the target progs
264 static void Cvar_UpdateAutoCvar(cvar_t *var)
265 {
266         int i;
267         int j;
268         const char *s;
269         vec3_t v;
270         prvm_prog_t *prog;
271         for (i = 0;i < PRVM_PROG_MAX;i++)
272         {
273                 prog = &prvm_prog_list[i];
274                 if (prog->loaded && var->globaldefindex[i] >= 0)
275                 {
276                         // MUST BE SYNCED WITH prvm_edict.c PRVM_LoadProgs
277                         switch(prog->globaldefs[var->globaldefindex[i]].type & ~DEF_SAVEGLOBAL)
278                         {
279                         case ev_float:
280                                 PRVM_GLOBALFIELDFLOAT(prog->globaldefs[var->globaldefindex[i]].ofs) = var->value;
281                                 break;
282                         case ev_vector:
283                                 s = var->string;
284                                 VectorClear(v);
285                                 for (j = 0;j < 3;j++)
286                                 {
287                                         while (*s && ISWHITESPACE(*s))
288                                                 s++;
289                                         if (!*s)
290                                                 break;
291                                         v[j] = atof(s);
292                                         while (!ISWHITESPACE(*s))
293                                                 s++;
294                                         if (!*s)
295                                                 break;
296                                 }
297                                 VectorCopy(v, PRVM_GLOBALFIELDVECTOR(prog->globaldefs[var->globaldefindex[i]].ofs));
298                                 break;
299                         case ev_string:
300                                 PRVM_ChangeEngineString(prog, var->globaldefindex_stringno[i], var->string);
301                                 PRVM_GLOBALFIELDSTRING(prog->globaldefs[var->globaldefindex[i]].ofs) = var->globaldefindex_stringno[i];
302                                 break;
303                         }
304                 }
305         }
306 }
307
308 // called after loading a savegame
309 void Cvar_UpdateAllAutoCvars(cvar_state_t *cvars)
310 {
311         cvar_t *var;
312         List_For_Each_Entry(var, &cvars->vars->list, list)
313         {
314                 if(var->parent)
315                         continue;
316                 Cvar_UpdateAutoCvar(var);
317         }
318 }
319
320 void Cvar_Callback(cvar_t *var)
321 {
322         if (var == NULL)
323         {
324                 Con_Print("Cvar_Callback: var == NULL\n");
325                 return;
326         }
327
328         if(var->callback)
329                 var->callback(var);
330 }
331
332 /*
333 ============
334 Cvar_Set
335 ============
336 */
337 extern cvar_t sv_disablenotify;
338 void Cvar_SetQuick (cvar_t *var, const char *value)
339 {
340         cvar_state_t *cvars = &cvars_all;
341         size_t valuelen;
342
343         if (var == NULL)
344         {
345                 Con_Print("Cvar_SetQuick: var == NULL\n");
346                 return;
347         }
348
349         // Don't reallocate when there is no change
350         if (!strcmp(var->string, value))
351                 return;
352
353         // LadyHavoc: don't reallocate when the buffer is the same size
354         valuelen = strlen(value);
355         if (!var->string || strlen(var->string) != valuelen)
356         {
357                 Mem_Free((char *)var->string);  // free the old value string
358
359                 var->string = (char *)Mem_Alloc (cvars->mempool, valuelen + 1);
360         }
361         memcpy ((char *)var->string, value, valuelen + 1);
362         var->value = atof (var->string);
363         var->integer = (int) var->value;
364         if ((var->flags & CF_NOTIFY) && sv.active && !sv_disablenotify.integer)
365                 SV_BroadcastPrintf("\003^3Server cvar \"%s\" changed to \"%s\"\n", var->name, var->string);
366 #if 0
367         // TODO: add infostring support to the server?
368         if ((var->flags & CF_SERVERINFO) && changed && sv.active)
369         {
370                 InfoString_SetValue(svs.serverinfo, sizeof(svs.serverinfo), var->name, var->string);
371                 if (sv.active)
372                 {
373                         MSG_WriteByte (&sv.reliable_datagram, svc_serverinfostring);
374                         MSG_WriteString (&sv.reliable_datagram, var->name);
375                         MSG_WriteString (&sv.reliable_datagram, var->string);
376                 }
377         }
378 #endif
379         if (var->flags & CF_USERINFO)
380                 CL_SetInfo(var->name, var->string, true, false, false, false);
381
382         Cvar_UpdateAutoCvar(var);
383
384         // Call the function stored in the cvar for bounds checking, cleanup, etc
385         Cvar_Callback(var);
386 }
387
388 void Cvar_Set(cvar_state_t *cvars, const char *var_name, const char *value)
389 {
390         cvar_t *var;
391         var = Cvar_FindVar(cvars, var_name, ~0);
392         if (var == NULL)
393         {
394                 Con_Printf("Cvar_Set: variable %s not found\n", var_name);
395                 return;
396         }
397         Cvar_SetQuick(var->parent ? var->parent : var, value);
398 }
399
400 /*
401 ============
402 Cvar_SetValue
403 ============
404 */
405 void Cvar_SetValueQuick(cvar_t *var, float value)
406 {
407         char val[MAX_INPUTLINE];
408
409         if ((float)((int)value) == value)
410                 dpsnprintf(val, sizeof(val), "%i", (int)value);
411         else
412                 dpsnprintf(val, sizeof(val), "%f", value);
413         Cvar_SetQuick(var, val);
414 }
415
416 void Cvar_SetValue(cvar_state_t *cvars, const char *var_name, float value)
417 {
418         char val[MAX_INPUTLINE];
419
420         if ((float)((int)value) == value)
421                 dpsnprintf(val, sizeof(val), "%i", (int)value);
422         else
423                 dpsnprintf(val, sizeof(val), "%f", value);
424         Cvar_Set(cvars, var_name, val);
425 }
426
427 /*
428 ============
429 Cvar_Link
430
431 Links a variable to the variable list and hashtable
432 ============
433 */
434 static void Cvar_Link(cvar_t *variable, cvar_state_t *cvars)
435 {
436         cvar_t *current;
437         int hashindex;
438
439         // Link the object in alphanumerical order
440         List_For_Each_Entry(current, &cvars->vars->list, list)
441                 if(strcmp(current->name, variable->name) > 0)
442                         break;
443
444         List_Add_Tail(&variable->list, &current->list);
445
446         // link to head of list in this hash table index
447         hashindex = CRC_Block((const unsigned char *)variable->name, strlen(variable->name)) % CVAR_HASHSIZE;
448         variable->hnext = cvars->hashtable[hashindex];
449         cvars->hashtable[hashindex] = variable;
450         variable->hashindex = hashindex;
451 }
452
453 void Cvar_RegisterCallback(cvar_t *variable, void (*callback)(cvar_t *))
454 {
455         if (variable == NULL)
456         {
457                 Con_Print("Cvar_RegisterCallback: var == NULL\n");
458                 return;
459         }
460         variable->callback = callback;
461 }
462
463 static void Cvar_DeleteVirtual(cvar_t *vcvar)
464 {
465         List_Delete(&vcvar->vlist);
466         Mem_Free((char *)vcvar->name);
467         Mem_Free(vcvar);
468 }
469
470 static void Cvar_DeleteVirtual_All(cvar_t *var)
471 {
472         cvar_t *vcvar, *vcvar_next;
473
474         List_For_Each_Entry_Safe(vcvar, vcvar_next, &var->vlist, vlist)
475                 Cvar_DeleteVirtual(vcvar);
476 }
477
478 void Cvar_RegisterVirtual(cvar_t *variable, const char *name)
479 {
480         cvar_state_t *cvars = &cvars_all;
481         cvar_t *vcvar;
482
483         if(!*name)
484         {
485                 Con_Printf("Cvar_RegisterVirtual: invalid virtual cvar name\n");
486                 return;
487         }
488
489         // check for overlap with a command
490         if (Cmd_Exists(cmd_local, name))
491         {
492                 Con_Printf("Cvar_RegisterVirtual: %s is a command\n", name);
493                 return;
494         }
495
496         if(Cvar_FindVar(&cvars_all, name, 0))
497         {
498                 Con_Printf("Cvar_RegisterVirtual: %s is a cvar\n", name);
499                 return;
500         }
501
502         vcvar = (cvar_t *)Mem_Alloc(cvars->mempool, sizeof(cvar_t));
503         vcvar->parent = variable;
504         vcvar->flags = variable->flags; 
505         vcvar->name = (char *)Mem_strdup(cvars->mempool, name);
506         vcvar->description = variable->description;
507
508         // Add to it
509         List_Add_Tail(&vcvar->vlist, &variable->vlist);
510
511         Cvar_Link(vcvar, cvars);
512 }
513
514 /*
515 ============
516 Cvar_RegisterVariable
517
518 Adds a freestanding variable to the variable list.
519 ============
520 */
521 void Cvar_RegisterVariable (cvar_t *variable)
522 {
523         cvar_state_t *cvars = NULL;
524         cvar_t *cvar;
525         int i;
526
527         switch (variable->flags & (CF_CLIENT | CF_SERVER))
528         {
529         case CF_CLIENT:
530         case CF_SERVER:
531         case CF_CLIENT | CF_SERVER:
532                 cvars = &cvars_all;
533                 break;
534         case 0:
535                 Sys_Error("Cvar_RegisterVariable({\"%s\", \"%s\", %i}) with no CF_CLIENT | CF_SERVER flags\n", variable->name, variable->string, variable->flags);
536                 break;
537         default:
538                 Sys_Error("Cvar_RegisterVariable({\"%s\", \"%s\", %i}) with weird CF_CLIENT | CF_SERVER flags\n", variable->name, variable->string, variable->flags);
539                 break;
540         }
541
542         if (developer_extra.integer)
543                 Con_DPrintf("Cvar_RegisterVariable({\"%s\", \"%s\", %i});\n", variable->name, variable->string, variable->flags);
544
545         // first check to see if it has already been defined
546         cvar = Cvar_FindVar(cvars, variable->name, ~0);
547         if (cvar)
548         {
549                 if (cvar->flags & CF_ALLOCATED)
550                 {
551                         if (developer_extra.integer)
552                                 Con_DPrintf("...  replacing existing allocated cvar {\"%s\", \"%s\", %i}\n", cvar->name, cvar->string, cvar->flags);
553                         // fixed variables replace allocated ones
554                         // (because the engine directly accesses fixed variables)
555                         // NOTE: this isn't actually used currently
556                         // (all cvars are registered before config parsing)
557                         variable->flags |= (cvar->flags & ~CF_ALLOCATED);
558                         // cvar->string is now owned by variable instead
559                         variable->string = cvar->string;
560                         variable->defstring = cvar->defstring;
561                         variable->value = atof (variable->string);
562                         variable->integer = (int) variable->value;
563                         // Preserve autocvar status.
564                         memcpy(variable->globaldefindex, cvar->globaldefindex, sizeof(variable->globaldefindex));
565                         memcpy(variable->globaldefindex_stringno, cvar->globaldefindex_stringno, sizeof(variable->globaldefindex_stringno));
566                         // replace cvar with this one...
567                         List_Replace(&cvar->list, &variable->list);
568
569                         // get rid of old allocated cvar
570                         // (but not cvar->string and cvar->defstring, because we kept those)
571                         Z_Free((char *)cvar->name);
572                         Z_Free(cvar);
573                 }
574                 else
575                         Con_DPrintf("Can't register variable %s, already defined\n", variable->name);
576                 return;
577         }
578
579         // check for overlap with a command
580         if (Cmd_Exists(cmd_local, variable->name))
581         {
582                 Con_Printf("Cvar_RegisterVariable: %s is a command\n", variable->name);
583                 return;
584         }
585
586         // copy the value off, because future sets will Z_Free it
587         variable->name = (char *)Mem_strdup(cvars->mempool, variable->name);
588         variable->string = (char *)Mem_strdup(cvars->mempool, variable->string);
589         variable->defstring = (char *)Mem_strdup(cvars->mempool, variable->string);
590         variable->value = atof (variable->string);
591         variable->integer = (int) variable->value;
592         variable->initstate = NULL;
593         variable->parent = NULL;
594         variable->vlist.next = variable->vlist.prev = &variable->vlist;
595
596         // Mark it as not an autocvar.
597         for (i = 0;i < PRVM_PROG_MAX;i++)
598                 variable->globaldefindex[i] = -1;
599
600         Cvar_Link(variable, cvars);
601 }
602
603 /*
604 ============
605 Cvar_Get
606
607 Adds a newly allocated variable to the variable list or sets its value.
608 ============
609 */
610 cvar_t *Cvar_Get(cvar_state_t *cvars, const char *name, const char *value, int flags, const char *newdescription)
611 {
612         cvar_t *cvar;
613         int i;
614
615         if (developer_extra.integer)
616                 Con_DPrintf("Cvar_Get(\"%s\", \"%s\", %i);\n", name, value, flags);
617
618         // check for pure evil
619         if (!*name)
620         {
621                 Con_Printf("Cvar_Get: invalid variable name\n");
622                 return NULL;
623         }
624
625         // first check to see if it has already been defined
626         cvar = Cvar_FindVar(cvars, name, ~0);
627         if (cvar)
628         {
629                 if(cvar->parent)
630                 {
631                         List_Delete(&cvar->vlist);
632                         cvar->parent = NULL;
633                 }
634
635                 cvar->flags |= flags;
636                 Cvar_SetQuick (cvar, value);
637                 if(newdescription && (cvar->flags & CF_ALLOCATED))
638                 {
639                         if(cvar->description != cvar_dummy_description)
640                                 Z_Free((char *)cvar->description);
641
642                         if(*newdescription)
643                                 cvar->description = (char *)Mem_strdup(cvars->mempool, newdescription);
644                         else
645                                 cvar->description = cvar_dummy_description;
646                 }
647                 return cvar;
648         }
649
650         // check for overlap with a command
651         if (Cmd_Exists(cmd_local, name))
652         {
653                 Con_Printf("Cvar_Get: %s is a command\n", name);
654                 return NULL;
655         }
656
657         // allocate a new cvar, cvar name, and cvar string
658         // TODO: factorize the following code with the one at the end of Cvar_RegisterVariable()
659         // FIXME: these never get Z_Free'd
660         cvar = (cvar_t *)Mem_Alloc(cvars->mempool, sizeof(cvar_t));
661         cvar->flags = flags | CF_ALLOCATED;
662         cvar->name = (char *)Mem_strdup(cvars->mempool, name);
663         cvar->string = (char *)Mem_strdup(cvars->mempool, value);
664         cvar->defstring = (char *)Mem_strdup(cvars->mempool, value);
665         cvar->value = atof (cvar->string);
666         cvar->integer = (int) cvar->value;
667         cvar->initstate = NULL;
668         cvar->parent = NULL;
669         cvar->vlist.next = cvar->vlist.prev = &cvar->vlist;
670
671         if(newdescription && *newdescription)
672                 cvar->description = (char *)Mem_strdup(cvars->mempool, newdescription);
673         else
674                 cvar->description = cvar_dummy_description; // actually checked by VM_cvar_type
675
676         // Mark it as not an autocvar.
677         for (i = 0;i < PRVM_PROG_MAX;i++)
678                 cvar->globaldefindex[i] = -1;
679
680         Cvar_Link(cvar, cvars);
681
682         return cvar;
683 }
684
685 static void Cvar_Delete(cvar_t *cvar)
686 {
687         cvar_state_t *cvars = &cvars_all;
688
689         List_Delete(&cvar->list);
690
691         for (cvar_t *hash = cvars->hashtable[cvar->hashindex]; hash->hnext != hash; hash = hash->hnext)
692         {
693                 if(hash->hnext == cvar)
694                 {
695                         hash->hnext = cvar->hnext;
696                         break;
697                 }
698         }
699
700         Cvar_DeleteVirtual_All(cvar);
701
702         if(cvar->flags & CF_ALLOCATED)
703         {
704                 if(cvar->description != cvar_dummy_description)
705                         Mem_Free((char *)cvar->description);
706
707                 Mem_Free((char *)cvar->name);
708                 Mem_Free((char *)cvar->string);
709                 Mem_Free((char *)cvar->defstring);
710                 Mem_Free(cvar);
711         }
712 }
713
714 qbool Cvar_Readonly (cvar_t *var, const char *cmd_name)
715 {
716         if (var->flags & CF_READONLY)
717         {
718                 if(cmd_name)
719                         Con_Printf("%s: ",cmd_name);
720                 Con_Printf("%s", var->name);
721                 Con_Printf(" is read-only\n");
722                 return true;
723         }
724         return false;
725 }
726
727 /*
728 ============
729 Cvar_Command
730
731 Handles variable inspection and changing from the console
732 ============
733 */
734 qbool Cvar_Command (cmd_state_t *cmd)
735 {
736         cvar_state_t *cvars = cmd->cvars;
737         cvar_t *var;
738
739         // check variables
740         var = Cvar_FindVar(cvars, Cmd_Argv(cmd, 0), (cmd->cvars_flagsmask));
741         if (!var)
742                 return false;
743
744         // perform a variable print or set
745         if (Cmd_Argc(cmd) == 1)
746         {
747                 Cvar_PrintHelp(var->parent ? var->parent : var, Cmd_Argv(cmd, 0), true);
748                 return true;
749         }
750
751         if (developer_extra.integer)
752                 Con_DPrint("Cvar_Command: ");
753
754         if(Cvar_Readonly(var, NULL))
755                 return true;
756
757         Cvar_SetQuick(var->parent ? var->parent : var, Cmd_Argv(cmd, 1));
758         if (developer_extra.integer)
759                 Con_DPrint("\n");
760         return true;
761 }
762
763 void Cvar_UnlockDefaults(cmd_state_t *cmd)
764 {
765         cvar_state_t *cvars = cmd->cvars;
766         cvar_t *var;
767         // unlock the default values of all cvars
768         List_For_Each_Entry(var, &cvars->vars->list, list)
769                 var->flags &= ~CF_DEFAULTSET;
770 }
771
772 void Cvar_LockDefaults_f(cmd_state_t *cmd)
773 {
774         cvar_state_t *cvars = cmd->cvars;
775         cvar_t *var;
776         // lock in the default values of all cvars
777         List_For_Each_Entry(var, &cvars->vars->list, list)
778         {
779                 if (!var->parent && !(var->flags & CF_DEFAULTSET))
780                 {
781                         size_t alloclen;
782
783                         //Con_Printf("locking cvar %s (%s -> %s)\n", var->name, var->string, var->defstring);
784                         var->flags |= CF_DEFAULTSET;
785                         Z_Free((char *)var->defstring);
786                         alloclen = strlen(var->string) + 1;
787                         var->defstring = (char *)Mem_Alloc(cvars->mempool, alloclen);
788                         memcpy((char *)var->defstring, var->string, alloclen);
789                 }
790         }
791 }
792
793 void Cvar_SaveInitState(cvar_state_t *cvars)
794 {
795         cvar_t *cvar, *vcvar;
796
797         List_For_Each_Entry(cvar, &cvars->vars->list, list)
798         {
799                 // Get the the virtual cvars separately
800                 if(cvar->parent)
801                         continue;
802
803                 cvar->initstate = (cvar_t *)Mem_Alloc(cvars->mempool, sizeof(cvar_t));
804                 memcpy(cvar->initstate, cvar, sizeof(cvar_t));
805
806                 /*
807                  * Consider any virtual cvar created up to this point as
808                  * existing during init. Use the initstate of the parent cvar.
809                  */
810                 List_For_Each_Entry(vcvar, &cvar->vlist, list)
811                         vcvar->initstate = cvar->initstate;
812         }
813 }
814
815 void Cvar_RestoreInitState(cvar_state_t *cvars)
816 {
817         cvar_t *var/*, *vcvar*/, *next/*, *vnext*/;
818
819         List_For_Each_Entry_Safe(var, next, &cvars->vars->list, list)
820         {
821                 // Ignore virtual cvars
822                 if(var->parent)
823                         continue;
824
825                 // Cloudwalk FIXME: This crashes for some reason, so it's disabled for now.
826                 
827                 // Destroy all virtual cvars that didn't exist at init
828                 //List_For_Each_Entry_Safe(vcvar, vnext, &var->vlist, vlist)
829                 //      if(!vcvar->initstate)
830                 //              Cvar_DeleteVirtual(vcvar);
831
832                 if (var->initstate)
833                 {
834                         // restore this cvar, it existed at init
835                         if (((var->flags ^ var->initstate->flags) & CF_MAXFLAGSVAL)
836                          || strcmp(var->defstring ? var->defstring : "", var->initstate->defstring ? var->initstate->defstring : "")
837                          || strcmp(var->string ? var->string : "", var->initstate->string ? var->initstate->string : ""))
838                         {
839                                 Con_DPrintf("Cvar_RestoreInitState: Restoring cvar \"%s\"\n", var->name);
840                                 if (var->defstring)
841                                         Z_Free((char *)var->defstring);
842                                 var->defstring = Mem_strdup(cvars->mempool, var->initstate->defstring);
843                                 if (var->string)
844                                         Z_Free((char *)var->string);
845                                 var->string = Mem_strdup(cvars->mempool, var->initstate->string);
846                         }
847                         var->flags = var->initstate->flags;
848                         var->value = var->initstate->value;
849                         var->integer = var->initstate->integer;
850                         VectorCopy(var->initstate->vector, var->vector);
851
852                 }
853                 else
854                 {
855                         if (!(var->flags & CF_ALLOCATED))
856                                 Con_DPrintf("Cvar_RestoreInitState: Unable to destroy cvar \"%s\", it was registered after init!\n", var->name);
857                         else if (Cvar_IsAutoCvar(var))
858                                 Con_DPrintf("Cvar_RestoreInitState: Unable to destroy cvar \"%s\", it is an autocvar used by running progs!\n", var->name);
859                         else
860                         {
861                                 // remove this cvar, it did not exist at init
862                                 Con_DPrintf("Cvar_RestoreInitState: Destroying cvar \"%s\"\n", var->name);
863                                 Cvar_Delete(var);
864                                 continue;
865                         }
866
867                         // At least reset it to the default.
868                         if((var->flags & CF_PERSISTENT) == 0)
869                                 Cvar_SetQuick(var, var->defstring);
870                 }
871         }
872 }
873
874 void Cvar_ResetToDefaults_All_f(cmd_state_t *cmd)
875 {
876         cvar_state_t *cvars = cmd->cvars;
877         cvar_t *var;
878         // restore the default values of all cvars
879         List_For_Each_Entry(var, &cvars->vars->list, list)
880         {
881                 if(!var->parent && !(var->flags & CF_PERSISTENT))
882                         Cvar_SetQuick(var, var->defstring);
883         }
884 }
885
886 void Cvar_ResetToDefaults_NoSaveOnly_f(cmd_state_t *cmd)
887 {
888         cvar_state_t *cvars = cmd->cvars;
889         cvar_t *var;
890         // restore the default values of all cvars
891         List_For_Each_Entry(var, &cvars->vars->list, list)
892         {
893                 if (!var->parent && !(var->flags & (CF_PERSISTENT | CF_ARCHIVE)))
894                         Cvar_SetQuick(var, var->defstring);
895         }
896 }
897
898 void Cvar_ResetToDefaults_SaveOnly_f(cmd_state_t *cmd)
899 {
900         cvar_state_t *cvars = cmd->cvars;
901         cvar_t *var;
902         // restore the default values of all cvars
903         List_For_Each_Entry(var, &cvars->vars->list, list)
904         {
905                 if (!var->parent && (var->flags & (CF_PERSISTENT | CF_ARCHIVE)) == CF_ARCHIVE)
906                         Cvar_SetQuick(var, var->defstring);
907         }
908 }
909
910 /*
911 ============
912 Cvar_WriteVariables
913
914 Writes lines containing "set variable value" for all variables
915 with the archive flag set to true.
916 ============
917 */
918 void Cvar_WriteVariables (cvar_state_t *cvars, qfile_t *f)
919 {
920         cvar_t  *var;
921         char buf1[MAX_INPUTLINE], buf2[MAX_INPUTLINE];
922
923         // don't save cvars that match their default value
924         List_For_Each_Entry(var, &cvars->vars->list, list)
925         {
926                 if(var->parent)
927                         continue;
928                 if ((var->flags & CF_ARCHIVE) && (strcmp(var->string, var->defstring) || ((var->flags & CF_ALLOCATED) && !(var->flags & CF_DEFAULTSET))))
929                 {
930                         Cmd_QuoteString(buf1, sizeof(buf1), var->name, "\"\\$", false);
931                         Cmd_QuoteString(buf2, sizeof(buf2), var->string, "\"\\$", false);
932                         FS_Printf(f, "%s\"%s\" \"%s\"\n", var->flags & CF_ALLOCATED ? "seta " : "", buf1, buf2);
933                 }
934         }
935 }
936
937 // Added by EvilTypeGuy eviltypeguy@qeradiant.com
938 // 2000-01-09 CvarList command By Matthias "Maddes" Buecher, http://www.inside3d.com/qip/
939 /*
940 =========
941 Cvar_List
942 =========
943 */
944 void Cvar_List_f(cmd_state_t *cmd)
945 {
946         cvar_state_t *cvars = cmd->cvars;
947         cvar_t *cvar;
948         const char *partial;
949         int count;
950         qbool ispattern;
951         char vabuf[1024];
952
953         if (Cmd_Argc(cmd) > 1)
954         {
955                 partial = Cmd_Argv(cmd, 1);
956                 ispattern = (strchr(partial, '*') || strchr(partial, '?'));
957                 if(!ispattern)
958                         partial = va(vabuf, sizeof(vabuf), "%s*", partial);
959         }
960         else
961         {
962                 partial = va(vabuf, sizeof(vabuf), "*");
963                 ispattern = false;
964         }
965
966         count = 0;
967         List_For_Each_Entry(cvar, &cvars->vars->list, list)
968         {
969                 if (matchpattern_with_separator(cvar->name, partial, false, "", false))
970                 {
971                         Cvar_PrintHelp(cvar->parent ? cvar->parent : cvar, cvar->name, true);
972                         count++;
973                 }
974         }
975
976         if (Cmd_Argc(cmd) > 1)
977         {
978                 if(ispattern)
979                         Con_Printf("%i cvar%s matching \"%s\"\n", count, (count > 1) ? "s" : "", partial);
980                 else
981                         Con_Printf("%i cvar%s beginning with \"%s\"\n", count, (count > 1) ? "s" : "", Cmd_Argv(cmd,1));
982         }
983         else
984                 Con_Printf("%i cvar(s)\n", count);
985 }
986 // 2000-01-09 CvarList command by Maddes
987
988 void Cvar_Set_f(cmd_state_t *cmd)
989 {
990         cvar_state_t *cvars = cmd->cvars;
991         cvar_t *cvar;
992
993         // make sure it's the right number of parameters
994         if (Cmd_Argc(cmd) < 3)
995         {
996                 Con_Printf("Set: wrong number of parameters, usage: set <variablename> <value> [<description>]\n");
997                 return;
998         }
999
1000         // check if it's read-only
1001         cvar = Cvar_FindVar(cvars, Cmd_Argv(cmd, 1), ~0);
1002         if (cvar)
1003                 if(Cvar_Readonly(cvar,"Set"))
1004                         return;
1005
1006         if (developer_extra.integer)
1007                 Con_DPrint("Set: ");
1008
1009         // all looks ok, create/modify the cvar
1010         Cvar_Get(cvars, Cmd_Argv(cmd, 1), Cmd_Argv(cmd, 2), cmd->cvars_flagsmask, Cmd_Argc(cmd) > 3 ? Cmd_Argv(cmd, 3) : NULL);
1011 }
1012
1013 void Cvar_SetA_f(cmd_state_t *cmd)
1014 {
1015         cvar_state_t *cvars = cmd->cvars;
1016         cvar_t *cvar;
1017
1018         // make sure it's the right number of parameters
1019         if (Cmd_Argc(cmd) < 3)
1020         {
1021                 Con_Printf("SetA: wrong number of parameters, usage: seta <variablename> <value> [<description>]\n");
1022                 return;
1023         }
1024
1025         // check if it's read-only
1026         cvar = Cvar_FindVar(cvars, Cmd_Argv(cmd, 1), ~0);
1027         if (cvar)
1028                 if(Cvar_Readonly(cvar,"SetA"))
1029                         return;
1030
1031         if (developer_extra.integer)
1032                 Con_DPrint("SetA: ");
1033
1034         // all looks ok, create/modify the cvar
1035         Cvar_Get(cvars, Cmd_Argv(cmd, 1), Cmd_Argv(cmd, 2), cmd->cvars_flagsmask | CF_ARCHIVE, Cmd_Argc(cmd) > 3 ? Cmd_Argv(cmd, 3) : NULL);
1036 }
1037
1038 void Cvar_Del_f(cmd_state_t *cmd)
1039 {
1040         cvar_state_t *cvars = cmd->cvars;
1041         int neededflags = ~0;
1042         int i;
1043         cvar_t *cvar;
1044
1045         if(Cmd_Argc(cmd) < 2)
1046         {
1047                 Con_Printf("%s: wrong number of parameters, usage: unset <variablename1> [<variablename2> ...]\n", Cmd_Argv(cmd, 0));
1048                 return;
1049         }
1050         for(i = 1; i < Cmd_Argc(cmd); ++i)
1051         {
1052                 cvar = Cvar_FindVar(cvars, Cmd_Argv(cmd, i), neededflags);
1053
1054                 if(!cvar)
1055                 {
1056                         Con_Printf("%s: %s is not defined\n", Cmd_Argv(cmd, 0), Cmd_Argv(cmd, i));
1057                         continue;
1058                 }
1059                 if(Cvar_Readonly(cvar, Cmd_Argv(cmd, 0)))
1060                         continue;
1061                 if(!(cvar->flags & CF_ALLOCATED))
1062                 {
1063                         Con_Printf("%s: %s is static and cannot be deleted\n", Cmd_Argv(cmd, 0), cvar->name);
1064                         continue;
1065                 }
1066                 Cvar_Delete(cvar);
1067         }
1068 }
1069
1070 #ifdef FILLALLCVARSWITHRUBBISH
1071 void Cvar_FillAll_f(cmd_state_t *cmd)
1072 {
1073         char *buf, *p, *q;
1074         int n, i;
1075         cvar_t *var;
1076         qbool verify;
1077         if(Cmd_Argc(cmd) != 2)
1078         {
1079                 Con_Printf("Usage: %s length to plant rubbish\n", Cmd_Argv(cmd, 0));
1080                 Con_Printf("Usage: %s -length to verify that the rubbish is still there\n", Cmd_Argv(cmd, 0));
1081                 return;
1082         }
1083         n = atoi(Cmd_Argv(cmd, 1));
1084         verify = (n < 0);
1085         if(verify)
1086                 n = -n;
1087         buf = Z_Malloc(n + 1);
1088         buf[n] = 0;
1089         for(var = cvars->vars; var; var = var->next)
1090         {
1091                 for(i = 0, p = buf, q = var->name; i < n; ++i)
1092                 {
1093                         *p++ = *q++;
1094                         if(!*q)
1095                                 q = var->name;
1096                 }
1097                 if(verify && strcmp(var->string, buf))
1098                 {
1099                         Con_Printf("\n%s does not contain the right rubbish, either this is the first run or a possible overrun was detected, or something changed it intentionally; it DOES contain: %s\n", var->name, var->string);
1100                 }
1101                 Cvar_SetQuick(var, buf);
1102         }
1103         Z_Free(buf);
1104 }
1105 #endif /* FILLALLCVARSWITHRUBBISH */