]> git.xonotic.org Git - xonotic/darkplaces.git/blob - cvar.c
Merge branch 'master' into Cloudwalk/cvar_overhaul
[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         List_Delete(&vcvar->list);
467         Mem_Free((char *)vcvar->name);
468         Mem_Free(vcvar);
469 }
470
471 static void Cvar_DeleteVirtual_All(cvar_t *var)
472 {
473         cvar_t *vcvar, *vcvar_next;
474
475         List_For_Each_Entry_Safe(vcvar, vcvar_next, &var->vlist, vlist)
476                 Cvar_DeleteVirtual(vcvar);
477 }
478
479 void Cvar_RegisterVirtual(cvar_t *variable, const char *name)
480 {
481         cvar_state_t *cvars = &cvars_all;
482         cvar_t *vcvar;
483
484         if(!*name)
485         {
486                 Con_Printf("Cvar_RegisterVirtual: invalid virtual cvar name\n");
487                 return;
488         }
489
490         // check for overlap with a command
491         if (Cmd_Exists(cmd_local, name))
492         {
493                 Con_Printf("Cvar_RegisterVirtual: %s is a command\n", name);
494                 return;
495         }
496
497         if(Cvar_FindVar(&cvars_all, name, 0))
498         {
499                 Con_Printf("Cvar_RegisterVirtual: %s is a cvar\n", name);
500                 return;
501         }
502
503         vcvar = (cvar_t *)Mem_Alloc(cvars->mempool, sizeof(cvar_t));
504         vcvar->parent = variable;
505         vcvar->flags = variable->flags; 
506         vcvar->name = (char *)Mem_strdup(cvars->mempool, name);
507         vcvar->description = variable->description;
508
509         // Add to it
510         List_Add_Tail(&vcvar->vlist, &variable->vlist);
511
512         Cvar_Link(vcvar, cvars);
513 }
514
515 /*
516 ============
517 Cvar_RegisterVariable
518
519 Adds a freestanding variable to the variable list.
520 ============
521 */
522 void Cvar_RegisterVariable (cvar_t *variable)
523 {
524         cvar_state_t *cvars = NULL;
525         cvar_t *cvar;
526         int i;
527
528         switch (variable->flags & (CF_CLIENT | CF_SERVER))
529         {
530         case CF_CLIENT:
531         case CF_SERVER:
532         case CF_CLIENT | CF_SERVER:
533                 cvars = &cvars_all;
534                 break;
535         case 0:
536                 Sys_Error("Cvar_RegisterVariable({\"%s\", \"%s\", %i}) with no CF_CLIENT | CF_SERVER flags\n", variable->name, variable->string, variable->flags);
537                 break;
538         default:
539                 Sys_Error("Cvar_RegisterVariable({\"%s\", \"%s\", %i}) with weird CF_CLIENT | CF_SERVER flags\n", variable->name, variable->string, variable->flags);
540                 break;
541         }
542
543         if (developer_extra.integer)
544                 Con_DPrintf("Cvar_RegisterVariable({\"%s\", \"%s\", %i});\n", variable->name, variable->string, variable->flags);
545
546         // first check to see if it has already been defined
547         cvar = Cvar_FindVar(cvars, variable->name, ~0);
548         if (cvar)
549         {
550                 if (cvar->flags & CF_ALLOCATED)
551                 {
552                         if (developer_extra.integer)
553                                 Con_DPrintf("...  replacing existing allocated cvar {\"%s\", \"%s\", %i}\n", cvar->name, cvar->string, cvar->flags);
554                         // fixed variables replace allocated ones
555                         // (because the engine directly accesses fixed variables)
556                         // NOTE: this isn't actually used currently
557                         // (all cvars are registered before config parsing)
558                         variable->flags |= (cvar->flags & ~CF_ALLOCATED);
559                         // cvar->string is now owned by variable instead
560                         variable->string = cvar->string;
561                         variable->defstring = cvar->defstring;
562                         variable->value = atof (variable->string);
563                         variable->integer = (int) variable->value;
564                         // Preserve autocvar status.
565                         memcpy(variable->globaldefindex, cvar->globaldefindex, sizeof(variable->globaldefindex));
566                         memcpy(variable->globaldefindex_stringno, cvar->globaldefindex_stringno, sizeof(variable->globaldefindex_stringno));
567                         // replace cvar with this one...
568                         List_Replace(&cvar->list, &variable->list);
569
570                         // get rid of old allocated cvar
571                         // (but not cvar->string and cvar->defstring, because we kept those)
572                         Z_Free((char *)cvar->name);
573                         Z_Free(cvar);
574                 }
575                 else
576                         Con_DPrintf("Can't register variable %s, already defined\n", variable->name);
577                 return;
578         }
579
580         // check for overlap with a command
581         if (Cmd_Exists(cmd_local, variable->name))
582         {
583                 Con_Printf("Cvar_RegisterVariable: %s is a command\n", variable->name);
584                 return;
585         }
586
587         // copy the value off, because future sets will Z_Free it
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         List_Create(&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         List_Create(&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                 if(cvar->description == cvar_dummy_description)
807                         cvar->initstate->description = cvar_dummy_description;
808                 else
809                         cvar->initstate->description = (char *)Mem_strdup(cvars->mempool, cvar->description);
810
811                 cvar->initstate->string = (char *)Mem_strdup(cvars->mempool, cvar->string);
812                 cvar->initstate->defstring = (char *)Mem_strdup(cvars->mempool, cvar->defstring);
813
814                 /*
815                  * Consider any virtual cvar created up to this point as
816                  * existing during init. Use the initstate of the parent cvar.
817                  */
818                 List_For_Each_Entry(vcvar, &cvar->vlist, list)
819                         vcvar->initstate = cvar->initstate;
820         }
821 }
822
823 void Cvar_RestoreInitState(cvar_state_t *cvars)
824 {
825         cvar_t *var, *next;
826
827         List_For_Each_Entry_Safe(var, next, &cvars->vars->list, list)
828         {
829                 // Destroy all virtual cvars that didn't exist at init
830                 if(var->parent && !var->initstate)
831                 {
832                         Cvar_DeleteVirtual(var);
833                         continue;
834                 }
835
836                 if (var->initstate)
837                 {
838                         // restore this cvar, it existed at init
839                         Con_DPrintf("Cvar_RestoreInitState: Restoring cvar \"%s\"\n", var->name);
840                         if(var->flags & CF_ALLOCATED)
841                         {
842                                 if(var->flags & CF_ALLOCATED && var->description && var->description != cvar_dummy_description)
843                                         Z_Free((char *)var->description);
844                                 if(var->initstate->description == cvar_dummy_description)
845                                         var->description = cvar_dummy_description;
846                                 else
847                                         var->initstate->description = (char *)Mem_strdup(cvars->mempool, var->description);
848                         }
849
850                         if (var->defstring)
851                                 Z_Free((char *)var->defstring);
852                         var->defstring = Mem_strdup(cvars->mempool, var->initstate->defstring);
853                         if (var->string)
854                                 Z_Free((char *)var->string);
855                         var->string = Mem_strdup(cvars->mempool, var->initstate->string);
856
857                         var->flags = var->initstate->flags;
858                         var->value = var->initstate->value;
859                         var->integer = var->initstate->integer;
860                         VectorCopy(var->initstate->vector, var->vector);
861                 }
862                 else
863                 {
864                         if (!(var->flags & CF_ALLOCATED))
865                                 Con_DPrintf("Cvar_RestoreInitState: Unable to destroy cvar \"%s\", it was registered after init!\n", var->name);
866                         else if (Cvar_IsAutoCvar(var))
867                                 Con_DPrintf("Cvar_RestoreInitState: Unable to destroy cvar \"%s\", it is an autocvar used by running progs!\n", var->name);
868                         else
869                         {
870                                 // remove this cvar, it did not exist at init
871                                 Con_DPrintf("Cvar_RestoreInitState: Destroying cvar \"%s\"\n", var->name);
872                                 Cvar_Delete(var);
873                                 continue;
874                         }
875
876                         // At least reset it to the default.
877                         if((var->flags & CF_PERSISTENT) == 0)
878                                 Cvar_SetQuick(var, var->defstring);
879                 }
880         }
881 }
882
883 void Cvar_ResetToDefaults_All_f(cmd_state_t *cmd)
884 {
885         cvar_state_t *cvars = cmd->cvars;
886         cvar_t *var;
887         // restore the default values of all cvars
888         List_For_Each_Entry(var, &cvars->vars->list, list)
889         {
890                 if(!var->parent && !(var->flags & CF_PERSISTENT))
891                         Cvar_SetQuick(var, var->defstring);
892         }
893 }
894
895 void Cvar_ResetToDefaults_NoSaveOnly_f(cmd_state_t *cmd)
896 {
897         cvar_state_t *cvars = cmd->cvars;
898         cvar_t *var;
899         // restore the default values of all cvars
900         List_For_Each_Entry(var, &cvars->vars->list, list)
901         {
902                 if (!var->parent && !(var->flags & (CF_PERSISTENT | CF_ARCHIVE)))
903                         Cvar_SetQuick(var, var->defstring);
904         }
905 }
906
907 void Cvar_ResetToDefaults_SaveOnly_f(cmd_state_t *cmd)
908 {
909         cvar_state_t *cvars = cmd->cvars;
910         cvar_t *var;
911         // restore the default values of all cvars
912         List_For_Each_Entry(var, &cvars->vars->list, list)
913         {
914                 if (!var->parent && (var->flags & (CF_PERSISTENT | CF_ARCHIVE)) == CF_ARCHIVE)
915                         Cvar_SetQuick(var, var->defstring);
916         }
917 }
918
919 /*
920 ============
921 Cvar_WriteVariables
922
923 Writes lines containing "set variable value" for all variables
924 with the archive flag set to true.
925 ============
926 */
927 void Cvar_WriteVariables (cvar_state_t *cvars, qfile_t *f)
928 {
929         cvar_t  *var;
930         char buf1[MAX_INPUTLINE], buf2[MAX_INPUTLINE];
931
932         // don't save cvars that match their default value
933         List_For_Each_Entry(var, &cvars->vars->list, list)
934         {
935                 if(var->parent)
936                         continue;
937                 if ((var->flags & CF_ARCHIVE) && (strcmp(var->string, var->defstring) || ((var->flags & CF_ALLOCATED) && !(var->flags & CF_DEFAULTSET))))
938                 {
939                         Cmd_QuoteString(buf1, sizeof(buf1), var->name, "\"\\$", false);
940                         Cmd_QuoteString(buf2, sizeof(buf2), var->string, "\"\\$", false);
941                         FS_Printf(f, "%s\"%s\" \"%s\"\n", var->flags & CF_ALLOCATED ? "seta " : "", buf1, buf2);
942                 }
943         }
944 }
945
946 // Added by EvilTypeGuy eviltypeguy@qeradiant.com
947 // 2000-01-09 CvarList command By Matthias "Maddes" Buecher, http://www.inside3d.com/qip/
948 /*
949 =========
950 Cvar_List
951 =========
952 */
953 void Cvar_List_f(cmd_state_t *cmd)
954 {
955         cvar_state_t *cvars = cmd->cvars;
956         cvar_t *cvar;
957         const char *partial;
958         int count;
959         qbool ispattern;
960         char vabuf[1024];
961
962         if (Cmd_Argc(cmd) > 1)
963         {
964                 partial = Cmd_Argv(cmd, 1);
965                 ispattern = (strchr(partial, '*') || strchr(partial, '?'));
966                 if(!ispattern)
967                         partial = va(vabuf, sizeof(vabuf), "%s*", partial);
968         }
969         else
970         {
971                 partial = va(vabuf, sizeof(vabuf), "*");
972                 ispattern = false;
973         }
974
975         count = 0;
976         List_For_Each_Entry(cvar, &cvars->vars->list, list)
977         {
978                 if (matchpattern_with_separator(cvar->name, partial, false, "", false))
979                 {
980                         Cvar_PrintHelp(cvar->parent ? cvar->parent : cvar, cvar->name, true);
981                         count++;
982                 }
983         }
984
985         if (Cmd_Argc(cmd) > 1)
986         {
987                 if(ispattern)
988                         Con_Printf("%i cvar%s matching \"%s\"\n", count, (count > 1) ? "s" : "", partial);
989                 else
990                         Con_Printf("%i cvar%s beginning with \"%s\"\n", count, (count > 1) ? "s" : "", Cmd_Argv(cmd,1));
991         }
992         else
993                 Con_Printf("%i cvar(s)\n", count);
994 }
995 // 2000-01-09 CvarList command by Maddes
996
997 void Cvar_Set_f(cmd_state_t *cmd)
998 {
999         cvar_state_t *cvars = cmd->cvars;
1000         cvar_t *cvar;
1001
1002         // make sure it's the right number of parameters
1003         if (Cmd_Argc(cmd) < 3)
1004         {
1005                 Con_Printf("Set: wrong number of parameters, usage: set <variablename> <value> [<description>]\n");
1006                 return;
1007         }
1008
1009         // check if it's read-only
1010         cvar = Cvar_FindVar(cvars, Cmd_Argv(cmd, 1), ~0);
1011         if (cvar)
1012                 if(Cvar_Readonly(cvar,"Set"))
1013                         return;
1014
1015         if (developer_extra.integer)
1016                 Con_DPrint("Set: ");
1017
1018         // all looks ok, create/modify the cvar
1019         Cvar_Get(cvars, Cmd_Argv(cmd, 1), Cmd_Argv(cmd, 2), cmd->cvars_flagsmask, Cmd_Argc(cmd) > 3 ? Cmd_Argv(cmd, 3) : NULL);
1020 }
1021
1022 void Cvar_SetA_f(cmd_state_t *cmd)
1023 {
1024         cvar_state_t *cvars = cmd->cvars;
1025         cvar_t *cvar;
1026
1027         // make sure it's the right number of parameters
1028         if (Cmd_Argc(cmd) < 3)
1029         {
1030                 Con_Printf("SetA: wrong number of parameters, usage: seta <variablename> <value> [<description>]\n");
1031                 return;
1032         }
1033
1034         // check if it's read-only
1035         cvar = Cvar_FindVar(cvars, Cmd_Argv(cmd, 1), ~0);
1036         if (cvar)
1037                 if(Cvar_Readonly(cvar,"SetA"))
1038                         return;
1039
1040         if (developer_extra.integer)
1041                 Con_DPrint("SetA: ");
1042
1043         // all looks ok, create/modify the cvar
1044         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);
1045 }
1046
1047 void Cvar_Del_f(cmd_state_t *cmd)
1048 {
1049         cvar_state_t *cvars = cmd->cvars;
1050         int neededflags = ~0;
1051         int i;
1052         cvar_t *cvar;
1053
1054         if(Cmd_Argc(cmd) < 2)
1055         {
1056                 Con_Printf("%s: wrong number of parameters, usage: unset <variablename1> [<variablename2> ...]\n", Cmd_Argv(cmd, 0));
1057                 return;
1058         }
1059         for(i = 1; i < Cmd_Argc(cmd); ++i)
1060         {
1061                 cvar = Cvar_FindVar(cvars, Cmd_Argv(cmd, i), neededflags);
1062
1063                 if(!cvar)
1064                 {
1065                         Con_Printf("%s: %s is not defined\n", Cmd_Argv(cmd, 0), Cmd_Argv(cmd, i));
1066                         continue;
1067                 }
1068                 if(Cvar_Readonly(cvar, Cmd_Argv(cmd, 0)))
1069                         continue;
1070                 if(!(cvar->flags & CF_ALLOCATED))
1071                 {
1072                         Con_Printf("%s: %s is static and cannot be deleted\n", Cmd_Argv(cmd, 0), cvar->name);
1073                         continue;
1074                 }
1075                 Cvar_Delete(cvar);
1076         }
1077 }
1078
1079 #ifdef FILLALLCVARSWITHRUBBISH
1080 void Cvar_FillAll_f(cmd_state_t *cmd)
1081 {
1082         char *buf, *p, *q;
1083         int n, i;
1084         cvar_t *var;
1085         qbool verify;
1086         if(Cmd_Argc(cmd) != 2)
1087         {
1088                 Con_Printf("Usage: %s length to plant rubbish\n", Cmd_Argv(cmd, 0));
1089                 Con_Printf("Usage: %s -length to verify that the rubbish is still there\n", Cmd_Argv(cmd, 0));
1090                 return;
1091         }
1092         n = atoi(Cmd_Argv(cmd, 1));
1093         verify = (n < 0);
1094         if(verify)
1095                 n = -n;
1096         buf = Z_Malloc(n + 1);
1097         buf[n] = 0;
1098         for(var = cvars->vars; var; var = var->next)
1099         {
1100                 for(i = 0, p = buf, q = var->name; i < n; ++i)
1101                 {
1102                         *p++ = *q++;
1103                         if(!*q)
1104                                 q = var->name;
1105                 }
1106                 if(verify && strcmp(var->string, buf))
1107                 {
1108                         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);
1109                 }
1110                 Cvar_SetQuick(var, buf);
1111         }
1112         Z_Free(buf);
1113 }
1114 #endif /* FILLALLCVARSWITHRUBBISH */