]> git.xonotic.org Git - xonotic/darkplaces.git/blob - cmd.c
Xonotic: don't hide config problems during startup
[xonotic/darkplaces.git] / cmd.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 // cmd.c -- Quake script command processing module
21
22 #include "quakedef.h"
23 #include "thread.h"
24
25 cmd_state_t *cmd_local;
26 cmd_state_t *cmd_serverfromclient;
27
28 cmd_userdefined_t cmd_userdefined_all;
29 cmd_userdefined_t cmd_userdefined_null;
30
31 typedef struct cmd_iter_s {
32         cmd_state_t *cmd;
33 }
34 cmd_iter_t;
35
36 static cmd_iter_t *cmd_iter_all;
37
38 mempool_t *cbuf_mempool;
39
40 // we only run the +whatever commandline arguments once
41 qbool host_stuffcmdsrun = false;
42
43 //=============================================================================
44
45 void Cbuf_Lock(cmd_buf_t *cbuf)
46 {
47         Thread_LockMutex(cbuf->lock);
48 }
49
50 void Cbuf_Unlock(cmd_buf_t *cbuf)
51 {
52         Thread_UnlockMutex(cbuf->lock);
53 }
54
55
56 /*
57 ============
58 Cmd_Wait_f
59
60 Causes execution of the remainder of the command buffer to be delayed until
61 next frame.  This allows commands like:
62 bind g "impulse 5 ; +attack ; wait ; -attack ; impulse 2"
63 ============
64 */
65 static void Cmd_Wait_f (cmd_state_t *cmd)
66 {
67         cmd->cbuf->wait = true;
68 }
69
70 /*
71 ============
72 Cmd_Defer_f
73
74 Cause a command to be executed after a delay.
75 ============
76 */
77 static void Cbuf_ParseText(cmd_state_t *cmd, llist_t *head, cmd_input_t *existing, const char *text, qbool allowpending);
78 static void Cbuf_LinkString(cmd_state_t *cmd, llist_t *head, cmd_input_t *existing, const char *text, qbool leavepending, unsigned int cmdsize);
79 static void Cmd_Defer_f (cmd_state_t *cmd)
80 {
81         cmd_input_t *current;
82         cmd_buf_t *cbuf = cmd->cbuf;
83         unsigned int cmdsize;
84
85         if(Cmd_Argc(cmd) == 1)
86         {
87                 if(List_Is_Empty(&cbuf->deferred))
88                         Con_Printf("No commands are pending.\n");
89                 else
90                 {
91                         List_For_Each_Entry(current, &cbuf->deferred, cmd_input_t, list)
92                                 Con_Printf("-> In %9.2f: %s\n", current->delay, current->text);
93                 }
94         }
95         else if(Cmd_Argc(cmd) == 2 && !strcasecmp("clear", Cmd_Argv(cmd, 1)))
96         {
97                 while(!List_Is_Empty(&cbuf->deferred))
98                 {
99                         cbuf->size -= List_Entry(cbuf->deferred.next, cmd_input_t, list)->length;
100                         List_Move_Tail(cbuf->deferred.next, &cbuf->free);
101                 }
102         }
103         else if(Cmd_Argc(cmd) == 3 && (cmdsize = strlen(Cmd_Argv(cmd, 2))) )
104         {
105                 Cbuf_Lock(cbuf);
106
107                 Cbuf_LinkString(cmd, &cbuf->deferred, NULL, Cmd_Argv(cmd, 2), false, cmdsize);
108                 List_Entry(cbuf->deferred.prev, cmd_input_t, list)->delay = atof(Cmd_Argv(cmd, 1));
109
110                 Cbuf_Unlock(cbuf);
111         }
112         else
113         {
114                 Con_Printf("usage: defer <seconds> <command>\n"
115                            "       defer clear\n");
116                 return;
117         }
118 }
119
120 /*
121 =============================================================================
122
123                                                 COMMAND BUFFER
124
125  * The Quake command-line is super basic. It can be entered in the console
126  * or in config files. A semicolon is used to terminate a command and chain
127  * them together. Otherwise, a newline delineates command input.
128  *
129  * In most engines, the Quake command-line is a simple linear text buffer that
130  * is parsed when it executes. In Darkplaces, we use a linked list of command
131  * input and parse the input on the spot.
132  *
133  * This was done because Darkplaces allows multiple command interpreters on the
134  * same thread. Previously, each interpreter maintained its own buffer and this
135  * caused problems related to execution order, and maintaining a single simple
136  * buffer for all interpreters makes it non-trivial to keep track of which
137  * command should execute on which interpreter.
138
139 =============================================================================
140 */
141
142 /*
143 ============
144 Cbuf_NodeGet
145
146 Returns an existing buffer node for appending or reuse, or allocates a new one
147 ============
148 */
149 static cmd_input_t *Cbuf_NodeGet(cmd_buf_t *cbuf, cmd_input_t *existing)
150 {
151         cmd_input_t *node;
152         if(existing && existing->pending)
153                 node = existing;
154         else if(!List_Is_Empty(&cbuf->free))
155         {
156                 node = List_Entry(cbuf->free.next, cmd_input_t, list);
157                 node->length = node->pending = 0;
158         }
159         else
160         {
161                 node = (cmd_input_t *)Mem_Alloc(cbuf_mempool, sizeof(cmd_input_t));
162                 node->list.prev = node->list.next = &node->list;
163                 node->size = node->length = node->pending = 0;
164         }
165         return node;
166 }
167
168 /*
169 ============
170 Cbuf_LinkString
171
172 Copies a command string into a buffer node.
173 The input should not be null-terminated, the output will be.
174 ============
175 */
176 static void Cbuf_LinkString(cmd_state_t *cmd, llist_t *head, cmd_input_t *existing, const char *text, qbool leavepending, unsigned int cmdsize)
177 {
178         cmd_buf_t *cbuf = cmd->cbuf;
179         cmd_input_t *node = Cbuf_NodeGet(cbuf, existing);
180         unsigned int offset = node->length; // > 0 if(pending)
181
182         // node will match existing if its text was pending continuation
183         if(node != existing)
184         {
185                 node->source = cmd;
186                 List_Move_Tail(&node->list, head);
187         }
188
189         node->length += cmdsize;
190         if(node->size < node->length)
191         {
192                 node->text = (char *)Mem_Realloc(cbuf_mempool, node->text, node->length + 1);
193                 node->size = node->length;
194         }
195         cbuf->size += cmdsize;
196
197         dp_ustr2stp(&node->text[offset], node->length + 1, text, cmdsize);
198         //Con_Printf("^5Cbuf_LinkString(): %s `^7%s^5`\n", node->pending ? "append" : "new", &node->text[offset]);
199         node->pending = leavepending;
200 }
201
202 /*
203 ============
204 Cbuf_ParseText
205
206 Parses text to isolate command strings for linking into the buffer
207 separators: \n \r or unquoted and uncommented ';'
208 ============
209 */
210 static void Cbuf_ParseText(cmd_state_t *cmd, llist_t *head, cmd_input_t *existing, const char *text, qbool allowpending)
211 {
212         unsigned int cmdsize = 0, start = 0, pos;
213         qbool quotes = false, comment = false;
214
215         for (pos = 0; text[pos]; ++pos)
216         {
217                 switch(text[pos])
218                 {
219                         case ';':
220                                 if (comment || quotes)
221                                         break;
222                         case '\r':
223                         case '\n':
224                                 comment = false;
225                                 quotes = false; // matches div0-stable
226                                 if (cmdsize)
227                                 {
228                                         Cbuf_LinkString(cmd, head, existing, &text[start], false, cmdsize);
229                                         cmdsize = 0;
230                                 }
231                                 else if (existing && existing->pending) // all I got was this lousy \n
232                                         existing->pending = false;
233                                 continue; // don't increment cmdsize
234
235                         case '/':
236                                 if (!quotes && text[pos + 1] == '/' && (pos == 0 || ISWHITESPACE(text[pos - 1])))
237                                         comment = true;
238                                 break;
239                         case '"':
240                                 if (!comment && (pos == 0 || text[pos - 1] != '\\'))
241                                         quotes = !quotes;
242                                 break;
243                 }
244
245                 if (!comment)
246                 {
247                         if (!cmdsize)
248                                 start = pos;
249                         ++cmdsize;
250                 }
251         }
252
253         if (cmdsize) // the line didn't end yet but we do have a string
254                 Cbuf_LinkString(cmd, head, existing, &text[start], allowpending, cmdsize);
255 }
256
257 /*
258 ============
259 Cbuf_AddText
260
261 Adds command text at the end of the buffer
262 ============
263 */
264 void Cbuf_AddText (cmd_state_t *cmd, const char *text)
265 {
266         size_t l = strlen(text);
267         cmd_buf_t *cbuf = cmd->cbuf;
268         llist_t llist = {&llist, &llist};
269
270         if (cbuf->size + l > cbuf->maxsize)
271         {
272                 Con_Printf(CON_WARN "Cbuf_AddText: input too large, %luKB ought to be enough for anybody.\n", (unsigned long)(cbuf->maxsize / 1024));
273                 return;
274         }
275
276         Cbuf_Lock(cbuf);
277
278         // If the string terminates but the (last) line doesn't, the node will be left in the pending state (to be continued).
279         Cbuf_ParseText(cmd, &llist, (List_Is_Empty(&cbuf->start) ? NULL : List_Entry(cbuf->start.prev, cmd_input_t, list)), text, true);
280         List_Splice_Tail(&llist, &cbuf->start);
281
282         Cbuf_Unlock(cbuf);
283 }
284
285 /*
286 ============
287 Cbuf_InsertText
288
289 Adds command text immediately after the current command
290 ============
291 */
292 void Cbuf_InsertText (cmd_state_t *cmd, const char *text)
293 {
294         cmd_buf_t *cbuf = cmd->cbuf;
295         llist_t llist = {&llist, &llist};
296         size_t l = strlen(text);
297
298         if (cbuf->size + l > cbuf->maxsize)
299         {
300                 Con_Printf(CON_WARN "Cbuf_InsertText: input too large, %luKB ought to be enough for anybody.\n", (unsigned long)(cbuf->maxsize / 1024));
301                 return;
302         }
303
304         Cbuf_Lock(cbuf);
305
306         // bones_was_here assertion: when prepending to the buffer it never makes sense to leave node(s) in the `pending` state,
307         // it would have been impossible to append to such text later in the old raw text buffer,
308         // and allowing it causes bugs when .cfg files lack \n at EOF (see: https://gitlab.com/xonotic/darkplaces/-/issues/378).
309         Cbuf_ParseText(cmd, &llist, (List_Is_Empty(&cbuf->start) ? NULL : List_Entry(cbuf->start.next, cmd_input_t, list)), text, false);
310         List_Splice(&llist, &cbuf->start);
311
312         Cbuf_Unlock(cbuf);
313 }
314
315 /*
316 ============
317 Cbuf_Execute_Deferred --blub
318 ============
319 */
320 static void Cbuf_Execute_Deferred (cmd_buf_t *cbuf)
321 {
322         cmd_input_t *current, *n;
323         vec_t eat;
324
325         if (host.realtime - cbuf->deferred_oldtime < 0 || host.realtime - cbuf->deferred_oldtime > 1800)
326                 cbuf->deferred_oldtime = host.realtime;
327         eat = host.realtime - cbuf->deferred_oldtime;
328         if (eat < 1.0/128.0)
329                 return;
330         cbuf->deferred_oldtime = host.realtime;
331
332         List_For_Each_Entry_Safe(current, n, &cbuf->deferred, cmd_input_t, list)
333         {
334                 current->delay -= eat;
335                 if(current->delay <= 0)
336                 {
337                         Cbuf_AddText(current->source, current->text); // parse deferred string and append its cmdstring(s)
338                         List_Entry(cbuf->start.prev, cmd_input_t, list)->pending = false; // faster than div0-stable's Cbuf_AddText(";\n");
339                         List_Move_Tail(&current->list, &cbuf->free); // make deferred string memory available for reuse
340                         cbuf->size -= current->length;
341                 }
342         }
343 }
344
345 /*
346 ============
347 Cbuf_Execute
348 ============
349 */
350 extern qbool prvm_runawaycheck;
351 static size_t Cmd_PreprocessString(cmd_state_t *cmd, const char *intext, char *outtext, unsigned maxoutlen, cmd_alias_t *alias);
352 void Cbuf_Execute (cmd_buf_t *cbuf)
353 {
354         cmd_input_t *current;
355         char preprocessed[MAX_INPUTLINE];
356         size_t preprocessed_len;
357         char *firstchar;
358         unsigned int i = 0;
359
360         // LadyHavoc: making sure the tokenizebuffer doesn't get filled up by repeated crashes
361         cbuf->tokenizebufferpos = 0;
362
363         while (!List_Is_Empty(&cbuf->start))
364         {
365                 /*
366                  * Delete the text from the command buffer and move remaining
367                  * commands down. This is necessary because commands (exec, alias)
368                  * can insert data at the beginning of the text buffer
369                  */
370                 current = List_Entry(cbuf->start.next, cmd_input_t, list);
371
372                 /*
373                  * Assume we're rolling with the current command-line and
374                  * always set this false because alias expansion or cbuf insertion
375                  * without a newline may set this true, and cause weirdness.
376                  */
377                 current->pending = false;
378
379                 cbuf->size -= current->length;
380
381                 firstchar = current->text;
382                 while(*firstchar && ISWHITESPACE(*firstchar))
383                         ++firstchar;
384                 if((strncmp(firstchar, "alias", 5)   || !ISWHITESPACE(firstchar[5]))
385                 && (strncmp(firstchar, "bind", 4)    || !ISWHITESPACE(firstchar[4]))
386                 && (strncmp(firstchar, "in_bind", 7) || !ISWHITESPACE(firstchar[7])))
387                 {
388                         if((preprocessed_len = Cmd_PreprocessString(current->source, current->text, preprocessed, sizeof(preprocessed), NULL)))
389                                 Cmd_ExecuteString(current->source, preprocessed, preprocessed_len, src_local, false);
390                 }
391                 else
392                 {
393                         Cmd_ExecuteString(current->source, current->text, current->length, src_local, false);
394                 }
395
396                 // Recycle memory so using WASD doesn't cause a malloc and free
397                 List_Move_Tail(&current->list, &cbuf->free);
398
399                 current = NULL;
400
401                 if (cbuf->wait)
402                 {
403                         /*
404                          * Skip out while text still remains in
405                          * buffer, leaving it for next frame
406                          */
407                         cbuf->wait = false;
408                         break;
409                 }
410
411                 if (++i == 1000000 && prvm_runawaycheck)
412                 {
413                         Con_Printf(CON_WARN "Cbuf_Execute: runaway loop counter hit limit of %d commands, clearing command buffers!\n", i);
414                         Cbuf_Clear(cbuf);
415                 }
416         }
417 }
418
419 /*
420 ===================
421 Cbuf_Frame_Input
422
423 Add them exactly as if they had been typed at the console
424 ===================
425 */
426 static void Cbuf_Frame_Input(void)
427 {
428         char *line;
429
430         if ((line = Sys_ConsoleInput()))
431         {
432                 // bones_was_here: prepending allows a loop such as `alias foo "bar; wait; foo"; foo`
433                 // to be broken with an alias or unalias command
434                 Cbuf_InsertText(cmd_local, line);
435         }
436 }
437
438 void Cbuf_Frame(cmd_buf_t *cbuf)
439 {
440         // check for commands typed to the host
441         Cbuf_Frame_Input();
442
443 //      R_TimeReport("preconsole");
444
445         // execute commands queued with the defer command
446         Cbuf_Execute_Deferred(cbuf);
447         if (cbuf->size)
448         {
449                 SV_LockThreadMutex();
450                 Cbuf_Execute(cbuf);
451                 SV_UnlockThreadMutex();
452         }
453
454 //      R_TimeReport("console");
455 }
456
457 void Cbuf_Clear(cmd_buf_t *cbuf)
458 {
459         while (!List_Is_Empty(&cbuf->start))
460                 List_Move_Tail(cbuf->start.next, &cbuf->free);
461         while (!List_Is_Empty(&cbuf->deferred))
462                 List_Move_Tail(cbuf->deferred.next, &cbuf->free);
463         cbuf->size = 0;
464 }
465
466 /*
467 ==============================================================================
468
469                                                 SCRIPT COMMANDS
470
471 ==============================================================================
472 */
473
474 /*
475 ===============
476 Cmd_StuffCmds_f
477
478 Adds command line parameters as script statements
479 Commands lead with a +, and continue until a - or another +
480 quake +prog jctest.qp +cmd amlev1
481 quake -nosound +cmd amlev1
482 ===============
483 */
484 static void Cmd_StuffCmds_f (cmd_state_t *cmd)
485 {
486         int             i, j, l;
487         // this is for all commandline options combined (and is bounds checked)
488         char    build[MAX_INPUTLINE];
489
490         if (Cmd_Argc (cmd) != 1)
491         {
492                 Con_Print("stuffcmds : execute command line parameters\n");
493                 return;
494         }
495
496         // no reason to run the commandline arguments twice
497         if (host_stuffcmdsrun)
498                 return;
499
500         host_stuffcmdsrun = true;
501         build[0] = 0;
502         l = 0;
503         for (i = 0;i < sys.argc;i++)
504         {
505                 if (sys.argv[i] && sys.argv[i][0] == '+' && (sys.argv[i][1] < '0' || sys.argv[i][1] > '9') && l + strlen(sys.argv[i]) - 1 <= sizeof(build) - 1)
506                 {
507                         j = 1;
508                         while (sys.argv[i][j])
509                                 build[l++] = sys.argv[i][j++];
510                         i++;
511                         for (;i < sys.argc;i++)
512                         {
513                                 if (!sys.argv[i])
514                                         continue;
515                                 if ((sys.argv[i][0] == '+' || sys.argv[i][0] == '-') && (sys.argv[i][1] < '0' || sys.argv[i][1] > '9'))
516                                         break;
517                                 if (l + strlen(sys.argv[i]) + 4 > sizeof(build) - 1)
518                                         break;
519                                 build[l++] = ' ';
520                                 if (strchr(sys.argv[i], ' '))
521                                         build[l++] = '\"';
522                                 for (j = 0;sys.argv[i][j];j++)
523                                         build[l++] = sys.argv[i][j];
524                                 if (strchr(sys.argv[i], ' '))
525                                         build[l++] = '\"';
526                         }
527                         build[l++] = '\n';
528                         i--;
529                 }
530         }
531         // now terminate the combined string and prepend it to the command buffer
532         // we already reserved space for the terminator
533         build[l++] = 0;
534         Cbuf_InsertText (cmd, build);
535 }
536
537 static void Cmd_Exec(cmd_state_t *cmd, const char *filename)
538 {
539         char *f;
540         size_t filenameLen = strlen(filename);
541         qbool isdefaultcfg =
542                 !strcmp(filename, "default.cfg") ||
543                 (filenameLen >= 12 && !strcmp(filename + filenameLen - 12, "/default.cfg"));
544
545         if (!strcmp(filename, "config.cfg"))
546         {
547                 filename = CONFIGFILENAME;
548                 if (Sys_CheckParm("-noconfig"))
549                         return; // don't execute config.cfg
550         }
551
552         f = (char *)FS_LoadFile (filename, tempmempool, false, NULL);
553         if (!f)
554         {
555                 Con_Printf(CON_WARN "couldn't exec %s\n",filename);
556                 return;
557         }
558         Con_Printf("execing %s\n",filename);
559
560         // if executing default.cfg for the first time, lock the cvar defaults
561         // it may seem backwards to insert this text BEFORE the default.cfg
562         // but Cbuf_InsertText inserts before, so this actually ends up after it.
563         if (isdefaultcfg)
564                 Cbuf_InsertText(cmd, "\ncvar_lockdefaults\n");
565
566         Cbuf_InsertText (cmd, f);
567         Mem_Free(f);
568
569         if (isdefaultcfg)
570         {
571                 // special defaults for specific games go here, these execute before default.cfg
572                 // Nehahra pushable crates malfunction in some levels if this is on
573                 // Nehahra NPC AI is confused by blowupfallenzombies
574                 switch(gamemode)
575                 {
576                 case GAME_NORMAL:
577                         Cbuf_InsertText(cmd, "\n"
578 "sv_gameplayfix_blowupfallenzombies 0\n"
579 "sv_gameplayfix_findradiusdistancetobox 0\n"
580 "sv_gameplayfix_grenadebouncedownslopes 0\n"
581 "sv_gameplayfix_slidemoveprojectiles 0\n"
582 "sv_gameplayfix_upwardvelocityclearsongroundflag 0\n"
583 "sv_gameplayfix_setmodelrealbox 0\n"
584 "sv_gameplayfix_droptofloorstartsolid 0\n"
585 "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 0\n"
586 "sv_gameplayfix_noairborncorpse 0\n"
587 "sv_gameplayfix_noairborncorpse_allowsuspendeditems 0\n"
588 "sv_gameplayfix_easierwaterjump 0\n"
589 "sv_gameplayfix_delayprojectiles 0\n"
590 "sv_gameplayfix_multiplethinksperframe 0\n"
591 "sv_gameplayfix_fixedcheckwatertransition 0\n"
592 "sv_gameplayfix_q1bsptracelinereportstexture 0\n"
593 "sv_gameplayfix_swiminbmodels 0\n"
594 "sv_gameplayfix_downtracesupportsongroundflag 0\n"
595 "sys_ticrate 0.01388889\n"
596 "r_shadow_gloss 1\n"
597 "r_shadow_bumpscale_basetexture 0\n"
598 "csqc_polygons_defaultmaterial_nocullface 0\n"
599                                 );
600                         break;
601                 case GAME_NEHAHRA:
602                         Cbuf_InsertText(cmd, "\n"
603 "sv_gameplayfix_blowupfallenzombies 0\n"
604 "sv_gameplayfix_findradiusdistancetobox 0\n"
605 "sv_gameplayfix_grenadebouncedownslopes 0\n"
606 "sv_gameplayfix_slidemoveprojectiles 0\n"
607 "sv_gameplayfix_upwardvelocityclearsongroundflag 0\n"
608 "sv_gameplayfix_setmodelrealbox 0\n"
609 "sv_gameplayfix_droptofloorstartsolid 0\n"
610 "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 0\n"
611 "sv_gameplayfix_noairborncorpse 0\n"
612 "sv_gameplayfix_noairborncorpse_allowsuspendeditems 0\n"
613 "sv_gameplayfix_easierwaterjump 0\n"
614 "sv_gameplayfix_delayprojectiles 0\n"
615 "sv_gameplayfix_multiplethinksperframe 0\n"
616 "sv_gameplayfix_fixedcheckwatertransition 0\n"
617 "sv_gameplayfix_q1bsptracelinereportstexture 0\n"
618 "sv_gameplayfix_swiminbmodels 0\n"
619 "sv_gameplayfix_downtracesupportsongroundflag 0\n"
620 "sys_ticrate 0.01388889\n"
621 "r_shadow_gloss 1\n"
622 "r_shadow_bumpscale_basetexture 0\n"
623 "csqc_polygons_defaultmaterial_nocullface 0\n"
624                                 );
625                         break;
626                 // hipnotic mission pack has issues in their 'friendly monster' ai, which seem to attempt to attack themselves for some reason when findradius() returns non-solid entities.
627                 // hipnotic mission pack has issues with bobbing water entities 'jittering' between different heights on alternate frames at the default 0.0138889 ticrate, 0.02 avoids this issue
628                 // hipnotic mission pack has issues in their proximity mine sticking code, which causes them to bounce off.
629                 case GAME_HIPNOTIC:
630                 case GAME_QUOTH:
631                         Cbuf_InsertText(cmd, "\n"
632 "sv_gameplayfix_blowupfallenzombies 0\n"
633 "sv_gameplayfix_findradiusdistancetobox 0\n"
634 "sv_gameplayfix_grenadebouncedownslopes 0\n"
635 "sv_gameplayfix_slidemoveprojectiles 0\n"
636 "sv_gameplayfix_upwardvelocityclearsongroundflag 0\n"
637 "sv_gameplayfix_setmodelrealbox 0\n"
638 "sv_gameplayfix_droptofloorstartsolid 0\n"
639 "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 0\n"
640 "sv_gameplayfix_noairborncorpse 0\n"
641 "sv_gameplayfix_noairborncorpse_allowsuspendeditems 0\n"
642 "sv_gameplayfix_easierwaterjump 0\n"
643 "sv_gameplayfix_delayprojectiles 0\n"
644 "sv_gameplayfix_multiplethinksperframe 0\n"
645 "sv_gameplayfix_fixedcheckwatertransition 0\n"
646 "sv_gameplayfix_q1bsptracelinereportstexture 0\n"
647 "sv_gameplayfix_swiminbmodels 0\n"
648 "sv_gameplayfix_downtracesupportsongroundflag 0\n"
649 "sys_ticrate 0.02\n"
650 "r_shadow_gloss 1\n"
651 "r_shadow_bumpscale_basetexture 0\n"
652 "csqc_polygons_defaultmaterial_nocullface 0\n"
653                                 );
654                         break;
655                 // rogue mission pack has a guardian boss that does not wake up if findradius returns one of the entities around its spawn area
656                 case GAME_ROGUE:
657                         Cbuf_InsertText(cmd, "\n"
658 "sv_gameplayfix_blowupfallenzombies 0\n"
659 "sv_gameplayfix_findradiusdistancetobox 0\n"
660 "sv_gameplayfix_grenadebouncedownslopes 0\n"
661 "sv_gameplayfix_slidemoveprojectiles 0\n"
662 "sv_gameplayfix_upwardvelocityclearsongroundflag 0\n"
663 "sv_gameplayfix_setmodelrealbox 0\n"
664 "sv_gameplayfix_droptofloorstartsolid 0\n"
665 "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 0\n"
666 "sv_gameplayfix_noairborncorpse 0\n"
667 "sv_gameplayfix_noairborncorpse_allowsuspendeditems 0\n"
668 "sv_gameplayfix_easierwaterjump 0\n"
669 "sv_gameplayfix_delayprojectiles 0\n"
670 "sv_gameplayfix_multiplethinksperframe 0\n"
671 "sv_gameplayfix_fixedcheckwatertransition 0\n"
672 "sv_gameplayfix_q1bsptracelinereportstexture 0\n"
673 "sv_gameplayfix_swiminbmodels 0\n"
674 "sv_gameplayfix_downtracesupportsongroundflag 0\n"
675 "sys_ticrate 0.01388889\n"
676 "r_shadow_gloss 1\n"
677 "r_shadow_bumpscale_basetexture 0\n"
678 "csqc_polygons_defaultmaterial_nocullface 0\n"
679                                 );
680                         break;
681                 case GAME_TENEBRAE:
682                         Cbuf_InsertText(cmd, "\n"
683 "sv_gameplayfix_blowupfallenzombies 0\n"
684 "sv_gameplayfix_findradiusdistancetobox 0\n"
685 "sv_gameplayfix_grenadebouncedownslopes 0\n"
686 "sv_gameplayfix_slidemoveprojectiles 0\n"
687 "sv_gameplayfix_upwardvelocityclearsongroundflag 0\n"
688 "sv_gameplayfix_setmodelrealbox 0\n"
689 "sv_gameplayfix_droptofloorstartsolid 0\n"
690 "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 0\n"
691 "sv_gameplayfix_noairborncorpse 0\n"
692 "sv_gameplayfix_noairborncorpse_allowsuspendeditems 0\n"
693 "sv_gameplayfix_easierwaterjump 0\n"
694 "sv_gameplayfix_delayprojectiles 0\n"
695 "sv_gameplayfix_multiplethinksperframe 0\n"
696 "sv_gameplayfix_fixedcheckwatertransition 0\n"
697 "sv_gameplayfix_q1bsptracelinereportstexture 0\n"
698 "sv_gameplayfix_swiminbmodels 0\n"
699 "sv_gameplayfix_downtracesupportsongroundflag 0\n"
700 "sys_ticrate 0.01388889\n"
701 "r_shadow_gloss 2\n"
702 "r_shadow_bumpscale_basetexture 4\n"
703 "csqc_polygons_defaultmaterial_nocullface 0\n"
704                                 );
705                         break;
706                 case GAME_NEXUIZ:
707                         Cbuf_InsertText(cmd, "\n"
708 "sv_gameplayfix_blowupfallenzombies 1\n"
709 "sv_gameplayfix_findradiusdistancetobox 1\n"
710 "sv_gameplayfix_grenadebouncedownslopes 1\n"
711 "sv_gameplayfix_slidemoveprojectiles 1\n"
712 "sv_gameplayfix_upwardvelocityclearsongroundflag 1\n"
713 "sv_gameplayfix_setmodelrealbox 1\n"
714 "sv_gameplayfix_droptofloorstartsolid 1\n"
715 "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 1\n"
716 "sv_gameplayfix_noairborncorpse 1\n"
717 "sv_gameplayfix_noairborncorpse_allowsuspendeditems 1\n"
718 "sv_gameplayfix_easierwaterjump 1\n"
719 "sv_gameplayfix_delayprojectiles 1\n"
720 "sv_gameplayfix_multiplethinksperframe 1\n"
721 "sv_gameplayfix_fixedcheckwatertransition 1\n"
722 "sv_gameplayfix_q1bsptracelinereportstexture 1\n"
723 "sv_gameplayfix_swiminbmodels 1\n"
724 "sv_gameplayfix_downtracesupportsongroundflag 1\n"
725 "sys_ticrate 0.01388889\n"
726 "sv_gameplayfix_q2airaccelerate 1\n"
727 "sv_gameplayfix_stepmultipletimes 1\n"
728 "csqc_polygons_defaultmaterial_nocullface 1\n"
729 "con_chatsound_team_mask 13\n"
730                                 );
731                         break;
732                 case GAME_XONOTIC:
733                 case GAME_VORETOURNAMENT:
734                         // compatibility for versions prior to 2020-05-25, this can be overridden in newer versions to get the default behavior and be consistent with FTEQW engine
735                         Cbuf_InsertText(cmd, "\n"
736 "csqc_polygons_defaultmaterial_nocullface 1\n"
737 "con_chatsound_team_mask 13\n"
738 "sv_qcstats 1\n"
739 "mod_q1bsp_zero_hullsize_cutoff 8.03125\n"
740                                 );
741                         break;
742                 // Steel Storm: Burning Retribution csqc misinterprets CSQC_InputEvent if type is a value other than 0 or 1
743                 case GAME_STEELSTORM:
744                         Cbuf_InsertText(cmd, "\n"
745 "sv_gameplayfix_blowupfallenzombies 1\n"
746 "sv_gameplayfix_findradiusdistancetobox 1\n"
747 "sv_gameplayfix_grenadebouncedownslopes 1\n"
748 "sv_gameplayfix_slidemoveprojectiles 1\n"
749 "sv_gameplayfix_upwardvelocityclearsongroundflag 1\n"
750 "sv_gameplayfix_setmodelrealbox 1\n"
751 "sv_gameplayfix_droptofloorstartsolid 1\n"
752 "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 1\n"
753 "sv_gameplayfix_noairborncorpse 1\n"
754 "sv_gameplayfix_noairborncorpse_allowsuspendeditems 1\n"
755 "sv_gameplayfix_easierwaterjump 1\n"
756 "sv_gameplayfix_delayprojectiles 1\n"
757 "sv_gameplayfix_multiplethinksperframe 1\n"
758 "sv_gameplayfix_fixedcheckwatertransition 1\n"
759 "sv_gameplayfix_q1bsptracelinereportstexture 1\n"
760 "sv_gameplayfix_swiminbmodels 1\n"
761 "sv_gameplayfix_downtracesupportsongroundflag 1\n"
762 "sys_ticrate 0.01388889\n"
763 "cl_csqc_generatemousemoveevents 0\n"
764 "csqc_polygons_defaultmaterial_nocullface 1\n"
765                                 );
766                         break;
767                 default:
768                         Cbuf_InsertText(cmd, "\n"
769 "sv_gameplayfix_blowupfallenzombies 1\n"
770 "sv_gameplayfix_findradiusdistancetobox 1\n"
771 "sv_gameplayfix_grenadebouncedownslopes 1\n"
772 "sv_gameplayfix_slidemoveprojectiles 1\n"
773 "sv_gameplayfix_upwardvelocityclearsongroundflag 1\n"
774 "sv_gameplayfix_setmodelrealbox 1\n"
775 "sv_gameplayfix_droptofloorstartsolid 1\n"
776 "sv_gameplayfix_droptofloorstartsolid_nudgetocorrect 1\n"
777 "sv_gameplayfix_noairborncorpse 1\n"
778 "sv_gameplayfix_noairborncorpse_allowsuspendeditems 1\n"
779 "sv_gameplayfix_easierwaterjump 1\n"
780 "sv_gameplayfix_delayprojectiles 1\n"
781 "sv_gameplayfix_multiplethinksperframe 1\n"
782 "sv_gameplayfix_fixedcheckwatertransition 1\n"
783 "sv_gameplayfix_q1bsptracelinereportstexture 1\n"
784 "sv_gameplayfix_swiminbmodels 1\n"
785 "sv_gameplayfix_downtracesupportsongroundflag 1\n"
786 "sys_ticrate 0.01388889\n"
787 "csqc_polygons_defaultmaterial_nocullface 0\n"
788                                 );
789                         break;
790                 }
791         }
792 }
793
794 /*
795 ===============
796 Cmd_Exec_f
797 ===============
798 */
799 static void Cmd_Exec_f (cmd_state_t *cmd)
800 {
801         fssearch_t *s;
802         int i;
803
804         if (Cmd_Argc(cmd) != 2)
805         {
806                 Con_Print("exec <filename> : execute a script file\n");
807                 return;
808         }
809
810         s = FS_Search(Cmd_Argv(cmd, 1), true, true, NULL);
811         if(!s || !s->numfilenames)
812         {
813                 Con_Printf(CON_WARN "couldn't exec %s\n",Cmd_Argv(cmd, 1));
814                 return;
815         }
816
817         for(i = 0; i < s->numfilenames; ++i)
818                 Cmd_Exec(cmd, s->filenames[i]);
819
820         FS_FreeSearch(s);
821 }
822
823
824 /*
825 ===============
826 Cmd_Echo_f
827
828 Just prints the rest of the line to the console
829 ===============
830 */
831 static void Cmd_Echo_f (cmd_state_t *cmd)
832 {
833         int             i;
834
835         for (i=1 ; i<Cmd_Argc(cmd) ; i++)
836                 Con_Printf("%s ",Cmd_Argv(cmd, i));
837         Con_Print("\n");
838 }
839
840 // DRESK - 5/14/06
841 // Support Doom3-style Toggle Console Command
842 /*
843 ===============
844 Cmd_Toggle_f
845
846 Toggles a specified console variable amongst the values specified (default is 0 and 1)
847 ===============
848 */
849 static void Cmd_Toggle_f(cmd_state_t *cmd)
850 {
851         // Acquire Number of Arguments
852         int nNumArgs = Cmd_Argc(cmd);
853
854         if(nNumArgs == 1)
855                 // No Arguments Specified; Print Usage
856                 Con_Print("Toggle Console Variable - Usage\n  toggle <variable> - toggles between 0 and 1\n  toggle <variable> <value> - toggles between 0 and <value>\n  toggle <variable> [string 1] [string 2]...[string n] - cycles through all strings\n");
857         else
858         { // Correct Arguments Specified
859                 // Acquire Potential CVar
860                 cvar_t* cvCVar = Cvar_FindVar(cmd->cvars, Cmd_Argv(cmd, 1), cmd->cvars_flagsmask);
861
862                 if(cvCVar != NULL)
863                 { // Valid CVar
864                         if(nNumArgs == 2)
865                         { // Default Usage
866                                 if(cvCVar->integer)
867                                         Cvar_SetValueQuick(cvCVar, 0);
868                                 else
869                                         Cvar_SetValueQuick(cvCVar, 1);
870                         }
871                         else
872                         if(nNumArgs == 3)
873                         { // 0 and Specified Usage
874                                 if(cvCVar->integer == atoi(Cmd_Argv(cmd, 2) ) )
875                                         // CVar is Specified Value; // Reset to 0
876                                         Cvar_SetValueQuick(cvCVar, 0);
877                                 else
878                                 if(cvCVar->integer == 0)
879                                         // CVar is 0; Specify Value
880                                         Cvar_SetQuick(cvCVar, Cmd_Argv(cmd, 2) );
881                                 else
882                                         // CVar does not match; Reset to 0
883                                         Cvar_SetValueQuick(cvCVar, 0);
884                         }
885                         else
886                         { // Variable Values Specified
887                                 int nCnt;
888                                 int bFound = 0;
889
890                                 for(nCnt = 2; nCnt < nNumArgs; nCnt++)
891                                 { // Cycle through Values
892                                         if( strcmp(cvCVar->string, Cmd_Argv(cmd, nCnt) ) == 0)
893                                         { // Current Value Located; Increment to Next
894                                                 if( (nCnt + 1) == nNumArgs)
895                                                         // Max Value Reached; Reset
896                                                         Cvar_SetQuick(cvCVar, Cmd_Argv(cmd, 2) );
897                                                 else
898                                                         // Next Value
899                                                         Cvar_SetQuick(cvCVar, Cmd_Argv(cmd, nCnt + 1) );
900
901                                                 // End Loop
902                                                 nCnt = nNumArgs;
903                                                 // Assign Found
904                                                 bFound = 1;
905                                         }
906                                 }
907                                 if(!bFound)
908                                         // Value not Found; Reset to Original
909                                         Cvar_SetQuick(cvCVar, Cmd_Argv(cmd, 2) );
910                         }
911
912                 }
913                 else
914                 { // Invalid CVar
915                         Con_Printf(CON_WARN "ERROR : CVar '%s' not found\n", Cmd_Argv(cmd, 1) );
916                 }
917         }
918 }
919
920 /*
921 ===============
922 Cmd_Alias_f
923
924 Creates a new command that executes a command string (possibly ; seperated)
925 ===============
926 */
927 static void Cmd_Alias_f (cmd_state_t *cmd)
928 {
929         cmd_alias_t     *a;
930         char            line[MAX_INPUTLINE];
931         int                     i, c;
932         const char              *s;
933         size_t          alloclen;
934
935         if (Cmd_Argc(cmd) == 1)
936         {
937                 Con_Print("Current alias commands:\n");
938                 for (a = cmd->userdefined->alias ; a ; a=a->next)
939                         Con_Printf("%s : %s", a->name, a->value);
940                 return;
941         }
942
943         s = Cmd_Argv(cmd, 1);
944         if (strlen(s) >= MAX_ALIAS_NAME)
945         {
946                 Con_Print(CON_WARN "Alias name is too long\n");
947                 return;
948         }
949
950         // if the alias already exists, reuse it
951         for (a = cmd->userdefined->alias ; a ; a=a->next)
952         {
953                 if (!strcmp(s, a->name))
954                 {
955                         Z_Free (a->value);
956                         break;
957                 }
958         }
959
960         if (!a)
961         {
962                 cmd_alias_t *prev, *current;
963
964                 a = (cmd_alias_t *)Z_Malloc (sizeof(cmd_alias_t));
965                 dp_strlcpy (a->name, s, sizeof (a->name));
966                 // insert it at the right alphanumeric position
967                 for( prev = NULL, current = cmd->userdefined->alias ; current && strcmp( current->name, a->name ) < 0 ; prev = current, current = current->next )
968                         ;
969                 if( prev ) {
970                         prev->next = a;
971                 } else {
972                         cmd->userdefined->alias = a;
973                 }
974                 a->next = current;
975         }
976
977
978 // copy the rest of the command line
979         line[0] = 0;            // start out with a null string
980         c = Cmd_Argc(cmd);
981         for (i=2 ; i < c ; i++)
982         {
983                 if (i != 2)
984                         dp_strlcat (line, " ", sizeof (line));
985                 dp_strlcat (line, Cmd_Argv(cmd, i), sizeof (line));
986         }
987         dp_strlcat (line, "\n", sizeof (line));
988
989         alloclen = strlen (line) + 1;
990         if(alloclen >= 2)
991                 line[alloclen - 2] = '\n'; // to make sure a newline is appended even if too long
992         a->value = (char *)Z_Malloc (alloclen);
993         memcpy (a->value, line, alloclen);
994 }
995
996 /*
997 ===============
998 Cmd_UnAlias_f
999
1000 Remove existing aliases.
1001 ===============
1002 */
1003 static void Cmd_UnAlias_f (cmd_state_t *cmd)
1004 {
1005         cmd_alias_t     *a, *p;
1006         int i;
1007         const char *s;
1008
1009         if(Cmd_Argc(cmd) == 1)
1010         {
1011                 Con_Print("unalias: Usage: unalias alias1 [alias2 ...]\n");
1012                 return;
1013         }
1014
1015         for(i = 1; i < Cmd_Argc(cmd); ++i)
1016         {
1017                 s = Cmd_Argv(cmd, i);
1018                 p = NULL;
1019                 for(a = cmd->userdefined->alias; a; p = a, a = a->next)
1020                 {
1021                         if(!strcmp(s, a->name))
1022                         {
1023                                 if (a->initstate) // we can not remove init aliases
1024                                         continue;
1025                                 if(a == cmd->userdefined->alias)
1026                                         cmd->userdefined->alias = a->next;
1027                                 if(p)
1028                                         p->next = a->next;
1029                                 Z_Free(a->value);
1030                                 Z_Free(a);
1031                                 break;
1032                         }
1033                 }
1034                 if(!a)
1035                         Con_Printf("unalias: %s alias not found\n", s);
1036         }
1037 }
1038
1039 /*
1040 =============================================================================
1041
1042                                         COMMAND EXECUTION
1043
1044 =============================================================================
1045 */
1046
1047 static const char *Cmd_GetDirectCvarValue(cmd_state_t *cmd, const char *varname, cmd_alias_t *alias, qbool *is_multiple)
1048 {
1049         cvar_t *cvar;
1050         long argno;
1051         char *endptr;
1052         static char vabuf[1024]; // cmd_mutex
1053
1054         if(is_multiple)
1055                 *is_multiple = false;
1056
1057         if(!varname || !*varname)
1058                 return NULL;
1059
1060         if(alias)
1061         {
1062                 if(!strcmp(varname, "*"))
1063                 {
1064                         if(is_multiple)
1065                                 *is_multiple = true;
1066                         return Cmd_Args(cmd);
1067                 }
1068                 else if(!strcmp(varname, "#"))
1069                 {
1070                         return va(vabuf, sizeof(vabuf), "%d", Cmd_Argc(cmd));
1071                 }
1072                 else if(varname[strlen(varname) - 1] == '-')
1073                 {
1074                         argno = strtol(varname, &endptr, 10);
1075                         if(endptr == varname + strlen(varname) - 1)
1076                         {
1077                                 // whole string is a number, apart from the -
1078                                 const char *p = Cmd_Args(cmd);
1079                                 for(; argno > 1; --argno)
1080                                         if(!COM_ParseToken_Console(&p))
1081                                                 break;
1082                                 if(p)
1083                                 {
1084                                         if(is_multiple)
1085                                                 *is_multiple = true;
1086
1087                                         // kill pre-argument whitespace
1088                                         for (;*p && ISWHITESPACE(*p);p++)
1089                                                 ;
1090
1091                                         return p;
1092                                 }
1093                         }
1094                 }
1095                 else
1096                 {
1097                         argno = strtol(varname, &endptr, 10);
1098                         if(*endptr == 0)
1099                         {
1100                                 // whole string is a number
1101                                 // NOTE: we already made sure we don't have an empty cvar name!
1102                                 if(argno >= 0 && argno < Cmd_Argc(cmd))
1103                                         return Cmd_Argv(cmd, argno);
1104                         }
1105                 }
1106         }
1107
1108         if((cvar = Cvar_FindVar(cmd->cvars, varname, cmd->cvars_flagsmask)) && !(cvar->flags & CF_PRIVATE))
1109                 return cvar->string;
1110
1111         return NULL;
1112 }
1113
1114 qbool Cmd_QuoteString(char *out, size_t outlen, const char *in, const char *quoteset, qbool putquotes)
1115 {
1116         qbool quote_quot = !!strchr(quoteset, '"');
1117         qbool quote_backslash = !!strchr(quoteset, '\\');
1118         qbool quote_dollar = !!strchr(quoteset, '$');
1119
1120         if(putquotes)
1121         {
1122                 if(outlen <= 2)
1123                 {
1124                         *out++ = 0;
1125                         return false;
1126                 }
1127                 *out++ = '"'; --outlen;
1128                 --outlen;
1129         }
1130
1131         while(*in)
1132         {
1133                 if(*in == '"' && quote_quot)
1134                 {
1135                         if(outlen <= 2)
1136                                 goto fail;
1137                         *out++ = '\\'; --outlen;
1138                         *out++ = '"'; --outlen;
1139                 }
1140                 else if(*in == '\\' && quote_backslash)
1141                 {
1142                         if(outlen <= 2)
1143                                 goto fail;
1144                         *out++ = '\\'; --outlen;
1145                         *out++ = '\\'; --outlen;
1146                 }
1147                 else if(*in == '$' && quote_dollar)
1148                 {
1149                         if(outlen <= 2)
1150                                 goto fail;
1151                         *out++ = '$'; --outlen;
1152                         *out++ = '$'; --outlen;
1153                 }
1154                 else
1155                 {
1156                         if(outlen <= 1)
1157                                 goto fail;
1158                         *out++ = *in; --outlen;
1159                 }
1160                 ++in;
1161         }
1162         if(putquotes)
1163                 *out++ = '"';
1164         *out++ = 0;
1165         return true;
1166 fail:
1167         if(putquotes)
1168                 *out++ = '"';
1169         *out++ = 0;
1170         return false;
1171 }
1172
1173 static const char *Cmd_GetCvarValue(cmd_state_t *cmd, const char *var, size_t varlen, cmd_alias_t *alias)
1174 {
1175         static char varname[MAX_INPUTLINE]; // cmd_mutex
1176         static char varval[MAX_INPUTLINE]; // cmd_mutex
1177         const char *varstr = NULL;
1178         char *varfunc;
1179         qbool required = false;
1180         qbool optional = false;
1181         static char asis[] = "asis"; // just to suppress const char warnings
1182
1183         if(varlen >= MAX_INPUTLINE)
1184                 varlen = MAX_INPUTLINE - 1;
1185         memcpy(varname, var, varlen);
1186         varname[varlen] = 0;
1187         varfunc = strchr(varname, ' ');
1188
1189         if(varfunc)
1190         {
1191                 *varfunc = 0;
1192                 ++varfunc;
1193         }
1194
1195         if(*var == 0)
1196         {
1197                 // empty cvar name?
1198                 if(alias)
1199                         Con_Printf(CON_WARN "Warning: Could not expand $ in alias %s\n", alias->name);
1200                 else
1201                         Con_Printf(CON_WARN "Warning: Could not expand $\n");
1202                 return "$";
1203         }
1204
1205         if(varfunc)
1206         {
1207                 char *p;
1208                 // ? means optional
1209                 while((p = strchr(varfunc, '?')))
1210                 {
1211                         optional = true;
1212                         memmove(p, p+1, strlen(p)); // with final NUL
1213                 }
1214                 // ! means required
1215                 while((p = strchr(varfunc, '!')))
1216                 {
1217                         required = true;
1218                         memmove(p, p+1, strlen(p)); // with final NUL
1219                 }
1220                 // kill spaces
1221                 while((p = strchr(varfunc, ' ')))
1222                 {
1223                         memmove(p, p+1, strlen(p)); // with final NUL
1224                 }
1225                 // if no function is left, NULL it
1226                 if(!*varfunc)
1227                         varfunc = NULL;
1228         }
1229
1230         if(varname[0] == '$')
1231                 varstr = Cmd_GetDirectCvarValue(cmd, Cmd_GetDirectCvarValue(cmd, varname + 1, alias, NULL), alias, NULL);
1232         else
1233         {
1234                 qbool is_multiple = false;
1235                 // Exception: $* and $n- don't use the quoted form by default
1236                 varstr = Cmd_GetDirectCvarValue(cmd, varname, alias, &is_multiple);
1237                 if(is_multiple)
1238                         if(!varfunc)
1239                                 varfunc = asis;
1240         }
1241
1242         if(!varstr)
1243         {
1244                 if(required)
1245                 {
1246                         if(alias)
1247                                 Con_Printf(CON_ERROR "Error: Could not expand $%s in alias %s\n", varname, alias->name);
1248                         else
1249                                 Con_Printf(CON_ERROR "Error: Could not expand $%s\n", varname);
1250                         return NULL;
1251                 }
1252                 else if(optional)
1253                 {
1254                         return "";
1255                 }
1256                 else
1257                 {
1258                         if(alias)
1259                                 Con_Printf(CON_WARN "Warning: Could not expand $%s in alias %s\n", varname, alias->name);
1260                         else
1261                                 Con_Printf(CON_WARN "Warning: Could not expand $%s\n", varname);
1262                         dpsnprintf(varval, sizeof(varval), "$%s", varname);
1263                         return varval;
1264                 }
1265         }
1266
1267         if(!varfunc || !strcmp(varfunc, "q")) // note: quoted form is default, use "asis" to override!
1268         {
1269                 // quote it so it can be used inside double quotes
1270                 // we just need to replace " by \", and of course, double backslashes
1271                 Cmd_QuoteString(varval, sizeof(varval), varstr, "\"\\", false);
1272                 return varval;
1273         }
1274         else if(!strcmp(varfunc, "asis"))
1275         {
1276                 return varstr;
1277         }
1278         else
1279                 Con_Printf("Unknown variable function %s\n", varfunc);
1280
1281         return varstr;
1282 }
1283
1284 /**
1285 Cmd_PreprocessString
1286
1287 Preprocesses strings and replaces $*, $param#, $cvar accordingly. Also strips comments.
1288 Returns the number of bytes written to *outtext excluding the \0 terminator.
1289 */
1290 static size_t Cmd_PreprocessString(cmd_state_t *cmd, const char *intext, char *outtext, unsigned maxoutlen, cmd_alias_t *alias)
1291 {
1292         const char *in;
1293         size_t eat, varlen;
1294         unsigned outlen;
1295         const char *val;
1296
1297         // don't crash if there's no room in the outtext buffer
1298         if( maxoutlen == 0 ) {
1299                 return 0;
1300         }
1301         maxoutlen--; // because of \0
1302
1303         in = intext;
1304         outlen = 0;
1305
1306         while( *in && outlen < maxoutlen ) {
1307                 if( *in == '$' ) {
1308                         // this is some kind of expansion, see what comes after the $
1309                         in++;
1310
1311                         // The console does the following preprocessing:
1312                         //
1313                         // - $$ is transformed to a single dollar sign.
1314                         // - $var or ${var} are expanded to the contents of the named cvar,
1315                         //   with quotation marks and backslashes quoted so it can safely
1316                         //   be used inside quotation marks (and it should always be used
1317                         //   that way)
1318                         // - ${var asis} inserts the cvar value as is, without doing this
1319                         //   quoting
1320                         // - ${var ?} silently expands to the empty string if
1321                         //   $var does not exist
1322                         // - ${var !} fails expansion and executes nothing if
1323                         //   $var does not exist
1324                         // - prefix the cvar name with a dollar sign to do indirection;
1325                         //   for example, if $x has the value timelimit, ${$x} will return
1326                         //   the value of $timelimit
1327                         // - when expanding an alias, the special variable name $* refers
1328                         //   to all alias parameters, and a number refers to that numbered
1329                         //   alias parameter, where the name of the alias is $0, the first
1330                         //   parameter is $1 and so on; as a special case, $* inserts all
1331                         //   parameters, without extra quoting, so one can use $* to just
1332                         //   pass all parameters around. All parameters starting from $n
1333                         //   can be referred to as $n- (so $* is equivalent to $1-).
1334                         // - ${* q} and ${n- q} force quoting anyway
1335                         //
1336                         // Note: when expanding an alias, cvar expansion is done in the SAME step
1337                         // as alias expansion so that alias parameters or cvar values containing
1338                         // dollar signs have no unwanted bad side effects. However, this needs to
1339                         // be accounted for when writing complex aliases. For example,
1340                         //   alias foo "set x NEW; echo $x"
1341                         // actually expands to
1342                         //   "set x NEW; echo OLD"
1343                         // and will print OLD! To work around this, use a second alias:
1344                         //   alias foo "set x NEW; foo2"
1345                         //   alias foo2 "echo $x"
1346                         //
1347                         // Also note: lines starting with alias are exempt from cvar expansion.
1348                         // If you want cvar expansion, write "alias" instead:
1349                         //
1350                         //   set x 1
1351                         //   alias foo "echo $x"
1352                         //   "alias" bar "echo $x"
1353                         //   set x 2
1354                         //
1355                         // foo will print 2, because the variable $x will be expanded when the alias
1356                         // gets expanded. bar will print 1, because the variable $x was expanded
1357                         // at definition time. foo can be equivalently defined as
1358                         //
1359                         //   "alias" foo "echo $$x"
1360                         //
1361                         // because at definition time, $$ will get replaced to a single $.
1362
1363                         if( *in == '$' ) {
1364                                 val = "$";
1365                                 eat = 1;
1366                         } else if(*in == '{') {
1367                                 varlen = strcspn(in + 1, "}");
1368                                 if(in[varlen + 1] == '}')
1369                                 {
1370                                         val = Cmd_GetCvarValue(cmd, in + 1, varlen, alias);
1371                                         if(!val)
1372                                                 return 0;
1373                                         eat = varlen + 2;
1374                                 }
1375                                 else
1376                                 {
1377                                         // ran out of data?
1378                                         val = NULL;
1379                                         eat = varlen + 1;
1380                                 }
1381                         } else {
1382                                 varlen = strspn(in, "#*0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-");
1383                                 val = Cmd_GetCvarValue(cmd, in, varlen, alias);
1384                                 if(!val)
1385                                         return 0;
1386                                 eat = varlen;
1387                         }
1388                         if(val)
1389                         {
1390                                 // insert the cvar value
1391                                 while(*val && outlen < maxoutlen)
1392                                         outtext[outlen++] = *val++;
1393                                 in += eat;
1394                         }
1395                         else
1396                         {
1397                                 // copy the unexpanded text
1398                                 outtext[outlen++] = '$';
1399                                 while(eat && outlen < maxoutlen)
1400                                 {
1401                                         outtext[outlen++] = *in++;
1402                                         --eat;
1403                                 }
1404                         }
1405                 }
1406                 else 
1407                         outtext[outlen++] = *in++;
1408         }
1409         outtext[outlen] = '\0';
1410         return outlen;
1411 }
1412
1413 /*
1414 ============
1415 Cmd_ExecuteAlias
1416
1417 Called for aliases and fills in the alias into the cbuffer
1418 ============
1419 */
1420 static void Cmd_ExecuteAlias (cmd_state_t *cmd, cmd_alias_t *alias)
1421 {
1422         static char buffer[ MAX_INPUTLINE ]; // cmd_mutex
1423         static char buffer2[ MAX_INPUTLINE ]; // cmd_mutex
1424         qbool ret = Cmd_PreprocessString( cmd, alias->value, buffer, sizeof(buffer) - 2, alias );
1425         if(!ret)
1426                 return;
1427         // insert at start of command buffer, so that aliases execute in order
1428         // (fixes bug introduced by Black on 20050705)
1429
1430         // Note: Cbuf_PreprocessString will be called on this string AGAIN! So we
1431         // have to make sure that no second variable expansion takes place, otherwise
1432         // alias parameters containing dollar signs can have bad effects.
1433         Cmd_QuoteString(buffer2, sizeof(buffer2), buffer, "$", false);
1434         Cbuf_InsertText(cmd, buffer2);
1435 }
1436
1437 /*
1438 ========
1439 Cmd_List
1440
1441         CmdList Added by EvilTypeGuy eviltypeguy@qeradiant.com
1442         Thanks to Matthias "Maddes" Buecher, http://www.inside3d.com/qip/
1443
1444 ========
1445 */
1446 static void Cmd_List_f (cmd_state_t *cmd)
1447 {
1448         cmd_function_t *func;
1449         const char *partial;
1450         size_t len;
1451         int count;
1452         qbool ispattern;
1453
1454         if (Cmd_Argc(cmd) > 1)
1455         {
1456                 partial = Cmd_Argv(cmd, 1);
1457                 len = strlen(partial);
1458                 ispattern = (strchr(partial, '*') || strchr(partial, '?'));
1459         }
1460         else
1461         {
1462                 partial = NULL;
1463                 len = 0;
1464                 ispattern = false;
1465         }
1466
1467         count = 0;
1468         for (func = cmd->userdefined->qc_functions; func; func = func->next)
1469         {
1470                 if (partial && (ispattern ? !matchpattern_with_separator(func->name, partial, false, "", false) : strncmp(partial, func->name, len)))
1471                         continue;
1472                 Con_Printf("%s : %s\n", func->name, func->description);
1473                 count++;
1474         }
1475         for (func = cmd->engine_functions; func; func = func->next)
1476         {
1477                 if (partial && (ispattern ? !matchpattern_with_separator(func->name, partial, false, "", false) : strncmp(partial, func->name, len)))
1478                         continue;
1479                 Con_Printf("%s : %s\n", func->name, func->description);
1480                 count++;
1481         }
1482
1483         if (len)
1484         {
1485                 if(ispattern)
1486                         Con_Printf("%i Command%s matching \"%s\"\n\n", count, (count > 1) ? "s" : "", partial);
1487                 else
1488                         Con_Printf("%i Command%s beginning with \"%s\"\n\n", count, (count > 1) ? "s" : "", partial);
1489         }
1490         else
1491                 Con_Printf("%i Command%s\n\n", count, (count > 1) ? "s" : "");
1492 }
1493
1494 static void Cmd_Apropos_f(cmd_state_t *cmd)
1495 {
1496         cmd_function_t *func;
1497         cvar_t *cvar;
1498         cmd_alias_t *alias;
1499         const char *partial;
1500         int count;
1501         qbool ispattern;
1502         char vabuf[1024];
1503
1504         if (Cmd_Argc(cmd) > 1)
1505                 partial = Cmd_Args(cmd);
1506         else
1507         {
1508                 Con_Printf("usage: %s <string>\n",Cmd_Argv(cmd, 0));
1509                 return;
1510         }
1511
1512         ispattern = partial && (strchr(partial, '*') || strchr(partial, '?'));
1513         if(!ispattern)
1514                 partial = va(vabuf, sizeof(vabuf), "*%s*", partial);
1515
1516         count = 0;
1517         for (cvar = cmd->cvars->vars; cvar; cvar = cvar->next)
1518         {
1519                 if (matchpattern_with_separator(cvar->name, partial, true, "", false) ||
1520                     matchpattern_with_separator(cvar->description, partial, true, "", false))
1521                 {
1522                         Con_Printf ("cvar ");
1523                         Cvar_PrintHelp(cvar, cvar->name, true);
1524                         count++;
1525                 }
1526                 for (char **cvar_alias = cvar->aliases; cvar_alias && *cvar_alias; cvar_alias++)
1527                 {
1528                         if (matchpattern_with_separator(*cvar_alias, partial, true, "", false))
1529                         {
1530                                 Con_Printf ("cvar ");
1531                                 Cvar_PrintHelp(cvar, *cvar_alias, true);
1532                                 count++;
1533                         }
1534                 }
1535         }
1536         for (func = cmd->userdefined->qc_functions; func; func = func->next)
1537         {
1538                 if (!matchpattern_with_separator(func->name, partial, true, "", false))
1539                         if (!matchpattern_with_separator(func->description, partial, true, "", false))
1540                                 continue;
1541                 Con_Printf("command ^2%s^7: %s\n", func->name, func->description);
1542                 count++;
1543         }
1544         for (func = cmd->engine_functions; func; func = func->next)
1545         {
1546                 if (!matchpattern_with_separator(func->name, partial, true, "", false))
1547                 if (!matchpattern_with_separator(func->description, partial, true, "", false))
1548                         continue;
1549                 Con_Printf("command ^2%s^7: %s\n", func->name, func->description);
1550                 count++;
1551         }
1552         for (alias = cmd->userdefined->alias; alias; alias = alias->next)
1553         {
1554                 // procede here a bit differently as an alias value always got a final \n
1555                 if (!matchpattern_with_separator(alias->name, partial, true, "", false))
1556                 if (!matchpattern_with_separator(alias->value, partial, true, "\n", false)) // when \n is as separator wildcards don't match it
1557                         continue;
1558                 Con_Printf("alias ^5%s^7: %s", alias->name, alias->value); // do not print an extra \n
1559                 count++;
1560         }
1561         Con_Printf("%i result%s\n\n", count, (count > 1) ? "s" : "");
1562 }
1563
1564 static cmd_state_t *Cmd_AddInterpreter(cmd_buf_t *cbuf, cvar_state_t *cvars, unsigned cvars_flagsmask, unsigned cmds_flagsmask, cmd_userdefined_t *userdefined)
1565 {
1566         cmd_state_t *cmd = (cmd_state_t *)Mem_Alloc(tempmempool, sizeof(cmd_state_t));
1567         
1568         cmd->mempool = Mem_AllocPool("commands", 0, NULL);
1569         // space for commands and script files
1570         cmd->cbuf = cbuf;
1571         cmd->null_string = "";
1572
1573         cmd->cvars = cvars;
1574         cmd->cvars_flagsmask = cvars_flagsmask;
1575         cmd->cmd_flagsmask = cmds_flagsmask;
1576         cmd->userdefined = userdefined;
1577
1578         return cmd;
1579 }
1580
1581 /*
1582 ============
1583 Cmd_Init
1584 ============
1585 */
1586 void Cmd_Init(void)
1587 {
1588         cmd_buf_t *cbuf;
1589         unsigned cvars_flagsmask, cmds_flagsmask;
1590
1591         cbuf_mempool = Mem_AllocPool("Command buffer", 0, NULL);
1592         cbuf = (cmd_buf_t *)Mem_Alloc(cbuf_mempool, sizeof(cmd_buf_t));
1593         cbuf->maxsize = CMDBUFSIZE;
1594         cbuf->lock = Thread_CreateMutex();
1595         cbuf->wait = false;
1596         host.cbuf = cbuf;
1597
1598         cbuf->start.prev = cbuf->start.next = &(cbuf->start);
1599         cbuf->deferred.prev = cbuf->deferred.next = &(cbuf->deferred);
1600         cbuf->free.prev = cbuf->free.next = &(cbuf->free);
1601
1602         // FIXME: Get rid of cmd_iter_all eventually. This is just a hack to reduce the amount of work to make the interpreters dynamic.
1603         cmd_iter_all = (cmd_iter_t *)Mem_Alloc(tempmempool, sizeof(cmd_iter_t) * 3);
1604
1605         // local console
1606         if (cls.state == ca_dedicated)
1607         {
1608                 cvars_flagsmask = CF_SERVER;
1609                 cmds_flagsmask = CF_SERVER | CF_SERVER_FROM_CLIENT;
1610         }
1611         else
1612         {
1613                 cvars_flagsmask = CF_CLIENT | CF_SERVER;
1614                 cmds_flagsmask = CF_CLIENT | CF_SERVER | CF_CLIENT_FROM_SERVER | CF_SERVER_FROM_CLIENT;
1615         }
1616         cmd_iter_all[0].cmd = cmd_local = Cmd_AddInterpreter(cbuf, &cvars_all, cvars_flagsmask, cmds_flagsmask, &cmd_userdefined_all);
1617         cmd_local->Handle = Cmd_CL_Callback;
1618
1619         // server commands received from clients have no reason to access cvars, cvar expansion seems perilous.
1620         cmd_iter_all[1].cmd = cmd_serverfromclient = Cmd_AddInterpreter(cbuf, &cvars_null, 0, CF_SERVER_FROM_CLIENT | CF_USERINFO, &cmd_userdefined_null);
1621         cmd_serverfromclient->Handle = Cmd_SV_Callback;
1622
1623         cmd_iter_all[2].cmd = NULL;
1624 //
1625 // register our commands
1626 //
1627         // client-only commands
1628         Cmd_AddCommand(CF_SHARED, "wait", Cmd_Wait_f, "make script execution wait for next rendered frame");
1629
1630         // maintenance commands used for upkeep of cvars and saved configs
1631         Cmd_AddCommand(CF_SHARED, "stuffcmds", Cmd_StuffCmds_f, "execute commandline parameters (must be present in quake.rc script)");
1632         Cmd_AddCommand(CF_SHARED, "cvar_lockdefaults", Cvar_LockDefaults_f, "stores the current values of all cvars into their default values, only used once during startup after parsing default.cfg");
1633         Cmd_AddCommand(CF_SHARED, "cvar_resettodefaults_all", Cvar_ResetToDefaults_All_f, "sets all cvars to their locked default values");
1634         Cmd_AddCommand(CF_SHARED, "cvar_resettodefaults_nosaveonly", Cvar_ResetToDefaults_NoSaveOnly_f, "sets all non-saved cvars to their locked default values (variables that will not be saved to config.cfg)");
1635         Cmd_AddCommand(CF_SHARED, "cvar_resettodefaults_saveonly", Cvar_ResetToDefaults_SaveOnly_f, "sets all saved cvars to their locked default values (variables that will be saved to config.cfg)");
1636
1637         // general console commands used in multiple environments
1638         Cmd_AddCommand(CF_SHARED, "exec", Cmd_Exec_f, "execute a script file");
1639         Cmd_AddCommand(CF_SHARED, "echo",Cmd_Echo_f, "print a message to the console (useful in scripts)");
1640         Cmd_AddCommand(CF_SHARED, "alias",Cmd_Alias_f, "create a script function (parameters are passed in as $X (being X a number), $* for all parameters, $X- for all parameters starting from $X). Without arguments show the list of all alias");
1641         Cmd_AddCommand(CF_SHARED, "unalias",Cmd_UnAlias_f, "remove an alias");
1642         Cmd_AddCommand(CF_SHARED, "set", Cvar_Set_f, "create or change the value of a console variable");
1643         Cmd_AddCommand(CF_SHARED, "seta", Cvar_SetA_f, "create or change the value of a console variable that will be saved to config.cfg");
1644         Cmd_AddCommand(CF_SHARED, "unset", Cvar_Del_f, "delete a cvar (does not work for static ones like _cl_name, or read-only ones)");
1645
1646 #ifdef FILLALLCVARSWITHRUBBISH
1647         Cmd_AddCommand(CF_SHARED, "fillallcvarswithrubbish", Cvar_FillAll_f, "fill all cvars with a specified number of characters to provoke buffer overruns");
1648 #endif /* FILLALLCVARSWITHRUBBISH */
1649
1650         // 2000-01-09 CmdList, CvarList commands By Matthias "Maddes" Buecher
1651         // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
1652         Cmd_AddCommand(CF_SHARED, "cmdlist", Cmd_List_f, "lists all console commands beginning with the specified prefix or matching the specified wildcard pattern");
1653         Cmd_AddCommand(CF_SHARED, "cvarlist", Cvar_List_f, "lists all console variables beginning with the specified prefix or matching the specified wildcard pattern");
1654         Cmd_AddCommand(CF_SHARED, "apropos", Cmd_Apropos_f, "lists all console variables/commands/aliases containing the specified string in the name or description");
1655         Cmd_AddCommand(CF_SHARED, "find", Cmd_Apropos_f, "lists all console variables/commands/aliases containing the specified string in the name or description");
1656
1657         Cmd_AddCommand(CF_SHARED, "defer", Cmd_Defer_f, "execute a command in the future");
1658
1659         // DRESK - 5/14/06
1660         // Support Doom3-style Toggle Command
1661         Cmd_AddCommand(CF_SHARED | CF_CLIENT_FROM_SERVER, "toggle", Cmd_Toggle_f, "toggles a console variable's values (use for more info)");
1662 }
1663
1664 /*
1665 ============
1666 Cmd_Shutdown
1667 ============
1668 */
1669 void Cmd_Shutdown(void)
1670 {
1671         cmd_iter_t *cmd_iter;
1672         for (cmd_iter = cmd_iter_all; cmd_iter->cmd; cmd_iter++)
1673         {
1674                 cmd_state_t *cmd = cmd_iter->cmd;
1675
1676                 if (cmd->cbuf->lock)
1677                 {
1678                         // we usually have this locked when we get here from Host_Quit_f
1679                         Cbuf_Unlock(cmd->cbuf);
1680                 }
1681
1682                 Mem_FreePool(&cmd->mempool);
1683         }
1684 }
1685
1686 /*
1687 ============
1688 Cmd_TokenizeString
1689
1690 Parses the given string into command line tokens.
1691 Takes a null terminated string.  Does not need to be /n terminated.
1692 ============
1693 */
1694 // AK: This function should only be called from ExecuteString because the current design is a bit of an hack
1695 static void Cmd_TokenizeString (cmd_state_t *cmd, const char *text)
1696 {
1697         int l;
1698
1699         cmd->argc = 0;
1700         cmd->args = NULL;
1701         cmd->cmdline = NULL;
1702
1703         while (1)
1704         {
1705                 // skip whitespace up to a /n
1706                 while (*text && ISWHITESPACE(*text) && *text != '\r' && *text != '\n')
1707                         text++;
1708
1709                 // line endings:
1710                 // UNIX: \n
1711                 // Mac: \r
1712                 // Windows: \r\n
1713                 if (*text == '\n' || *text == '\r')
1714                 {
1715                         // a newline separates commands in the buffer
1716                         if (*text == '\r' && text[1] == '\n')
1717                                 text++;
1718                         text++;
1719                         break;
1720                 }
1721
1722                 if (!*text)
1723                         return;
1724
1725                 if(!cmd->argc)
1726                         cmd->cmdline = text;
1727                 if (cmd->argc == 1)
1728                         cmd->args = text;
1729
1730                 if (!COM_ParseToken_Console(&text))
1731                         return;
1732
1733                 if (cmd->argc < MAX_ARGS)
1734                 {
1735                         l = (int)strlen(com_token) + 1;
1736                         if (cmd->cbuf->tokenizebufferpos + l > CMD_TOKENIZELENGTH)
1737                         {
1738                                 Con_Printf(CON_WARN "Cmd_TokenizeString: ran out of %i character buffer space for command arguments\n", CMD_TOKENIZELENGTH);
1739                                 break;
1740                         }
1741                         memcpy (cmd->cbuf->tokenizebuffer + cmd->cbuf->tokenizebufferpos, com_token, l);
1742                         cmd->argv[cmd->argc] = cmd->cbuf->tokenizebuffer + cmd->cbuf->tokenizebufferpos;
1743                         cmd->cbuf->tokenizebufferpos += l;
1744                         cmd->argc++;
1745                 }
1746         }
1747 }
1748
1749
1750 /*
1751 ============
1752 Cmd_AddCommand
1753 ============
1754 */
1755 void Cmd_AddCommand(unsigned flags, const char *cmd_name, xcommand_t function, const char *description)
1756 {
1757         cmd_function_t *func;
1758         cmd_function_t *prev, *current;
1759         cmd_state_t *cmd;
1760         int i;
1761
1762         for (i = 0; i < 2; i++)
1763         {
1764                 cmd = cmd_iter_all[i].cmd;
1765                 if (flags & cmd->cmd_flagsmask)
1766                 {
1767                         // fail if the command is a variable name
1768                         if (Cvar_FindVar(cmd->cvars, cmd_name, ~0))
1769                         {
1770                                 Con_Printf(CON_WARN "Cmd_AddCommand: %s already defined as a var\n", cmd_name);
1771                                 return;
1772                         }
1773
1774                         if (function)
1775                         {
1776                                 // fail if the command already exists in this interpreter
1777                                 for (func = cmd->engine_functions; func; func = func->next)
1778                                 {
1779                                         if (!strcmp(cmd_name, func->name))
1780                                         {
1781                                                 Con_Printf(CON_WARN "Cmd_AddCommand: %s already defined\n", cmd_name);
1782                                                 continue;
1783                                         }
1784                                 }
1785
1786                                 func = (cmd_function_t *)Mem_Alloc(cmd->mempool, sizeof(cmd_function_t));
1787                                 func->flags = flags;
1788                                 func->name = cmd_name;
1789                                 func->function = function;
1790                                 func->description = description;
1791                                 func->next = cmd->engine_functions;
1792
1793                                 // insert it at the right alphanumeric position
1794                                 for (prev = NULL, current = cmd->engine_functions; current && strcmp(current->name, func->name) < 0; prev = current, current = current->next)
1795                                         ;
1796                                 if (prev) {
1797                                         prev->next = func;
1798                                 }
1799                                 else {
1800                                         cmd->engine_functions = func;
1801                                 }
1802                                 func->next = current;
1803                         }
1804                         else
1805                         {
1806                                 // mark qcfunc if the function already exists in the qc_functions list
1807                                 for (func = cmd->userdefined->qc_functions; func; func = func->next)
1808                                 {
1809                                         if (!strcmp(cmd_name, func->name))
1810                                         {
1811                                                 func->qcfunc = true; //[515]: csqc
1812                                                 continue;
1813                                         }
1814                                 }
1815
1816                                 func = (cmd_function_t *)Mem_Alloc(cmd->mempool, sizeof(cmd_function_t));
1817                                 func->flags = flags;
1818                                 func->name = cmd_name;
1819                                 func->function = function;
1820                                 func->description = description;
1821                                 func->qcfunc = true; //[515]: csqc
1822                                 func->next = cmd->userdefined->qc_functions;
1823
1824                                 // bones_was_here: if this QC command overrides an engine command, store its pointer
1825                                 // to avoid doing this search at invocation if QC declines to handle this command.
1826                                 for (cmd_function_t *f = cmd->engine_functions; f; f = f->next)
1827                                 {
1828                                         if (!strcmp(cmd_name, f->name))
1829                                         {
1830                                                 Con_DPrintf("Adding QC override of engine command %s\n", cmd_name);
1831                                                 func->overridden = f;
1832                                                 break;
1833                                         }
1834                                 }
1835
1836                                 // insert it at the right alphanumeric position
1837                                 for (prev = NULL, current = cmd->userdefined->qc_functions; current && strcmp(current->name, func->name) < 0; prev = current, current = current->next)
1838                                         ;
1839                                 if (prev) {
1840                                         prev->next = func;
1841                                 }
1842                                 else {
1843                                         cmd->userdefined->qc_functions = func;
1844                                 }
1845                                 func->next = current;
1846                         }
1847                 }
1848         }
1849 }
1850
1851 /*
1852 ============
1853 Cmd_Exists
1854 ============
1855 */
1856 qbool Cmd_Exists (cmd_state_t *cmd, const char *cmd_name)
1857 {
1858         cmd_function_t  *func;
1859
1860         for (func = cmd->userdefined->qc_functions; func; func = func->next)
1861                 if (!strcmp(cmd_name, func->name))
1862                         return true;
1863
1864         for (func=cmd->engine_functions ; func ; func=func->next)
1865                 if (!strcmp (cmd_name,func->name))
1866                         return true;
1867
1868         return false;
1869 }
1870
1871
1872 /*
1873 ============
1874 Cmd_CompleteCommand
1875 ============
1876 */
1877 const char *Cmd_CompleteCommand (cmd_state_t *cmd, const char *partial)
1878 {
1879         cmd_function_t *func;
1880         size_t len;
1881
1882         len = strlen(partial);
1883
1884         if (!len)
1885                 return NULL;
1886
1887 // check functions
1888         for (func = cmd->userdefined->qc_functions; func; func = func->next)
1889                 if (!strncasecmp(partial, func->name, len))
1890                         return func->name;
1891
1892         for (func = cmd->engine_functions; func; func = func->next)
1893                 if (!strncasecmp(partial, func->name, len))
1894                         return func->name;
1895
1896         return NULL;
1897 }
1898
1899 /*
1900         Cmd_CompleteCountPossible
1901
1902         New function for tab-completion system
1903         Added by EvilTypeGuy
1904         Thanks to Fett erich@heintz.com
1905         Thanks to taniwha
1906
1907 */
1908 int Cmd_CompleteCountPossible (cmd_state_t *cmd, const char *partial)
1909 {
1910         cmd_function_t *func;
1911         size_t len;
1912         int h;
1913
1914         h = 0;
1915         len = strlen(partial);
1916
1917         if (!len)
1918                 return 0;
1919
1920         // Loop through the command list and count all partial matches
1921         for (func = cmd->userdefined->qc_functions; func; func = func->next)
1922                 if (!strncasecmp(partial, func->name, len))
1923                         h++;
1924
1925         for (func = cmd->engine_functions; func; func = func->next)
1926                 if (!strncasecmp(partial, func->name, len))
1927                         h++;
1928
1929         return h;
1930 }
1931
1932 /*
1933         Cmd_CompleteBuildList
1934
1935         New function for tab-completion system
1936         Added by EvilTypeGuy
1937         Thanks to Fett erich@heintz.com
1938         Thanks to taniwha
1939
1940 */
1941 const char **Cmd_CompleteBuildList (cmd_state_t *cmd, const char *partial)
1942 {
1943         cmd_function_t *func;
1944         size_t len = 0;
1945         size_t bpos = 0;
1946         size_t sizeofbuf = (Cmd_CompleteCountPossible (cmd, partial) + 1) * sizeof (const char *);
1947         const char **buf;
1948
1949         len = strlen(partial);
1950         buf = (const char **)Mem_Alloc(tempmempool, sizeofbuf + sizeof (const char *));
1951         // Loop through the functions lists and print all matches
1952         for (func = cmd->userdefined->qc_functions; func; func = func->next)
1953                 if (!strncasecmp(partial, func->name, len))
1954                         buf[bpos++] = func->name;
1955         for (func = cmd->engine_functions; func; func = func->next)
1956                 if (!strncasecmp(partial, func->name, len))
1957                         buf[bpos++] = func->name;
1958
1959         buf[bpos] = NULL;
1960         return buf;
1961 }
1962
1963 // written by LadyHavoc
1964 void Cmd_CompleteCommandPrint (cmd_state_t *cmd, const char *partial)
1965 {
1966         cmd_function_t *func;
1967         size_t len = strlen(partial);
1968         // Loop through the command list and print all matches
1969         for (func = cmd->userdefined->qc_functions; func; func = func->next)
1970                 if (!strncasecmp(partial, func->name, len))
1971                         Con_Printf("^2%s^7: %s\n", func->name, func->description);
1972         for (func = cmd->engine_functions; func; func = func->next)
1973                 if (!strncasecmp(partial, func->name, len))
1974                         Con_Printf("^2%s^7: %s\n", func->name, func->description);
1975 }
1976
1977 /*
1978         Cmd_CompleteAlias
1979
1980         New function for tab-completion system
1981         Added by EvilTypeGuy
1982         Thanks to Fett erich@heintz.com
1983         Thanks to taniwha
1984
1985 */
1986 const char *Cmd_CompleteAlias (cmd_state_t *cmd, const char *partial)
1987 {
1988         cmd_alias_t *alias;
1989         size_t len;
1990
1991         len = strlen(partial);
1992
1993         if (!len)
1994                 return NULL;
1995
1996         // Check functions
1997         for (alias = cmd->userdefined->alias; alias; alias = alias->next)
1998                 if (!strncasecmp(partial, alias->name, len))
1999                         return alias->name;
2000
2001         return NULL;
2002 }
2003
2004 // written by LadyHavoc
2005 void Cmd_CompleteAliasPrint (cmd_state_t *cmd, const char *partial)
2006 {
2007         cmd_alias_t *alias;
2008         size_t len = strlen(partial);
2009         // Loop through the alias list and print all matches
2010         for (alias = cmd->userdefined->alias; alias; alias = alias->next)
2011                 if (!strncasecmp(partial, alias->name, len))
2012                         Con_Printf("^5%s^7: %s", alias->name, alias->value);
2013 }
2014
2015
2016 /*
2017         Cmd_CompleteAliasCountPossible
2018
2019         New function for tab-completion system
2020         Added by EvilTypeGuy
2021         Thanks to Fett erich@heintz.com
2022         Thanks to taniwha
2023
2024 */
2025 int Cmd_CompleteAliasCountPossible (cmd_state_t *cmd, const char *partial)
2026 {
2027         cmd_alias_t     *alias;
2028         size_t          len;
2029         int                     h;
2030
2031         h = 0;
2032
2033         len = strlen(partial);
2034
2035         if (!len)
2036                 return 0;
2037
2038         // Loop through the command list and count all partial matches
2039         for (alias = cmd->userdefined->alias; alias; alias = alias->next)
2040                 if (!strncasecmp(partial, alias->name, len))
2041                         h++;
2042
2043         return h;
2044 }
2045
2046 /*
2047         Cmd_CompleteAliasBuildList
2048
2049         New function for tab-completion system
2050         Added by EvilTypeGuy
2051         Thanks to Fett erich@heintz.com
2052         Thanks to taniwha
2053
2054 */
2055 const char **Cmd_CompleteAliasBuildList (cmd_state_t *cmd, const char *partial)
2056 {
2057         cmd_alias_t *alias;
2058         size_t len = 0;
2059         size_t bpos = 0;
2060         size_t sizeofbuf = (Cmd_CompleteAliasCountPossible (cmd, partial) + 1) * sizeof (const char *);
2061         const char **buf;
2062
2063         len = strlen(partial);
2064         buf = (const char **)Mem_Alloc(tempmempool, sizeofbuf + sizeof (const char *));
2065         // Loop through the alias list and print all matches
2066         for (alias = cmd->userdefined->alias; alias; alias = alias->next)
2067                 if (!strncasecmp(partial, alias->name, len))
2068                         buf[bpos++] = alias->name;
2069
2070         buf[bpos] = NULL;
2071         return buf;
2072 }
2073
2074 // TODO: Make this more generic?
2075 void Cmd_ClearCSQCCommands (cmd_state_t *cmd)
2076 {
2077         cmd_function_t *func;
2078         cmd_function_t **next = &cmd->userdefined->qc_functions;
2079         
2080         while(*next)
2081         {
2082                 func = *next;
2083                 *next = func->next;
2084                 Z_Free(func);
2085         }
2086 }
2087
2088 extern cvar_t sv_cheats;
2089
2090 /*
2091  * Cloudwalk FIXME: This idea sounded great in my head but...
2092  * How do we handle commands that can be received by the client,
2093  * but which the server can also execute locally?
2094  * 
2095  * If we create a callback where the engine will forward to server
2096  * but try to execute the command locally if it's dedicated,
2097  * we're back to intermixing client and server code which I'm
2098  * trying to avoid. There's no other way I can think of to
2099  * implement that behavior that doesn't involve an #ifdef, or
2100  * making a mess of hooks.
2101  */
2102 qbool Cmd_Callback(cmd_state_t *cmd, cmd_function_t *func)
2103 {
2104         if (func->function)
2105                 func->function(cmd);
2106         else
2107                 Con_Printf(CON_WARN "Command \"%s\" can not be executed\n", Cmd_Argv(cmd, 0));
2108         return true;
2109 }
2110
2111 qbool Cmd_CL_Callback(cmd_state_t *cmd, cmd_function_t *func, const char *text, size_t textlen, cmd_source_t src)
2112 {
2113         // TODO: Assign these functions to QC commands directly?
2114         if(func->qcfunc)
2115         {
2116                 if(((func->flags & CF_CLIENT) && CL_VM_ConsoleCommand(text, textlen)) ||
2117                    ((func->flags & CF_SERVER) && SV_VM_ConsoleCommand(text, textlen)))
2118                         return true;
2119
2120                 if (func->overridden) // If this QC command overrides an engine command,
2121                         func = func->overridden; // fall back to that command.
2122         }
2123         if (func->flags & CF_SERVER_FROM_CLIENT)
2124         {
2125                 if(host_isclient.integer)
2126                 {
2127                         CL_ForwardToServer_f(cmd);
2128                         return true;
2129                 }
2130                 else if(!(func->flags & CF_SERVER))
2131                 {
2132                         Con_Printf(CON_WARN "Cannot execute client commands from a dedicated server console.\n");
2133                         return true;
2134                 }
2135         }
2136         return Cmd_Callback(cmd, func);
2137 }
2138
2139 qbool Cmd_SV_Callback(cmd_state_t *cmd, cmd_function_t *func, const char *text, size_t textlen, cmd_source_t src)
2140 {
2141         if(func->qcfunc && (func->flags & CF_SERVER))
2142                 return SV_VM_ConsoleCommand(text, textlen);
2143         else if (src == src_client)
2144         {
2145                 if((func->flags & CF_CHEAT) && !sv_cheats.integer)
2146                         SV_ClientPrintf(CON_WARN "No cheats allowed. The server must have sv_cheats set to 1\n");
2147                 else
2148                         func->function(cmd);
2149                 return true;
2150         }
2151         return false;
2152 }
2153
2154 /*
2155 ============
2156 Cmd_ExecuteString
2157
2158 A complete command line has been parsed, so try to execute it
2159 FIXME: lookupnoadd the token to speed search?
2160 ============
2161 */
2162 void Cmd_ExecuteString(cmd_state_t *cmd, const char *text, size_t textlen, cmd_source_t src, qbool lockmutex)
2163 {
2164         int oldpos;
2165         cmd_function_t *func;
2166         cmd_alias_t *a;
2167
2168         if (lockmutex)
2169                 Cbuf_Lock(cmd->cbuf);
2170         oldpos = cmd->cbuf->tokenizebufferpos;
2171         cmd->source = src;
2172
2173         Cmd_TokenizeString (cmd, text);
2174
2175 // execute the command line
2176         if (!Cmd_Argc(cmd))
2177                 goto done; // no tokens
2178
2179 // check functions
2180         for (func = cmd->userdefined->qc_functions; func; func = func->next)
2181                 if (!strcasecmp(cmd->argv[0], func->name))
2182                         if(cmd->Handle(cmd, func, text, textlen, src))
2183                                 goto functions_done;
2184
2185         for (func = cmd->engine_functions; func; func=func->next)
2186                 if (!strcasecmp (cmd->argv[0], func->name))
2187                         if(cmd->Handle(cmd, func, text, textlen, src))
2188                                 goto functions_done;
2189
2190 functions_done:
2191         // If it's a client command and wasn't found and handled, say so.
2192         // Also don't let clients call server aliases.
2193         if (cmd->source == src_client)
2194         {
2195                 if (!func)
2196                         Con_Printf("Client \"%s\" tried to execute \"%s\"\n", host_client->name, text);
2197                 goto done;
2198         }
2199
2200 // check alias
2201         // Execute any alias with the same name as a command after the command.
2202         for (a=cmd->userdefined->alias ; a ; a=a->next)
2203         {
2204                 if (!strcasecmp (cmd->argv[0], a->name))
2205                 {
2206                         Cmd_ExecuteAlias(cmd, a);
2207                         goto done;
2208                 }
2209         }
2210
2211         // If the command was found and handled don't try to handle it as a cvar.
2212         if (func)
2213                 goto done;
2214
2215 // check cvars
2216         // Xonotic is still maintained so we don't want to hide problems from getting fixed
2217         if (!Cvar_Command(cmd) && (host.framecount > 0 || gamemode == GAME_XONOTIC))
2218                 Con_Printf(CON_WARN "Unknown command \"%s\"\n", Cmd_Argv(cmd, 0));
2219 done:
2220         cmd->cbuf->tokenizebufferpos = oldpos;
2221         if (lockmutex)
2222                 Cbuf_Unlock(cmd->cbuf);
2223 }
2224
2225 /*
2226 ================
2227 Cmd_CheckParm
2228
2229 Returns the position (1 to argc-1) in the command's argument list
2230 where the given parameter apears, or 0 if not present
2231 ================
2232 */
2233
2234 int Cmd_CheckParm (cmd_state_t *cmd, const char *parm)
2235 {
2236         int i;
2237
2238         if (!parm)
2239         {
2240                 Con_Printf(CON_WARN "Cmd_CheckParm: NULL");
2241                 return 0;
2242         }
2243
2244         for (i = 1; i < Cmd_Argc (cmd); i++)
2245                 if (!strcasecmp (parm, Cmd_Argv(cmd, i)))
2246                         return i;
2247
2248         return 0;
2249 }
2250
2251
2252
2253 void Cmd_SaveInitState(void)
2254 {
2255         cmd_iter_t *cmd_iter;
2256         for (cmd_iter = cmd_iter_all; cmd_iter->cmd; cmd_iter++)
2257         {
2258                 cmd_state_t *cmd = cmd_iter->cmd;
2259                 cmd_function_t *f;
2260                 cmd_alias_t *a;
2261                 for (f = cmd->userdefined->qc_functions; f; f = f->next)
2262                         f->initstate = true;
2263                 for (f = cmd->engine_functions; f; f = f->next)
2264                         f->initstate = true;
2265                 for (a = cmd->userdefined->alias; a; a = a->next)
2266                 {
2267                         a->initstate = true;
2268                         a->initialvalue = Mem_strdup(zonemempool, a->value);
2269                 }
2270         }
2271         Cvar_SaveInitState(&cvars_all);
2272 }
2273
2274 void Cmd_RestoreInitState(void)
2275 {
2276         cmd_iter_t *cmd_iter;
2277         for (cmd_iter = cmd_iter_all; cmd_iter->cmd; cmd_iter++)
2278         {
2279                 cmd_state_t *cmd = cmd_iter->cmd;
2280                 cmd_function_t *f, **fp;
2281                 cmd_alias_t *a, **ap;
2282                 for (fp = &cmd->userdefined->qc_functions; (f = *fp);)
2283                 {
2284                         if (f->initstate)
2285                                 fp = &f->next;
2286                         else
2287                         {
2288                                 // destroy this command, it didn't exist at init
2289                                 Con_DPrintf("Cmd_RestoreInitState: Destroying command %s\n", f->name);
2290                                 *fp = f->next;
2291                                 Z_Free(f);
2292                         }
2293                 }
2294                 for (fp = &cmd->engine_functions; (f = *fp);)
2295                 {
2296                         if (f->initstate)
2297                                 fp = &f->next;
2298                         else
2299                         {
2300                                 // destroy this command, it didn't exist at init
2301                                 Con_DPrintf("Cmd_RestoreInitState: Destroying command %s\n", f->name);
2302                                 *fp = f->next;
2303                                 Z_Free(f);
2304                         }
2305                 }
2306                 for (ap = &cmd->userdefined->alias; (a = *ap);)
2307                 {
2308                         if (a->initstate)
2309                         {
2310                                 // restore this alias, it existed at init
2311                                 if (strcmp(a->value ? a->value : "", a->initialvalue ? a->initialvalue : ""))
2312                                 {
2313                                         Con_DPrintf("Cmd_RestoreInitState: Restoring alias %s\n", a->name);
2314                                         if (a->value)
2315                                                 Z_Free(a->value);
2316                                         a->value = Mem_strdup(zonemempool, a->initialvalue);
2317                                 }
2318                                 ap = &a->next;
2319                         }
2320                         else
2321                         {
2322                                 // free this alias, it didn't exist at init...
2323                                 Con_DPrintf("Cmd_RestoreInitState: Destroying alias %s\n", a->name);
2324                                 *ap = a->next;
2325                                 if (a->value)
2326                                         Z_Free(a->value);
2327                                 Z_Free(a);
2328                         }
2329                 }
2330         }
2331         Cvar_RestoreInitState(&cvars_all);
2332 }
2333
2334 void Cmd_NoOperation_f(cmd_state_t *cmd)
2335 {
2336 }