beginnings of qw protocol support
[divverent/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 cvar_t *cvar_vars = NULL;
25 char *cvar_null_string = "";
26
27 /*
28 ============
29 Cvar_FindVar
30 ============
31 */
32 cvar_t *Cvar_FindVar (const char *var_name)
33 {
34         cvar_t *var;
35
36         for (var = cvar_vars;var;var = var->next)
37                 if (!strcasecmp (var_name, var->name))
38                         return var;
39
40         return NULL;
41 }
42
43 cvar_t *Cvar_FindVarAfter (const char *prev_var_name, int neededflags)
44 {
45         cvar_t *var;
46
47         if (*prev_var_name)
48         {
49                 var = Cvar_FindVar (prev_var_name);
50                 if (!var)
51                         return NULL;
52                 var = var->next;
53         }
54         else
55                 var = cvar_vars;
56
57         // search for the next cvar matching the needed flags
58         while (var)
59         {
60                 if ((var->flags & neededflags) || !neededflags)
61                         break;
62                 var = var->next;
63         }
64         return var;
65 }
66
67 /*
68 ============
69 Cvar_VariableValue
70 ============
71 */
72 float Cvar_VariableValue (const char *var_name)
73 {
74         cvar_t *var;
75
76         var = Cvar_FindVar (var_name);
77         if (!var)
78                 return 0;
79         return atof (var->string);
80 }
81
82
83 /*
84 ============
85 Cvar_VariableString
86 ============
87 */
88 const char *Cvar_VariableString (const char *var_name)
89 {
90         cvar_t *var;
91
92         var = Cvar_FindVar (var_name);
93         if (!var)
94                 return cvar_null_string;
95         return var->string;
96 }
97
98 /*
99 ============
100 Cvar_VariableDefString
101 ============
102 */
103 const char *Cvar_VariableDefString (const char *var_name)
104 {
105         cvar_t *var;
106
107         var = Cvar_FindVar (var_name);
108         if (!var)
109                 return cvar_null_string;
110         return var->defstring;
111 }
112
113
114 /*
115 ============
116 Cvar_CompleteVariable
117 ============
118 */
119 const char *Cvar_CompleteVariable (const char *partial)
120 {
121         cvar_t          *cvar;
122         size_t          len;
123
124         len = strlen(partial);
125
126         if (!len)
127                 return NULL;
128
129 // check functions
130         for (cvar=cvar_vars ; cvar ; cvar=cvar->next)
131                 if (!strncasecmp (partial,cvar->name, len))
132                         return cvar->name;
133
134         return NULL;
135 }
136
137
138 /*
139         CVar_CompleteCountPossible
140
141         New function for tab-completion system
142         Added by EvilTypeGuy
143         Thanks to Fett erich@heintz.com
144
145 */
146 int Cvar_CompleteCountPossible (const char *partial)
147 {
148         cvar_t  *cvar;
149         size_t  len;
150         int             h;
151
152         h = 0;
153         len = strlen(partial);
154
155         if (!len)
156                 return  0;
157
158         // Loop through the cvars and count all possible matches
159         for (cvar = cvar_vars; cvar; cvar = cvar->next)
160                 if (!strncasecmp(partial, cvar->name, len))
161                         h++;
162
163         return h;
164 }
165
166 /*
167         CVar_CompleteBuildList
168
169         New function for tab-completion system
170         Added by EvilTypeGuy
171         Thanks to Fett erich@heintz.com
172         Thanks to taniwha
173
174 */
175 const char **Cvar_CompleteBuildList (const char *partial)
176 {
177         const cvar_t *cvar;
178         size_t len = 0;
179         size_t bpos = 0;
180         size_t sizeofbuf = (Cvar_CompleteCountPossible (partial) + 1) * sizeof (const char *);
181         const char **buf;
182
183         len = strlen(partial);
184         buf = (const char **)Mem_Alloc(tempmempool, sizeofbuf + sizeof (const char *));
185         // Loop through the alias list and print all matches
186         for (cvar = cvar_vars; cvar; cvar = cvar->next)
187                 if (!strncasecmp(partial, cvar->name, len))
188                         buf[bpos++] = cvar->name;
189
190         buf[bpos] = NULL;
191         return buf;
192 }
193
194 // written by LordHavoc
195 void Cvar_CompleteCvarPrint (const char *partial)
196 {
197         cvar_t *cvar;
198         size_t len = strlen(partial);
199         // Loop through the command list and print all matches
200         for (cvar = cvar_vars; cvar; cvar = cvar->next)
201                 if (!strncasecmp(partial, cvar->name, len))
202                         Con_Printf("%s : \"%s\" (\"%s\") : %s\n", cvar->name, cvar->string, cvar->defstring, cvar->description);
203 }
204
205
206 /*
207 ============
208 Cvar_Set
209 ============
210 */
211 void Cvar_SetQuick_Internal (cvar_t *var, const char *value)
212 {
213         qboolean changed;
214
215         changed = strcmp(var->string, value);
216         // LordHavoc: don't reallocate when there is no change
217         if (!changed)
218                 return;
219
220         // LordHavoc: don't reallocate when the buffer is the same size
221         if (!var->string || strlen(var->string) != strlen(value))
222         {
223                 Z_Free (var->string);   // free the old value string
224
225                 var->string = (char *)Z_Malloc (strlen(value)+1);
226         }
227         strcpy (var->string, value);
228         var->value = atof (var->string);
229         var->integer = (int) var->value;
230         if ((var->flags & CVAR_NOTIFY) && changed && sv.active)
231                 SV_BroadcastPrintf("\"%s\" changed to \"%s\"\n", var->name, var->string);
232 #if 0
233         // TODO: add infostring support to the server?
234         if ((var->flags & CVAR_SERVERINFO) && changed && sv.active)
235         {
236                 InfoString_SetValue(svs.serverinfo, sizeof(svs.serverinfo), var->name, var->string);
237                 if (sv.active)
238                 {
239                         MSG_WriteByte (&sv.reliable_datagram, svc_serverinfostring);
240                         MSG_WriteString (&sv.reliable_datagram, var->name);
241                         MSG_WriteString (&sv.reliable_datagram, var->string);
242                 }
243         }
244 #endif
245         if ((var->flags & CVAR_USERINFO) && changed && cls.state != ca_dedicated)
246         {
247                 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), var->name, var->string);
248                 if (cls.state == ca_connected)
249                         Cmd_ForwardStringToServer(va("setinfo \"%s\" \"%s\"\n", var->name, var->string));
250         }
251 }
252
253 void Cvar_SetQuick (cvar_t *var, const char *value)
254 {
255         if (var == NULL)
256         {
257                 Con_Print("Cvar_SetQuick: var == NULL\n");
258                 return;
259         }
260
261         if (developer.integer)
262                 Con_Printf("Cvar_SetQuick({\"%s\", \"%s\", %i, \"%s\"}, \"%s\");\n", var->name, var->string, var->flags, var->defstring, value);
263
264         Cvar_SetQuick_Internal(var, value);
265 }
266
267 void Cvar_Set (const char *var_name, const char *value)
268 {
269         cvar_t *var;
270         var = Cvar_FindVar (var_name);
271         if (var == NULL)
272         {
273                 Con_Printf("Cvar_Set: variable %s not found\n", var_name);
274                 return;
275         }
276         Cvar_SetQuick(var, value);
277 }
278
279 /*
280 ============
281 Cvar_SetValue
282 ============
283 */
284 void Cvar_SetValueQuick(cvar_t *var, float value)
285 {
286         char val[MAX_INPUTLINE];
287
288         if ((float)((int)value) == value)
289                 sprintf(val, "%i", (int)value);
290         else
291                 sprintf(val, "%f", value);
292         Cvar_SetQuick(var, val);
293 }
294
295 void Cvar_SetValue(const char *var_name, float value)
296 {
297         char val[MAX_INPUTLINE];
298
299         if ((float)((int)value) == value)
300                 sprintf(val, "%i", (int)value);
301         else
302                 sprintf(val, "%f", value);
303         Cvar_Set(var_name, val);
304 }
305
306 /*
307 ============
308 Cvar_RegisterVariable
309
310 Adds a freestanding variable to the variable list.
311 ============
312 */
313 void Cvar_RegisterVariable (cvar_t *variable)
314 {
315         cvar_t *current, *next, *cvar;
316         char *oldstr;
317
318         if (developer.integer)
319                 Con_Printf("Cvar_RegisterVariable({\"%s\", \"%s\", %i});\n", variable->name, variable->string, variable->flags);
320
321 // first check to see if it has already been defined
322         cvar = Cvar_FindVar (variable->name);
323         if (cvar)
324         {
325                 if (cvar->flags & CVAR_ALLOCATED)
326                 {
327                         if (developer.integer)
328                                 Con_Printf("...  replacing existing allocated cvar {\"%s\", \"%s\", %i}\n", cvar->name, cvar->string, cvar->flags);
329                         // fixed variables replace allocated ones
330                         // (because the engine directly accesses fixed variables)
331                         // NOTE: this isn't actually used currently
332                         // (all cvars are registered before config parsing)
333                         variable->flags |= (cvar->flags & ~CVAR_ALLOCATED);
334                         // cvar->string is now owned by variable instead
335                         variable->string = cvar->string;
336                         variable->defstring = cvar->defstring;
337                         variable->value = atof (variable->string);
338                         variable->integer = (int) variable->value;
339                         // replace cvar with this one...
340                         variable->next = cvar->next;
341                         if (cvar_vars == cvar)
342                         {
343                                 // head of the list is easy to change
344                                 cvar_vars = variable;
345                         }
346                         else
347                         {
348                                 // otherwise find it somewhere in the list
349                                 for (current = cvar_vars;current->next != cvar;current = current->next)
350                                         ;
351                                 current->next = variable;
352                         }
353
354                         // get rid of old allocated cvar
355                         // (but not cvar->string and cvar->defstring, because we kept those)
356                         Z_Free(cvar->name);
357                         Z_Free(cvar);
358                 }
359                 else
360                         Con_Printf("Can't register variable %s, already defined\n", variable->name);
361                 return;
362         }
363
364 // check for overlap with a command
365         if (Cmd_Exists (variable->name))
366         {
367                 Con_Printf("Cvar_RegisterVariable: %s is a command\n", variable->name);
368                 return;
369         }
370
371 // copy the value off, because future sets will Z_Free it
372         oldstr = variable->string;
373         variable->string = (char *)Z_Malloc (strlen(variable->string)+1);
374         strcpy (variable->string, oldstr);
375         variable->defstring = (char *)Z_Malloc (strlen(variable->string)+1);
376         strcpy (variable->defstring, oldstr);
377         variable->value = atof (variable->string);
378         variable->integer = (int) variable->value;
379
380 // link the variable in
381 // alphanumerical order
382         for( current = NULL, next = cvar_vars ; next && strcmp( next->name, variable->name ) < 0 ; current = next, next = next->next )
383                 ;
384         if( current ) {
385                 current->next = variable;
386         } else {
387                 cvar_vars = variable;
388         }
389         variable->next = next;
390 }
391
392 /*
393 ============
394 Cvar_Get
395
396 Adds a newly allocated variable to the variable list or sets its value.
397 ============
398 */
399 cvar_t *Cvar_Get (const char *name, const char *value, int flags)
400 {
401         cvar_t *cvar;
402
403         if (developer.integer)
404                 Con_Printf("Cvar_Get(\"%s\", \"%s\", %i);\n", name, value, flags);
405
406 // first check to see if it has already been defined
407         cvar = Cvar_FindVar (name);
408         if (cvar)
409         {
410                 cvar->flags |= flags;
411                 Cvar_SetQuick_Internal (cvar, value);
412                 // also set the default value (but only once)
413                 if (~cvar->flags & CVAR_DEFAULTSET)
414                 {
415                         cvar->flags |= CVAR_DEFAULTSET;
416
417                         Z_Free(cvar->defstring);
418                         cvar->defstring = (char *)Z_Malloc(strlen(value) + 1);
419                         strcpy(cvar->defstring, value);
420                 }
421                 return cvar;
422         }
423
424 // check for overlap with a command
425         if (Cmd_Exists (name))
426         {
427                 Con_Printf("Cvar_Get: %s is a command\n", name);
428                 return NULL;
429         }
430
431 // allocate a new cvar, cvar name, and cvar string
432 // FIXME: these never get Z_Free'd
433         cvar = (cvar_t *)Z_Malloc(sizeof(cvar_t));
434         cvar->flags = flags | CVAR_ALLOCATED | CVAR_DEFAULTSET;
435         cvar->name = (char *)Z_Malloc(strlen(name)+1);
436         strcpy(cvar->name, name);
437         cvar->string = (char *)Z_Malloc(strlen(value)+1);
438         strcpy(cvar->string, value);
439         cvar->defstring = (char *)Z_Malloc(strlen(value)+1);
440         strcpy(cvar->defstring, value);
441         cvar->value = atof (cvar->string);
442         cvar->integer = (int) cvar->value;
443         cvar->description = "custom cvar";
444
445 // link the variable in
446         cvar->next = cvar_vars;
447         cvar_vars = cvar;
448         return cvar;
449 }
450
451
452 /*
453 ============
454 Cvar_Command
455
456 Handles variable inspection and changing from the console
457 ============
458 */
459 qboolean        Cvar_Command (void)
460 {
461         cvar_t                  *v;
462
463 // check variables
464         v = Cvar_FindVar (Cmd_Argv(0));
465         if (!v)
466                 return false;
467
468 // perform a variable print or set
469         if (Cmd_Argc() == 1)
470         {
471                 Con_Printf("\"%s\" is \"%s\" [\"%s\"]\n", v->name, v->string, v->defstring);
472                 return true;
473         }
474
475         Con_DPrint("Cvar_Command: ");
476
477         if (v->flags & CVAR_READONLY)
478         {
479                 Con_Printf("%s is read-only\n", v->name);
480                 return true;
481         }
482         Cvar_Set (v->name, Cmd_Argv(1));
483         return true;
484 }
485
486
487 /*
488 ============
489 Cvar_WriteVariables
490
491 Writes lines containing "set variable value" for all variables
492 with the archive flag set to true.
493 ============
494 */
495 void Cvar_WriteVariables (qfile_t *f)
496 {
497         cvar_t  *var;
498
499         for (var = cvar_vars ; var ; var = var->next)
500                 if (var->flags & CVAR_SAVE)
501                         FS_Printf(f, "%s%s \"%s\"\n", var->flags & CVAR_ALLOCATED ? "seta " : "", var->name, var->string);
502 }
503
504
505 // Added by EvilTypeGuy eviltypeguy@qeradiant.com
506 // 2000-01-09 CvarList command By Matthias "Maddes" Buecher, http://www.inside3d.com/qip/
507 /*
508 =========
509 Cvar_List
510 =========
511 */
512 void Cvar_List_f (void)
513 {
514         cvar_t *cvar;
515         const char *partial;
516         size_t len;
517         int count;
518
519         if (Cmd_Argc() > 1)
520         {
521                 partial = Cmd_Argv (1);
522                 len = strlen(partial);
523         }
524         else
525         {
526                 partial = NULL;
527                 len = 0;
528         }
529
530         count = 0;
531         for (cvar = cvar_vars; cvar; cvar = cvar->next)
532         {
533                 if (partial && strncasecmp (partial,cvar->name,len))
534                         continue;
535
536                 Con_Printf("%s is \"%s\" [\"%s\"] %s\n", cvar->name, cvar->string, cvar->defstring, cvar->description);
537                 count++;
538         }
539
540         if (partial)
541                 Con_Printf("%i cvar(s) beginning with \"%s\"\n", count, partial);
542         else
543                 Con_Printf("%i cvar(s)\n", count);
544 }
545 // 2000-01-09 CvarList command by Maddes
546
547 void Cvar_Set_f (void)
548 {
549         cvar_t *cvar;
550
551         // make sure it's the right number of parameters
552         if (Cmd_Argc() < 3)
553         {
554                 Con_Printf("Set: wrong number of parameters, usage: set <variablename> <value>\n");
555                 return;
556         }
557
558         // check if it's read-only
559         cvar = Cvar_FindVar(Cmd_Argv(1));
560         if (cvar && cvar->flags & CVAR_READONLY)
561         {
562                 Con_Printf("Set: %s is read-only\n", cvar->name);
563                 return;
564         }
565
566         Con_DPrint("Set: ");
567
568         // all looks ok, create/modify the cvar
569         Cvar_Get(Cmd_Argv(1), Cmd_Argv(2), 0);
570 }
571
572 void Cvar_SetA_f (void)
573 {
574         cvar_t *cvar;
575
576         // make sure it's the right number of parameters
577         if (Cmd_Argc() < 3)
578         {
579                 Con_Printf("SetA: wrong number of parameters, usage: seta <variablename> <value>\n");
580                 return;
581         }
582
583         // check if it's read-only
584         cvar = Cvar_FindVar(Cmd_Argv(1));
585         if (cvar && cvar->flags & CVAR_READONLY)
586         {
587                 Con_Printf("SetA: %s is read-only\n", cvar->name);
588                 return;
589         }
590
591         Con_DPrint("SetA: ");
592
593         // all looks ok, create/modify the cvar
594         Cvar_Get(Cmd_Argv(1), Cmd_Argv(2), CVAR_SAVE);
595 }
596
597