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