1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: command.c 1542 2020-08-22 02:35:24Z wesleyjohnson $
5 //
6 // Copyright (C) 1998-2016 by DooM Legacy Team.
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License
10 // as published by the Free Software Foundation; either version 2
11 // of the License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU General Public License for more details.
17 //
18 //
19 // $Log: command.c,v $
20 // Revision 1.15 2005/05/21 08:41:23 iori_
21 // May 19, 2005 - PlayerArmor FS function; 1.43 can be compiled again.
22 //
23 // Revision 1.14 2004/08/26 23:15:45 hurdler
24 // add FS functions in console (+ minor linux fixes)
25 //
26 // Revision 1.13 2003/05/30 22:44:08 hurdler
27 // add checkcvar function to FS
28 //
29 // Revision 1.12 2001/12/27 22:50:25 hurdler
30 // fix a colormap bug, add scrolling floor/ceiling in hw mode
31 //
32 // Revision 1.11 2001/02/24 13:35:19 bpereira
33 //
34 // Revision 1.10 2001/01/25 22:15:41 bpereira
35 // added heretic support
36 //
37 // Revision 1.9 2000/11/11 13:59:45 bpereira
38 // Revision 1.8 2000/11/02 19:49:35 bpereira
39 // Revision 1.7 2000/10/08 13:29:59 bpereira
40 // Revision 1.6 2000/09/28 20:57:14 bpereira
41 // Revision 1.5 2000/08/31 14:30:54 bpereira
42 // Revision 1.4 2000/08/03 17:57:41 bpereira
43 // Revision 1.3 2000/02/27 00:42:10 hurdler
44 // Revision 1.2 2000/02/26 00:28:42 hurdler
45 // Mostly bug fix (see borislog.txt 23-2-2000, 24-2-2000)
46 //
47 //
48 // DESCRIPTION:
49 // parse and execute commands from console input/scripts/
50 // and remote server.
51 //
52 // handles console variables, which is a simplified version
53 // of commands, each consvar can have a function called when
54 // it is modified.. thus it acts nearly as commands.
55 //
56 // code shamelessly inspired by the QuakeC sources, thanks Id :)
57 //
58 //-----------------------------------------------------------------------------
59
60
61 #include "doomincl.h"
62 #include "doomstat.h"
63 #include "command.h"
64 #include "console.h"
65 #include "z_zone.h"
66 #include "d_clisrv.h"
67 #include "d_netcmd.h"
68 #include "m_misc.h"
69 #include "m_fixed.h"
70 #include "byteptr.h"
71 #include "p_saveg.h"
72
73 // Hurdler: add FS functionnality to console command
74 #include "t_vari.h"
75 //void run_string(char *data);
76
77 //========
78 // protos.
79 //========
80 static boolean COM_Exists (const char * com_name);
81 static void COM_ExecuteString (const char * text, boolean script, byte cfg);
82
83 static void COM_Alias_f (void);
84 static void COM_Echo_f (void);
85 static void COM_Exec_f (void);
86 static void COM_Wait_f (void);
87 static void COM_Help_f (void);
88 static void COM_Toggle_f (void);
89
90 static boolean CV_Var_Command ( byte cfg );
91 static char * CV_StringValue (const char * var_name);
92 static consvar_t *consvar_vars; // list of registered console variables
93
94 #define COM_TOKEN_MAX 1024
95 static char com_token[COM_TOKEN_MAX];
96 static const char * COM_Parse (const char * data, boolean script);
97
98 CV_PossibleValue_t CV_OnOff[] = {{0,"Off"}, {1,"On"}, {0,NULL}};
99 CV_PossibleValue_t CV_YesNo[] = {{0,"No"} , {1,"Yes"}, {0,NULL}};
100 CV_PossibleValue_t CV_uint16[]= {{0,"MIN"}, {0xFFFF,"MAX"}, {0,NULL}};
101 CV_PossibleValue_t CV_Unsigned[]= {{0,"MIN"}, {999999999,"MAX"}, {0,NULL}};
102 CV_PossibleValue_t CV_byte[]= {{0,"MIN"}, {255,"MAX"}, {0,NULL}};
103
104 #define COM_BUF_SIZE 8192 // command buffer size
105
106 static int com_wait; // one command per frame (for cmd sequences)
107
108
109 // command aliases
110 //
111 typedef struct cmdalias_s
112 {
113 struct cmdalias_s *next;
114 char *name;
115 char *value; // the command string to replace the alias
116 } cmd_alias_t;
117
118 static cmd_alias_t *com_alias; // aliases list
119
120
121 // =========================================================================
122 // COMMAND BUFFER
123 // =========================================================================
124
125
126 static vsbuf_t com_text; // variable sized buffer
127
128
129 // Add text (a NUL-terminated string) in the command buffer (for later execution)
130 //
COM_BufAddText(const char * text)131 void COM_BufAddText (const char *text)
132 {
133 if (!VS_Print(&com_text, text))
134 CONS_Printf ("Command buffer full!\n");
135 }
136
137
138 // Adds command text immediately after the current command
139 // Adds a \n to the text
140 //
COM_BufInsertText(const char * text)141 void COM_BufInsertText (const char *text)
142 {
143 char *temp;
144
145 // copy off any commands still remaining in the exec buffer
146 int templen = com_text.cursize;
147 if (templen)
148 {
149 // add a trailing NUL (TODO why do we even allow non-string data in a vsbuf_t?)
150 temp = Z_Malloc (templen + 1, PU_STATIC, NULL);
151 temp[templen] = '\0';
152 memcpy (temp, com_text.data, templen);
153 VS_Clear (&com_text);
154 }
155 else
156 temp = NULL; // shut up compiler
157
158 // add the entire text of the file (or alias)
159 COM_BufAddText (text);
160
161 // add the copied off data
162 if (templen)
163 {
164 if (!VS_Print(&com_text, temp))
165 CONS_Printf ("Command buffer full!!\n");
166
167 Z_Free (temp);
168 }
169 }
170
171
172 // Flush (execute) console commands in buffer.
173 //
174 // cfg : cv_config_e, to mark the cvar as to the source
175 //
176 // Global: com_wait : does nothing until com_wait ticks down
177 //
178 // Called by: TryRunTics( CFG_none )
179 // Called by: P_Load_LevelInfo( CFG_none )
180 // Called by: M_LoadConfig( cfg )
181 // Was Called by: M_Choose_to_quit_Response, but that is disabled now.
COM_BufExecute(byte cfg)182 void COM_BufExecute( byte cfg )
183 {
184 int i;
185 boolean script = 1;
186 char line[1024];
187
188 if (com_wait)
189 {
190 com_wait--;
191 return;
192 }
193
194 while (com_text.cursize)
195 {
196 // find a '\n' or ; line break
197 char *text = (char *)com_text.data;
198 boolean in_quote = false;
199
200 // This is called without a clue as to what commands are present.
201 // Scripts have quoted strings,
202 // exec have quoted filenames with backslash: exec "c:\doomdir\".
203 // The while loop continues into the exec which can have two levels
204 // of quoted strings:
205 // alias zoom_in "fov 15; bind \"mouse 3\" zoom_out"
206 script =
207 ( ( strncmp(text,"exec",4) == 0 )
208 ||( strncmp(text,"map",3) == 0 )
209 ||( strncmp(text,"playdemo",8) == 0 )
210 ||( strncmp(text,"addfile",7) == 0 )
211 ||( strncmp(text,"loadconfig",10) == 0 )
212 ||( strncmp(text,"saveconfig",10) == 0 )
213 ) ? 0 : 1; // has filename : is script with quoted strings
214
215 for (i=0; i < com_text.cursize; i++)
216 {
217 register char ch = text[i];
218 if (ch == '"') // non-escaped quote
219 in_quote = !in_quote;
220 else if( in_quote )
221 {
222 if (script && (ch == '\\')) // escape sequence
223 {
224 #if 1
225 // [WDJ] Only doublequote and backslash really matter
226 i += 1; // skip it, because other parser does too
227 continue;
228 #else
229 switch (text[i+1])
230 {
231 case '\\': // backslash
232 case '"': // double quote
233 case 't': // tab
234 case 'n': // newline
235 i += 1; // skip it
236 break;
237
238 default:
239 // unknown sequence, parser will give an error later on.
240 break;
241 }
242 continue;
243 #endif
244 }
245 }
246 else
247 { // not in quoted string
248 if (ch == ';') // semicolon separates commands
249 break;
250 if (ch == '\n' || ch == '\r') // always separate commands
251 break;
252 }
253 }
254
255
256 if( i > 1023 ) i = 1023; // overrun of line
257 memcpy (line, text, i);
258 line[i] = 0;
259
260 // flush the command text from the command buffer, _BEFORE_
261 // executing, to avoid that 'recursive' aliases overflow the
262 // command text buffer, in that case, new commands are inserted
263 // at the beginning, in place of the actual, so it doesn't
264 // overflow
265 if (i == com_text.cursize)
266 {
267 // the last command was just flushed
268 com_text.cursize = 0;
269 }
270 else
271 {
272 i++;
273 com_text.cursize -= i;
274 // Shuffle text, overlap copy. Bug fix by Ryan bug_0626.
275 memmove(text, text+i, com_text.cursize);
276 }
277
278 // execute the command line, marking the cvar values as to the source
279 COM_ExecuteString( line, script, cfg );
280
281 // delay following commands if a wait was encountered
282 if (com_wait)
283 {
284 com_wait--;
285 break;
286 }
287 }
288 }
289
290
291 // =========================================================================
292 // COMMAND EXECUTION
293 // =========================================================================
294
295 typedef struct xcommand_s
296 {
297 const char * name;
298 struct xcommand_s * next;
299 com_func_t function;
300 byte cctype; // classification for help
301 } xcommand_t;
302
303 static xcommand_t *com_commands = NULL; // current commands
304
305
306 #define MAX_ARGS 80
307 static int com_argc;
308 static char *com_argv[MAX_ARGS];
309 static char *com_null_string = "";
310 static const char * com_args = NULL; // current command args or NULL
311
312
313 // Initialize command buffer and add basic commands
314 //
315 // Called only once
COM_Init(void)316 void COM_Init (void)
317 {
318 int i;
319 for( i=0; i<MAX_ARGS; i++ ) com_argv[i] = com_null_string;
320 com_argc = 0;
321
322 // allocate command buffer
323 VS_Alloc (&com_text, COM_BUF_SIZE);
324
325 // add standard commands
326 COM_AddCommand ("alias",COM_Alias_f, CC_command);
327 COM_AddCommand ("echo", COM_Echo_f, CC_command);
328 COM_AddCommand ("exec", COM_Exec_f, CC_command);
329 COM_AddCommand ("wait", COM_Wait_f, CC_command);
330 COM_AddCommand ("toggle", COM_Toggle_f, CC_command);
331 COM_AddCommand ("help", COM_Help_f, CC_info);
332 Register_NetXCmd(XD_NETVAR, Got_NetXCmd_NetVar);
333 }
334
335
336 // Returns how many args for last command
337 //
COM_Argc(void)338 int COM_Argc (void)
339 {
340 return com_argc;
341 }
342
343
344 // Returns string pointer for given argument number
345 //
COM_Argv(int arg)346 char * COM_Argv (int arg)
347 {
348 if ( arg >= com_argc || arg < 0 )
349 return com_null_string;
350 return com_argv[arg];
351 }
352
353 // get some args
354 // More efficient, but preserves read only interface
COM_Args(COM_args_t * comargs)355 void COM_Args( COM_args_t * comargs )
356 {
357 int i;
358 comargs->num = com_argc;
359 for( i=0; i<4; i++ )
360 {
361 comargs->arg[i] = com_argv[i];
362 }
363 }
364
365 #if 0
366 // [WDJ] Unused
367 // Returns string pointer of all command args
368 //
369 char *COM_Args (void)
370 {
371 return com_args;
372 }
373 #endif
374
375
COM_CheckParm(const char * check)376 int COM_CheckParm (const char *check)
377 {
378 int i;
379
380 for (i = 1; i<com_argc; i++)
381 {
382 if( strcasecmp(check, com_argv[i]) == 0 )
383 return i;
384 }
385 return 0;
386 }
387
388
389 // Parses the given string into command line tokens.
390 //
391 // Takes a null terminated string. Does not need to be /n terminated.
392 // breaks the string up into arg tokens.
COM_TokenizeString(const char * text,boolean script)393 static void COM_TokenizeString (const char * text, boolean script)
394 {
395 int i;
396
397 // clear the args from the last string
398 for (i=0 ; i<com_argc ; i++)
399 {
400 Z_Free (com_argv[i]);
401 com_argv[i] = com_null_string; // never leave behind ptrs to old mem
402 }
403
404 com_argc = 0;
405 com_args = NULL;
406
407 while (1)
408 {
409 // skip whitespace up to a /n
410 while (*text && *text <= ' ' && *text != '\n')
411 text++;
412
413 if (*text == '\n')
414 { // a newline means end of command in buffer,
415 // thus end of this command's args too
416 text++;
417 break;
418 }
419
420 if (!*text)
421 return;
422
423 if (com_argc == 1)
424 com_args = text;
425
426 text = COM_Parse (text, script);
427 if (!text)
428 return;
429
430 if (com_argc < MAX_ARGS)
431 {
432 com_argv[com_argc] = Z_Malloc (strlen(com_token)+1, PU_STATIC, NULL);
433 strcpy (com_argv[com_argc], com_token);
434 com_argc++;
435 }
436 }
437
438 }
439
440
441 // Add a command before existing ones.
442 //
COM_AddCommand(const char * name,com_func_t func,byte command_type)443 void COM_AddCommand( const char *name, com_func_t func, byte command_type )
444 {
445 xcommand_t * cmd;
446
447 // fail if the command is a variable name
448 if (CV_StringValue(name)[0])
449 {
450 CONS_Printf ("%s is a variable name\n", name);
451 return;
452 }
453
454 // fail if the command already exists
455 for (cmd=com_commands ; cmd ; cmd=cmd->next)
456 {
457 if (!strcmp (name, cmd->name))
458 {
459 CONS_Printf ("Command %s already exists\n", name);
460 return;
461 }
462 }
463
464 cmd = Z_Malloc (sizeof(xcommand_t), PU_STATIC, NULL);
465 cmd->name = name;
466 cmd->function = func;
467 cmd->cctype = command_type;
468 cmd->next = com_commands;
469 com_commands = cmd;
470 }
471
472
473 // Returns true if a command by the name given exists
474 //
COM_Exists(const char * com_name)475 static boolean COM_Exists (const char * com_name)
476 {
477 xcommand_t * cmd;
478
479 for (cmd=com_commands ; cmd ; cmd=cmd->next)
480 {
481 if (!strcmp (com_name,cmd->name))
482 return true;
483 }
484
485 return false;
486 }
487
488
489 // Command completion using TAB key like '4dos'
490 // Will skip 'skips' commands
491 //
492 // partial : a partial keyword
COM_CompleteCommand(const char * partial,int skips)493 const char * COM_CompleteCommand (const char *partial, int skips)
494 {
495 xcommand_t *cmd;
496 int len;
497
498 len = strlen(partial);
499
500 if (!len)
501 return NULL;
502
503 // check functions
504 for (cmd=com_commands ; cmd ; cmd=cmd->next)
505 {
506 if (!strncmp (partial,cmd->name, len))
507 {
508 if (!skips--)
509 return cmd->name;
510 }
511 }
512
513 return NULL;
514 }
515
516
517
518 // Parses a single line of text into arguments and tries to execute it.
519 // The text can come from the command buffer, a remote client, or stdin.
520 //
521 // cfg : cv_config_e, to mark the cvar as to the source
522 // Called by: COM_BufExecute
COM_ExecuteString(const char * text,boolean script,byte cfg)523 static void COM_ExecuteString (const char *text, boolean script, byte cfg )
524 {
525 xcommand_t *cmd;
526 cmd_alias_t *a;
527
528 COM_TokenizeString (text, script);
529
530 // execute the command line
531 if (com_argc==0)
532 return; // no tokens
533
534 // check functions
535 for (cmd=com_commands ; cmd ; cmd=cmd->next)
536 {
537 if (!strcmp (com_argv[0],cmd->name))
538 {
539 cmd->function ();
540 return;
541 }
542 }
543
544 // check aliases
545 for (a=com_alias ; a ; a=a->next)
546 {
547 if (!strcmp (com_argv[0], a->name))
548 {
549 COM_BufInsertText (a->value);
550 return;
551 }
552 }
553
554 // check FraggleScript functions
555 if (find_variable(com_argv[0])) // if this is a potential FS function, try to execute it
556 {
557 // run_string(text);
558 return;
559 }
560
561 // check cvars
562 // Hurdler: added at Ebola's request ;)
563 // (don't flood the console in software mode with bad gr_xxx command)
564 if (!CV_Var_Command( cfg ) && con_destlines)
565 {
566 CONS_Printf ("Unknown command '%s'\n", com_argv[0]);
567 }
568 }
569
570
571
572 // =========================================================================
573 // SCRIPT COMMANDS
574 // =========================================================================
575
576
577 // alias command : a command name that replaces another command
578 //
COM_Alias_f(void)579 static void COM_Alias_f (void)
580 {
581 cmd_alias_t *a;
582 char cmd[1024];
583 int i;
584 COM_args_t carg;
585
586 COM_Args( &carg );
587
588 if ( carg.num < 3 )
589 {
590 CONS_Printf("alias <name> <command>\n");
591 return;
592 }
593
594 a = Z_Malloc (sizeof(cmd_alias_t), PU_STATIC, NULL);
595 a->next = com_alias;
596 com_alias = a;
597
598 a->name = Z_StrDup (carg.arg[1]);
599
600 // copy the rest of the command line
601 cmd[0] = 0; // start out with a null string
602 for (i=2 ; i<carg.num ; i++)
603 {
604 register int n = 1020 - strlen( cmd ); // free space, with " " and "\n"
605 strncat (cmd, COM_Argv(i), n);
606 if (i != carg.num)
607 strcat (cmd, " ");
608 }
609 strcat (cmd, "\n");
610
611 a->value = Z_StrDup (cmd);
612 }
613
614
615 // Echo a line of text to console
616 //
COM_Echo_f(void)617 static void COM_Echo_f (void)
618 {
619 int i;
620 COM_args_t carg;
621
622 COM_Args( &carg );
623
624 for (i=1 ; i<carg.num ; i++)
625 CONS_Printf ("%s ",COM_Argv(i));
626 CONS_Printf ("\n");
627 }
628
629
630 // Execute a script file
631 //
COM_Exec_f(void)632 static void COM_Exec_f (void)
633 {
634 byte* buf=NULL;
635 COM_args_t carg;
636
637 COM_Args( &carg );
638
639 if (carg.num != 2)
640 {
641 CONS_Printf ("exec <filename> : run a script file\n");
642 return;
643 }
644
645 // load file
646
647 #ifdef DEBUG_EXEC_FILE_LENGTH
648 int length;
649 length = FIL_ReadFile (carg.arg[1], &buf);
650 debug_Printf ("EXEC: file length = %d\n", length);
651 #else
652 FIL_ReadFile (carg.arg[1], &buf);
653 #endif
654
655 if (!buf)
656 {
657 CONS_Printf ("couldn't execute file %s\n", carg.arg[1]);
658 return;
659 }
660
661 CONS_Printf ("executing %s\n", carg.arg[1]);
662
663 // insert text file into the command buffer
664
665 COM_BufInsertText((char *)buf);
666
667 // free buffer
668
669 Z_Free(buf);
670 }
671
672
673 // Delay execution of the rest of the commands to the next frame,
674 // allows sequences of commands like "jump; fire; backward"
675 //
COM_Wait_f(void)676 static void COM_Wait_f (void)
677 {
678 COM_args_t carg;
679
680 COM_Args( &carg );
681 if (carg.num>1)
682 com_wait = atoi( carg.arg[1] );
683 else
684 com_wait = 1; // 1 frame
685 }
686
687
688 // [WDJ] Categorized help.
689 typedef struct {
690 const char * str;
691 byte cctype; // cc type
692 byte varflag; // cvar flags
693 } help_cat_t;
694
695 #define NUM_HELP_CAT 13
696 static help_cat_t helpcat_table[ NUM_HELP_CAT ] =
697 {
698 {"INFO", CC_info, 0},
699 {"CHEAT", CC_cheat, 0},
700 {"COMMAND", CC_command, 0},
701 {"SAVEGAME", CC_savegame, 0},
702 {"CONFIG", CC_config, 0},
703 {"CONTROL", CC_control, 0},
704 {"FS", CC_fs, 0}, // fragglescript
705 {"CHAT", CC_chat, 0},
706 {"NET", CC_net, 0},
707 {"CONSOLE", CC_console, 0},
708 {"VAR", 0xFF, 0xFF},
709 {"NETVAR", 0xFF, CV_NETVAR},
710 {"CFGVAR", 0xFF, CV_SAVE},
711 };
712
COM_Help_f(void)713 static void COM_Help_f (void)
714 {
715 xcommand_t *cmd;
716 consvar_t *cvar;
717 int i, k;
718 uint32_t varflag = 0;
719 byte cctype = 0;
720
721 COM_args_t carg;
722
723 COM_Args( &carg );
724
725 // [WDJ] Categorized help.
726 if( carg.num < 2 )
727 {
728 CONS_Printf ("HELP <category>\n" );
729 CONS_Printf (" INFO CHEAT COMMAND SAVEGAME CONFIG CONTROL FS CHAT NET CONSOLE\n" );
730 CONS_Printf (" VAR NETVAR CFGVAR\n" );
731 CONS_Printf ("HELP <varname>\n");
732 return;
733 }
734
735 for( i = 1; i < carg.num; i++ )
736 {
737 for( k = 0; k < NUM_HELP_CAT; k++ )
738 {
739 if( strcasecmp( carg.arg[i], helpcat_table[k].str ) == 0 )
740 {
741 if( helpcat_table[k].cctype < 20 )
742 {
743 cctype = helpcat_table[k].cctype;
744 break;
745 }
746 varflag |= helpcat_table[k].varflag;
747 cctype = 0xF0; // var listing
748 }
749 }
750 }
751
752 if((carg.num>1) && (cctype == 0))
753 {
754 cvar = CV_FindVar (carg.arg[1]);
755 if( cvar )
756 {
757 con_Printf("Variable %s:\n",cvar->name);
758 con_Printf(" flags :");
759 if( cvar->flags & CV_SAVE )
760 con_Printf("AUTOSAVE ");
761 if( cvar->flags & CV_FLOAT )
762 con_Printf("FLOAT ");
763 if( cvar->flags & CV_NETVAR )
764 con_Printf("NETVAR ");
765 if( cvar->flags & CV_CALL )
766 con_Printf("ACTION ");
767 con_Printf("\n");
768
769 if( cvar->PossibleValue )
770 {
771 CV_PossibleValue_t * pv0 = cvar->PossibleValue;
772 CV_PossibleValue_t * pv;
773 if( strcasecmp(pv0->strvalue,"MIN") == 0 )
774 {
775 // search for MAX
776 for( pv = pv0+1; pv->strvalue; pv++)
777 {
778 if( strcasecmp(pv->strvalue,"MAX") == 0 )
779 break;
780 }
781 con_Printf(" range from %d to %d\n", pv0->value, pv->value);
782 }
783 else
784 {
785 con_Printf(" possible value :\n",cvar->name);
786 for( pv = pv0; pv->strvalue; pv++)
787 {
788 con_Printf(" %-2d : %s\n", pv->value, pv->strvalue);
789 }
790 }
791 }
792 }
793 else
794 con_Printf("No Help for this command/variable\n");
795
796 return;
797 }
798
799 i = 0; // cnt vars and commands
800 if( cctype == 0 )
801 varflag = 0xFFFF; // all variables
802
803 if( cctype < 20 )
804 {
805 // commands
806 con_Printf("\2Commands\n");
807 for (cmd=com_commands ; cmd ; cmd=cmd->next)
808 {
809 if( (cctype == 0) || (cctype == cmd->cctype) )
810 {
811 con_Printf("%s ",cmd->name);
812 i++;
813 }
814 }
815 }
816
817 if( varflag )
818 {
819 // variable
820 con_Printf("\2\nVariables\n");
821 for (cvar=consvar_vars; cvar; cvar = cvar->next)
822 {
823 if( cvar->flags & varflag )
824 {
825 con_Printf("%s ",cvar->name);
826 i++;
827 }
828 }
829 }
830
831 con_Printf("\2\nRead the console docs for more or type help <command or variable>\n");
832
833 if( devparm > 1 )
834 con_Printf("\2Total : %d\n",i);
835 }
836
COM_Toggle_f(void)837 static void COM_Toggle_f(void)
838 {
839 consvar_t *cvar;
840 COM_args_t carg;
841
842 COM_Args( &carg );
843
844 if(carg.num!=2 && carg.num!=3)
845 {
846 CONS_Printf("Toggle <cvar_name> [-1]\n"
847 "Toggle the value of a cvar\n");
848 return;
849 }
850 cvar = CV_FindVar (carg.arg[1]);
851 if(!cvar)
852 {
853 CONS_Printf("%s is not a cvar\n", carg.arg[1]);
854 return;
855 }
856
857 // netcvar don't change imediately
858 cvar->flags |= CV_SHOWMODIF_ONCE; // show modification, reset flag
859 if( carg.num==3 )
860 CV_ValueIncDec(cvar, atol( carg.arg[2] ));
861 else
862 CV_ValueIncDec(cvar,+1);
863 }
864
865 // =========================================================================
866 // VARIABLE SIZE BUFFERS
867 // =========================================================================
868
869 #define VSBUFMINSIZE 256
870
VS_Alloc(vsbuf_t * buf,int initsize)871 void VS_Alloc (vsbuf_t *buf, int initsize)
872 {
873 if (initsize < VSBUFMINSIZE)
874 initsize = VSBUFMINSIZE;
875 buf->data = Z_Malloc (initsize, PU_STATIC, NULL);
876 buf->maxsize = initsize;
877 buf->cursize = 0;
878 buf->allowoverflow = false;
879 }
880
881
VS_Free(vsbuf_t * buf)882 void VS_Free (vsbuf_t *buf)
883 {
884 // Z_Free (buf->data);
885 buf->cursize = 0;
886 }
887
888
VS_Clear(vsbuf_t * buf)889 void VS_Clear (vsbuf_t *buf)
890 {
891 buf->cursize = 0;
892 }
893
894
895 // Add length to the space in use. Detect overflow.
VS_GetSpace(vsbuf_t * buf,int length)896 void *VS_GetSpace (vsbuf_t *buf, int length)
897 {
898 if (buf->cursize + length > buf->maxsize)
899 {
900 if (!buf->allowoverflow)
901 return NULL;
902
903 if (length > buf->maxsize)
904 return NULL;
905
906 buf->overflowed = true;
907 CONS_Printf ("VS buffer overflow");
908 VS_Clear (buf);
909 }
910
911 void *data = buf->data + buf->cursize;
912 buf->cursize += length;
913
914 return data;
915 }
916
917
918 #if 0
919 // [WDJ] Unused
920 // Copy data at end of variable sized buffer
921 //
922 boolean VS_Write (vsbuf_t *buf, void *data, int length)
923 {
924 void *to = VS_GetSpace(buf, length);
925 if (!to)
926 return false;
927
928 memcpy(to, data, length);
929 return true;
930 }
931 #endif
932
933
934 // Print text in variable size buffer, like VS_Write + trailing 0
935 //
VS_Print(vsbuf_t * buf,const char * data)936 boolean VS_Print (vsbuf_t *buf, const char *data)
937 {
938 int len = strlen(data) + 1;
939 int old_size = buf->cursize; // VS_GetSpace modifies cursize
940
941 // Remove trailing 0 before any consideration.
942 // Otherwise the extra accumulates until garbage gets between the appends.
943 if( old_size )
944 {
945 if( buf->data[old_size-1] == 0 )
946 buf->cursize --;
947 }
948
949 // len-1 would be enough if there already is a trailing zero, but...
950 byte *to = (byte *)VS_GetSpace(buf, len);
951 if (!to) goto fail_cleanup;
952
953 memcpy(to, data, len);
954 return true;
955
956 fail_cleanup:
957 // Restore
958 buf->cursize = old_size;
959 return false;
960 }
961
962 // =========================================================================
963 //
964 // CONSOLE VARIABLES
965 //
966 // console variables are a simple way of changing variables of the game
967 // through the console or code, at run time.
968 //
969 // console vars acts like simplified commands, because a function can be
970 // attached to them, and called whenever a console var is modified
971 //
972 // =========================================================================
973
974 static char *cv_null_string = "";
975 byte command_EV_param = 0;
976
977 static byte OnChange_user_enable = 0;
978
979 static byte CV_Pop_Config( consvar_t * cvar );
980 static void CV_set_str_value( consvar_t * cvar, const char * valstr, byte call_enable, byte user_enable );
981
982
983 // Search if a variable has been registered
984 // returns true if given variable has been registered
985 //
CV_FindVar(const char * name)986 consvar_t * CV_FindVar (const char * name)
987 {
988 consvar_t *cvar;
989
990 for (cvar=consvar_vars; cvar; cvar = cvar->next)
991 {
992 if ( !strcmp(name,cvar->name) )
993 return cvar;
994 }
995
996 return NULL;
997 }
998
999
1000 // Build a unique Net Variable identifier number, that is used
1001 // in network packets instead of the fullname
1002 //
CV_ComputeNetid(const char * s)1003 uint16_t CV_ComputeNetid (const char * s)
1004 {
1005 uint16_t ret;
1006 static byte premiers[16] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
1007 int i;
1008
1009 ret=0;
1010 i=0;
1011 while(*s)
1012 {
1013 ret += ((byte)(*s)) * ((unsigned int) premiers[i]);
1014 s++;
1015 i = (i+1)%16;
1016 }
1017 return ret;
1018 }
1019
1020
1021 // Return the Net Variable, from it's identifier number
1022 //
CV_FindNetVar(uint16_t netid)1023 static consvar_t * CV_FindNetVar (uint16_t netid)
1024 {
1025 consvar_t *cvar;
1026
1027 for (cvar=consvar_vars; cvar; cvar = cvar->next)
1028 {
1029 if (cvar->netid==netid)
1030 return cvar;
1031 }
1032
1033 return NULL;
1034 }
1035
1036
1037 // Register a variable, that can be used later at the console
1038 //
CV_RegisterVar(consvar_t * cvar)1039 void CV_RegisterVar (consvar_t *cvar)
1040 {
1041 // first check to see if it has already been defined
1042 if (CV_FindVar (cvar->name))
1043 {
1044 CONS_Printf ("Variable %s is already defined\n", cvar->name);
1045 return;
1046 }
1047
1048 // check for overlap with a command
1049 if (COM_Exists (cvar->name))
1050 {
1051 CONS_Printf ("%s is a command name\n", cvar->name);
1052 return;
1053 }
1054
1055 cvar->string = NULL;
1056 cvar->state = 0;
1057
1058 // check net cvars
1059 if (cvar->flags & CV_NETVAR)
1060 {
1061 cvar->netid = CV_ComputeNetid (cvar->name);
1062 if (CV_FindNetVar(cvar->netid))
1063 I_Error("Variable %s has duplicate netid\n",cvar->name);
1064 }
1065
1066 // link the cvar in
1067 if( !(cvar->flags & CV_HIDEN) )
1068 {
1069 cvar->next = consvar_vars;
1070 consvar_vars = cvar;
1071 }
1072
1073 #ifdef PARANOIA
1074 if ((cvar->flags & CV_NOINIT) && !(cvar->flags & CV_CALL))
1075 I_Error("variable %s has CV_NOINIT without CV_CALL\n",cvar->name);
1076 if ((cvar->flags & CV_CALL) && !cvar->func)
1077 I_Error("variable %s has CV_CALL without func",cvar->name);
1078 #endif
1079 CV_set_str_value( cvar, cvar->defaultvalue,
1080 ((cvar->flags & CV_NOINIT) == 0), // call_enable
1081 1 ); // user_enable, default is a user setting
1082
1083
1084 // CV_set_str_value will have set this bit
1085 cvar->state &= ~CS_MODIFIED;
1086 }
1087
1088
1089 // Returns the string value of a console var
1090 //
CV_StringValue(const char * var_name)1091 static char * CV_StringValue (const char * var_name)
1092 {
1093 consvar_t *cvar;
1094
1095 cvar = CV_FindVar (var_name);
1096 if (!cvar)
1097 return cv_null_string;
1098 return cvar->string;
1099 }
1100
1101
1102 // Completes the name of a console var
1103 //
CV_CompleteVar(const char * partial,int skips)1104 const char * CV_CompleteVar (const char * partial, int skips)
1105 {
1106 consvar_t *cvar;
1107 int len;
1108
1109 len = strlen(partial);
1110
1111 if (!len)
1112 return NULL;
1113
1114 // check functions
1115 for (cvar=consvar_vars ; cvar ; cvar=cvar->next)
1116 {
1117 if (!strncmp (partial,cvar->name, len))
1118 {
1119 if (!skips--)
1120 return cvar->name;
1121 }
1122 }
1123
1124 return NULL;
1125 }
1126
1127 // [WDJ] Hard to tell yet which of the two methods has the least problems.
1128 // For now, they both work, and are about the same size.
1129 #define COMMAND_RECOVER_STRING
1130 #ifdef COMMAND_RECOVER_STRING
1131 static const char * cvar_string_min = NULL;
1132 static const char * cvar_string_max = NULL;
1133 #endif
1134
1135 // Free this string allocation, when it is not a PossibleValue const.
1136 static
CV_Free_cvar_string_param(consvar_t * cvar,char * str)1137 void CV_Free_cvar_string_param( consvar_t * cvar, char * str )
1138 {
1139 #ifdef COMMAND_RECOVER_STRING
1140 // Check if is in bounds of allocated cvar strings.
1141 if( cvar_string_min
1142 && str >= cvar_string_min
1143 && str <= cvar_string_max )
1144 {
1145 // was allocated
1146 Z_Free( str );
1147 }
1148 #else
1149 // It is Z_StrDup or a string from PossibleValue
1150 CV_PossibleValue_t * pv = cvar->PossibleValue;
1151 if( pv )
1152 {
1153 for( ; pv->strvalue ; pv++ )
1154 {
1155 if( str == pv->strvalue )
1156 return; // was a ptr to a PossibleValue string
1157 }
1158 }
1159
1160 // not in PossibleValue
1161 Z_Free( str );
1162 #endif
1163 }
1164
1165 // Free the cvar string allocation, when it is not a PossibleValue const.
CV_Free_cvar_string(consvar_t * cvar)1166 void CV_Free_cvar_string( consvar_t * cvar )
1167 {
1168 if( cvar->string )
1169 {
1170 CV_Free_cvar_string_param( cvar, cvar->string );
1171
1172 cvar->string = NULL;
1173 }
1174 }
1175
1176 // Makes a copy of the string, and handles PossibleValue string values.
1177 // str : a reference to a string, it will be copied.
CV_Set_cvar_string(consvar_t * cvar,const char * str)1178 void CV_Set_cvar_string( consvar_t * cvar, const char * str )
1179 {
1180 CV_PossibleValue_t * pv;
1181
1182 // Free an allocated existing string.
1183 CV_Free_cvar_string( cvar );
1184
1185 // Check if str is Z_StrDup or a string from PossibleValue
1186 pv = cvar->PossibleValue;
1187 if( pv )
1188 {
1189 for( ; pv->strvalue ; pv++ )
1190 {
1191 // Only if it is a pointer to a PossibleValue string.
1192 if( str == pv->strvalue )
1193 goto update; // just point to it, by reference
1194 }
1195 }
1196
1197 // Have to copy it.
1198 str = Z_StrDup( str );
1199 #ifdef COMMAND_RECOVER_STRING
1200 // Track range of allocated cvar strings.
1201 if( cvar_string_min == NULL || str < cvar_string_min )
1202 cvar_string_min = str;
1203 if( str > cvar_string_max )
1204 cvar_string_max = str;
1205 #endif
1206
1207 update:
1208 // current cvar
1209 cvar->string = (char*) str;
1210 return;
1211 }
1212
1213
1214 // Do the CV_CALL, with validity tests, and enforcing user_enable rules.
1215 static
CV_cvar_call(consvar_t * cvar,byte user_enable)1216 void CV_cvar_call( consvar_t *cvar, byte user_enable )
1217 {
1218 // Call the CV_CALL func to restore state dependent upon this setting.
1219 // Set the 'on change' code.
1220 if((cvar->flags & CV_CALL) && (cvar->func))
1221 {
1222 // Handle recursive OnChange calls. Propagate a valid user_enable.
1223 byte cfg = cvar->state & CS_CONFIG;
1224 OnChange_user_enable = user_enable && ((cfg >= CFG_main) && (cfg <= CFG_drawmode));
1225 cvar->func();
1226 }
1227 }
1228
1229
1230 // Set variable value, for user settings, save games, and network settings.
1231 // Updates value and EV.
1232 // Does NOT relay NETVAR to clients.
1233 // call_enable : when 0, blocks CV_CALL
1234 // user_enable : enable setting the string value which gets saved in config files.
1235 static
CV_set_str_value(consvar_t * cvar,const char * valstr,byte call_enable,byte user_enable)1236 void CV_set_str_value( consvar_t * cvar, const char * valstr, byte call_enable, byte user_enable )
1237 {
1238 char value_str[64]; // print %d cannot exceed 64
1239 CV_PossibleValue_t * pv0, * pv;
1240 int ival;
1241 byte is_a_number = 0;
1242
1243 #ifdef PARANOIA
1244 if( valstr == NULL )
1245 {
1246 I_SoftError( "CV_set_str_value passed NULL string: %s\n", cvar->name );
1247 return;
1248 }
1249 #endif
1250
1251 // [WDJ] If the value is a float, then all comparisons must be fixed_t.
1252 // Any PossibleValues would be fixed_t too.
1253 if( cvar->flags & CV_FLOAT )
1254 {
1255 // store as fixed_t
1256 double d = atof( valstr );
1257 ival = (int)(d * FRACUNIT);
1258 }
1259 else
1260 {
1261 ival = atoi(valstr); // enum and integer values
1262 }
1263
1264 if( ival )
1265 {
1266 is_a_number = 1; // atoi or atof found a number
1267 }
1268 else
1269 {
1270 // Deal with the case where there are leading spaces.
1271 const char * c = valstr;
1272 while( *c == ' ' ) c++; // skip spaces
1273 is_a_number = (*c >= '0' && *c <= '9');
1274 }
1275
1276 pv0 = cvar->PossibleValue;
1277 if( pv0 )
1278 {
1279 if( strcasecmp(pv0->strvalue,"MIN") == 0 )
1280 { // bounded cvar
1281 // search for MAX
1282 for( pv = pv0+1; pv->strvalue; pv++)
1283 {
1284 if( strcasecmp(pv->strvalue,"MAX") == 0 )
1285 break;
1286 }
1287
1288 #ifdef PARANOIA
1289 if( pv->strvalue == NULL )
1290 I_Error("Bounded cvar \"%s\" without MAX !", cvar->name);
1291 #endif
1292 // PossibleValue is MIN MAX, so value must be a number.
1293 if( ! is_a_number ) goto error;
1294
1295 // [WDJ] Cannot print into const string.
1296 if(ival < pv0->value) // MIN value
1297 {
1298 ival = pv0->value;
1299 sprintf(value_str,"%d", ival);
1300 valstr = value_str;
1301 }
1302 if(ival > pv->value) // supposedly MAX value
1303 {
1304 ival = pv->value;
1305 sprintf(value_str,"%d", ival);
1306 valstr = value_str;
1307 }
1308 }
1309 else
1310 {
1311 // waw spaghetti programming ! :)
1312
1313 // check for string match
1314 for( pv = pv0; pv->strvalue; pv++)
1315 {
1316 if( strcasecmp(pv->strvalue, valstr) == 0 )
1317 goto found_possible_value;
1318 }
1319
1320 // If valstr is not a number, then it cannot be used as a PossibleValue.
1321 if( ! is_a_number ) goto error;
1322
1323 // check as PossibleValue number
1324 for( pv = pv0; pv->strvalue; pv++)
1325 {
1326 if( ival == pv->value )
1327 goto found_possible_value;
1328 }
1329 goto error;
1330
1331 found_possible_value:
1332 ival = pv->value;
1333 if( user_enable )
1334 {
1335 // [WDJ] Used to assume existing was a const string, whenever the new string
1336 // was a const string. Cannot prove that assumption so call CV_Free.
1337 CV_Free_cvar_string( cvar );
1338 cvar->value = ival;
1339 // When value is from PossibleValue, string is a const char *.
1340 cvar->string = (char*) pv->strvalue;
1341 }
1342 goto finish;
1343 }
1344 }
1345
1346 // CV_STRING has no temp values, and is used for network addresses.
1347 // Block it for security reasons, to prevent redirecting.
1348 // Only change the cvar string when user is making the change.
1349 if( user_enable )
1350 {
1351 // free the old value string, set the new value
1352 CV_Set_cvar_string( cvar, valstr );
1353 }
1354
1355 // Update value when set by user, or if flagged as numeric value.
1356 // CV_uint16, CV_Unsigned values may not fit into EV.
1357 if( cvar->flags & (CV_FLOAT | CV_VALUE) )
1358 {
1359 if( ! is_a_number ) goto error;
1360 cvar->value = ival;
1361 }
1362 else if( user_enable )
1363 {
1364 cvar->value = ival;
1365 }
1366
1367
1368 finish:
1369 // The SHOWMODIF is display of CV_Set, and not other set paths.
1370 if( cvar->flags & (CV_SHOWMODIF | CV_SHOWMODIF_ONCE) )
1371 {
1372 CONS_Printf("%s set to %s\n", cvar->name, valstr );
1373 cvar->flags &= ~CV_SHOWMODIF_ONCE;
1374 }
1375 DEBFILE(va("%s set to %s\n", cvar->name, cvar->string));
1376
1377 cvar->state |= CS_MODIFIED;
1378 cvar->EV = ival; // user setting of active value
1379
1380 // raise 'on change' code
1381 if( call_enable )
1382 CV_cvar_call( cvar, user_enable );
1383 return;
1384
1385 error: // not found
1386 CONS_Printf("\"%s\" is not a possible value for \"%s\"\n", valstr, cvar->name);
1387 if( strcasecmp(cvar->defaultvalue, valstr) == 0 )
1388 {
1389 I_SoftError("Variable %s default value \"%s\" is not a possible value\n",
1390 cvar->name, cvar->defaultvalue);
1391 }
1392 return;
1393 }
1394
1395 // Called after demo to restore the user settings.
1396 // Copies value to EV.
CV_Restore_User_Settings(void)1397 void CV_Restore_User_Settings( void )
1398 {
1399 consvar_t * cvar;
1400
1401 // Check for modified cvar
1402 for (cvar=consvar_vars; cvar; cvar = cvar->next)
1403 {
1404 if( cvar->state & CS_EV_PROT ) // protected EV value
1405 continue;
1406
1407 if((cvar->state & CS_CONFIG) > CFG_other )
1408 {
1409 // Undo a push of NETVAR
1410 CV_Pop_Config( cvar ); // CV_CALL
1411 cvar->state &= ~CS_EV_PARAM;
1412 continue;
1413 }
1414
1415 if( cvar->flags & CV_VALUE )
1416 {
1417 cvar->value = atoi( cvar->string );
1418 }
1419
1420 if( (cvar->EV != (byte)cvar->value)
1421 || (cvar->value >> 8)
1422 || (cvar->state & CS_EV_PARAM) ) // command line param in EV
1423 {
1424 cvar->EV = cvar->value; // user setting of active value
1425 CV_cvar_call( cvar, 1 );
1426 cvar->state &= ~CS_EV_PARAM;
1427 }
1428 }
1429 command_EV_param = 0;
1430 }
1431
1432
1433 //
1434 // Use XD_NETVAR argument :
1435 // 2 byte for variable identification
1436 // then the value of the variable followed with a 0 byte (like str)
1437 //
1438 // Receive network game settings, or restore save game.
Got_NetXCmd_NetVar(xcmd_t * xc)1439 void Got_NetXCmd_NetVar(xcmd_t * xc)
1440 {
1441 byte * bp = xc->curpos; // macros READ,SKIP want byte*
1442
1443 consvar_t *cvar = CV_FindNetVar(READU16(bp)); // netvar id
1444 char *svalue = (char *)bp; // after netvar id
1445
1446 while( *(bp++) ) { // find 0 term
1447 if( bp > xc->endpos ) goto buff_overrun; // bad string
1448 }
1449 xc->curpos = bp; // return updated ptr only once
1450
1451 if(cvar==NULL)
1452 {
1453 CONS_Printf("\2Netvar not found\n");
1454 return;
1455 }
1456
1457 if( cvar->flags & (CV_FLOAT | CV_VALUE | CV_STRING))
1458 {
1459 // Netvar value will not fit in EV, so use NETVAR push.
1460 CV_Put_Config_string( cvar, CFG_netvar, svalue );
1461 // Current config is netvar setting (not saved).
1462 }
1463 else
1464 {
1465 // Put netvar value in EV.
1466 CV_set_str_value(cvar, svalue, 1, 0); // CV_CALL, temp
1467 // Current config is netvar setting (not saved).
1468 // Not visible to menu. Menu displays the string.
1469 }
1470 return;
1471
1472 buff_overrun:
1473 xc->curpos = xc->endpos+2; // indicate overrun
1474 return;
1475 }
1476
1477
1478 // Called by SV_Send_ServerConfig, P_Savegame_Save_game.
CV_SaveNetVars(xcmd_t * xc)1479 void CV_SaveNetVars(xcmd_t * xc)
1480 {
1481 char buf[32];
1482 char * vp;
1483 consvar_t *cvar;
1484 byte * bp = xc->curpos; // macros want byte*
1485
1486
1487 // We must send all NETVAR cvar, because on another machine,
1488 // some of them may have a different value.
1489 for (cvar=consvar_vars; cvar; cvar = cvar->next)
1490 {
1491 if( ! (cvar->flags & CV_NETVAR) ) continue;
1492
1493 // Command line settings goto network games and savegames.
1494 // CV_STRING do not have temp values.
1495 if( cvar->state & CS_EV_PARAM ) // command line param in EV
1496 {
1497 // Send the EV param value instead.
1498 sprintf (buf, "%d", cvar->EV);
1499 vp = buf;
1500 }
1501 else if( cvar->flags & CV_VALUE )
1502 {
1503 // Value has precedence over the string.
1504 sprintf (buf, "%d", cvar->value);
1505 vp = buf;
1506 }
1507 else
1508 {
1509 vp = cvar->string;
1510 }
1511 // potential buffer overrun test
1512 if((bp + 2 + strlen(vp)) > xc->endpos ) goto buff_overrun;
1513 // Format: netid uint16, var_string str0.
1514 WRITE16(bp,cvar->netid);
1515 bp = write_string(bp, vp);
1516 }
1517 xc->curpos = bp; // return updated ptr only once
1518 return;
1519
1520 buff_overrun:
1521 I_SoftError( "Net Vars overrun available packet space\n" );
1522 return;
1523 }
1524
1525 // Client: Receive server netvars state. Server config.
CV_LoadNetVars(xcmd_t * xc)1526 void CV_LoadNetVars(xcmd_t * xc)
1527 {
1528 consvar_t *cvar;
1529
1530 // Read netvar from byte stream, identified by netid.
1531 for (cvar=consvar_vars; cvar; cvar = cvar->next)
1532 {
1533 if (cvar->flags & CV_NETVAR)
1534 Got_NetXCmd_NetVar( xc );
1535 // curpos on last read can go to endpos+1
1536 if(xc->curpos > xc->endpos+1) goto buff_overrun;
1537 }
1538 return;
1539
1540 buff_overrun:
1541 I_SoftError( "Load Net Vars overran packet buffer\n" );
1542 return;
1543 }
1544
1545 #define SET_BUFSIZE 128
1546
1547 // PUBLIC
1548
1549 // Sets a var to a string value.
1550 // called by CV_Var_Command to handle "<varname> <value>" entered at the console
CV_Set(consvar_t * cvar,const char * str_value)1551 void CV_Set (consvar_t *cvar, const char *str_value)
1552 {
1553 //changed = strcmp(var->string, value);
1554 #ifdef PARANOIA
1555 if(!cvar)
1556 I_Error("CV_Set : no variable\n");
1557
1558 // Not an error if cvar does not have string.
1559 #endif
1560
1561 if( cvar->string )
1562 {
1563 if( strcasecmp(cvar->string, str_value) == 0 )
1564 return; // no changes
1565 }
1566
1567 if (netgame)
1568 {
1569 // in a netgame, certain cvars are handled differently
1570 if (cvar->flags & CV_NET_LOCK)
1571 {
1572 CONS_Printf("This variable cannot be changed during a netgame.\n");
1573 return;
1574 }
1575
1576 if( cvar->flags & CV_NETVAR )
1577 {
1578 if (!server)
1579 {
1580 CONS_Printf("Only the server can change this variable.\n");
1581 return;
1582 }
1583
1584 // Change user settings too, but want only one CV_CALL.
1585 CV_set_str_value(cvar, str_value, 0, 1); // no CALL, user
1586
1587 // send the value of the variable
1588 byte buf[SET_BUFSIZE], *p; // macros want byte*
1589 p = buf;
1590 // Format: netid uint16, var_string str0.
1591 WRITEU16(p, cvar->netid);
1592 p = write_stringn(p, str_value, SET_BUFSIZE-2-1);
1593 SV_Send_NetXCmd(XD_NETVAR, buf, (p - buf)); // as server
1594 // NetXCmd will set as netvar, CV_CALL, not user.
1595 // This NetXCmd is also used by savegame restore, so it cannot block server.
1596 return;
1597 }
1598 }
1599
1600 // Single player
1601 CV_set_str_value(cvar, str_value, 1, 1); // CALL, user
1602 }
1603
1604
1605 // Expands value to string before calling CV_Set ()
1606 //
CV_SetValue(consvar_t * cvar,int value)1607 void CV_SetValue (consvar_t *cvar, int value)
1608 {
1609 char val[32];
1610
1611 sprintf (val, "%d", value);
1612 CV_Set (cvar, val);
1613 }
1614
1615 // Set a command line parameter value (temporary).
1616 // This should not affect owner saved values.
CV_SetParam(consvar_t * cvar,int value)1617 void CV_SetParam (consvar_t *cvar, int value)
1618 {
1619 command_EV_param = 1; // flag to undo these later
1620 cvar->EV = value; // temp setting, during game play
1621 cvar->state |= CS_EV_PARAM;
1622 CV_cvar_call( cvar, 0 ); // not user
1623 }
1624
1625 // If a OnChange func tries to change other values,
1626 // this function should be used.
1627 // It will determine the same user_enable.
CV_Set_by_OnChange(consvar_t * cvar,int value)1628 void CV_Set_by_OnChange (consvar_t *cvar, int value)
1629 {
1630 byte saved_user_enable = OnChange_user_enable;
1631 if( OnChange_user_enable )
1632 {
1633 CV_SetValue( cvar, value );
1634 }
1635 else
1636 {
1637 CV_SetParam( cvar, value );
1638 }
1639 OnChange_user_enable = saved_user_enable;
1640 }
1641
1642
1643
CV_ValueIncDec(consvar_t * cvar,int increment)1644 void CV_ValueIncDec (consvar_t *cvar, int increment)
1645 {
1646 int newvalue = cvar->value + increment;
1647 CV_PossibleValue_t * pv0 = cvar->PossibleValue; // array of
1648
1649 if( pv0 )
1650 {
1651 // If first item in list is "MIN"
1652 if( strcmp( pv0->strvalue,"MIN") == 0 )
1653 {
1654 // MIN .. MAX
1655 int min_value = pv0->value; // MIN value
1656 int max_value = INT_MAX;
1657 CV_PossibleValue_t * pv;
1658
1659 // Search the list for MAX value, or INC.
1660 for( pv = pv0; pv->strvalue ; pv++ )
1661 {
1662 if( strcmp(pv->strvalue,"INC") == 0 )
1663 {
1664 // Has an INC
1665 newvalue = cvar->value + (increment * pv->value);
1666 }
1667 else
1668 {
1669 max_value = pv->value; // last value is assumed "MAX"
1670 }
1671 }
1672
1673 if( newvalue < min_value )
1674 {
1675 // To accomodate negative increment.
1676 newvalue += max_value - min_value + 1; // add the max+1
1677 }
1678 newvalue = min_value
1679 + ((newvalue - min_value) % (max_value - min_value + 1));
1680
1681 CV_SetValue(cvar,newvalue);
1682 }
1683 else
1684 {
1685 // List of Values
1686 int max, currentindice=-1;
1687
1688 // this code do not support more than same value for differant PossibleValue
1689 for( max = 0; ; max++ )
1690 {
1691 if( pv0[max].strvalue == NULL ) break; // end of list
1692 if( pv0[max].value == cvar->value )
1693 currentindice = max;
1694 }
1695 // max is at NULL, has count of possible value list
1696 #ifdef PARANOIA
1697 if( currentindice == -1 )
1698 {
1699 I_SoftError("CV_ValueIncDec : current value %d not found in possible value\n", cvar->value);
1700 return;
1701 }
1702 #endif
1703 // calculate position in possiblevalue
1704 // max is list count
1705 // To accommodate neg increment, add extra list count.
1706 // Modulo result back into the possible value range,
1707 int newindice = ( currentindice + increment + max) % (max);
1708 CV_Set(cvar, pv0[newindice].strvalue);
1709 }
1710 }
1711 else
1712 {
1713 CV_SetValue(cvar,newvalue);
1714 }
1715 }
1716
1717
1718 // =================
1719 // Pushed cvar values
1720
1721 typedef struct cv_pushed_s {
1722 struct cv_pushed_s * next;
1723 consvar_t * parent;
1724 char * string; // value in string
1725 int32_t value; // for int and fixed_t
1726 byte state; // cv_state_e
1727 } cv_pushed_t;
1728
1729 cv_pushed_t * cvar_pushed_list = NULL; // malloc
1730
1731
1732 // Frees the string and the pushed record.
1733 static
release_pushed_cvar(cv_pushed_t * pp_rel)1734 void release_pushed_cvar( cv_pushed_t * pp_rel )
1735 {
1736 // unlink from pushed list
1737 if( cvar_pushed_list == pp_rel )
1738 {
1739 cvar_pushed_list = pp_rel->next;
1740 }
1741 else
1742 {
1743 // find the pp_rel in the list.
1744 cv_pushed_t * pp;
1745 for( pp = cvar_pushed_list; pp ; pp = pp->next )
1746 {
1747 if( pp->next == pp_rel )
1748 {
1749 // found it, unlink it.
1750 pp->next = pp_rel->next;
1751 break;
1752 }
1753 }
1754 }
1755
1756 // The string must be freed.
1757 if( pp_rel->string )
1758 CV_Free_cvar_string_param( pp_rel->parent, pp_rel->string );
1759
1760 free( pp_rel );
1761 }
1762
1763 static
create_pushed_cvar(consvar_t * parent_cvar)1764 cv_pushed_t * create_pushed_cvar( consvar_t * parent_cvar )
1765 {
1766 cv_pushed_t * pp = (cv_pushed_t*) malloc( sizeof(cv_pushed_t) );
1767 if( pp )
1768 {
1769 // link into the push list
1770 pp->next = cvar_pushed_list;
1771 cvar_pushed_list = pp;
1772
1773 pp->parent = parent_cvar;
1774 }
1775 return pp;
1776 }
1777
1778 // Search the pushed cv for a matching config.
1779 static
find_pushed_cvar(consvar_t * cvar,byte cfg)1780 cv_pushed_t * find_pushed_cvar( consvar_t * cvar, byte cfg )
1781 {
1782 cv_pushed_t * pp;
1783 for( pp = cvar_pushed_list; pp ; pp = pp->next )
1784 {
1785 if( (pp->parent == cvar)
1786 && ((pp->state & CS_CONFIG) == cfg) )
1787 {
1788 return pp;
1789 }
1790 }
1791 return NULL; // not found
1792 }
1793
1794 // update the CS_PUSHED flag
1795 static
update_pushed_cvar(consvar_t * cvar)1796 void update_pushed_cvar( consvar_t * cvar )
1797 {
1798 cv_pushed_t * pp;
1799 for( pp = cvar_pushed_list; pp ; pp = pp->next )
1800 {
1801 if( pp->parent == cvar )
1802 {
1803 cvar->state |= CS_PUSHED;
1804 return;
1805 }
1806 }
1807 // none found
1808 cvar->state &= ~CS_PUSHED;
1809 }
1810
1811 // new_cfg : the new config that is causing the push
1812 static
CV_Push_Config(consvar_t * cvar,byte new_cfg)1813 void CV_Push_Config( consvar_t * cvar, byte new_cfg )
1814 {
1815 cv_pushed_t * pp = create_pushed_cvar( cvar );
1816 if( ! pp )
1817 return;
1818
1819 // save cvar values
1820 pp->string = cvar->string; // move the string
1821 cvar->string = NULL;
1822 pp->value = cvar->value;
1823 pp->state = cvar->state;
1824
1825 // update the current cvar
1826 cvar->state = (cvar->state & ~CS_CONFIG) | new_cfg | CS_PUSHED;
1827 }
1828
1829 // return 1 if pop succeeded, 0 if no pop.
1830 static
CV_Pop_Config(consvar_t * cvar)1831 byte CV_Pop_Config( consvar_t * cvar )
1832 {
1833 cv_pushed_t * pp;
1834 byte old_config = (cvar->state & CS_CONFIG);
1835 while( --old_config )
1836 {
1837 pp = find_pushed_cvar( cvar, old_config );
1838 if( pp ) goto restore_cvar;
1839 }
1840 return 0;
1841
1842 restore_cvar:
1843 // restore cvar values
1844 // Move the pushed string to the cvar.
1845 CV_Free_cvar_string( cvar );
1846 cvar->string = pp->string; // move string, as pushed cvar will be released
1847 pp->string = NULL; // because the string in the pushed record will be freed.
1848
1849 // If this happens during a demo or other usage, protections have already been applied.
1850 cvar->EV = pp->value;
1851 cvar->value = pp->value;
1852 cvar->state = pp->state;
1853
1854 release_pushed_cvar( pp );
1855 update_pushed_cvar( cvar );
1856
1857 CV_cvar_call( cvar, 1 ); // Pop brings user settings back into force.
1858 return 1;
1859 }
1860
1861 // Public
1862
1863 // Get the values of a pushed cvar, into the temp cvar.
1864 // pushed_cvar : copy of the cvar values, is assumed to be uninitialized,
1865 // any lingering string value will not be freed
1866 // temp_cvar : an uninitialized cvar to receive the value
1867 // If NULL, then is just a check on existance.
1868 // Return false if not found.
CV_Get_Pushed_cvar(consvar_t * cvar,byte cfg,consvar_t * temp_cvar)1869 boolean CV_Get_Pushed_cvar( consvar_t * cvar, byte cfg, /*OUT*/ consvar_t * temp_cvar )
1870 {
1871 cv_pushed_t * pp = find_pushed_cvar( cvar, cfg );
1872 if( !pp )
1873 return false;
1874
1875 if( temp_cvar )
1876 {
1877 memcpy( temp_cvar, cvar, sizeof(consvar_t) ); // setup values
1878
1879 temp_cvar->string = NULL; // must be Z_StrDup, not copied
1880 CV_Set_cvar_string( temp_cvar, pp->string ); // may get edited
1881
1882 temp_cvar->value = pp->value;
1883 temp_cvar->state = pp->state;
1884 // EV is not needed
1885 }
1886 return true;
1887 }
1888
1889
1890 // Put the values in the temp cvar, into the pushed or current cvar.
1891 // temp_cvar : copy of the cvar values, cannot be NULL
1892 // The string value of temp_cvar will be stolen.
CV_Put_Config_cvar(consvar_t * cvar,byte cfg,consvar_t * temp_cvar)1893 void CV_Put_Config_cvar( consvar_t * cvar, byte cfg, /*IN*/ consvar_t * temp_cvar )
1894 {
1895 cv_pushed_t * pp;
1896 byte current_cfg = cvar->state & CS_CONFIG;
1897
1898 if( cfg == current_cfg )
1899 goto update_cvar; // put to the current cvar
1900
1901 if( cfg > current_cfg )
1902 {
1903 // push the current cvar
1904 CV_Push_Config( cvar, cfg ); // change current to cfg
1905 goto update_cvar; // put to the new current cvar
1906 }
1907
1908 // assert (cfg < current_cfg)
1909 // Put to the pushed cvar record.
1910 pp = find_pushed_cvar( cvar, cfg );
1911 if( pp )
1912 {
1913 // Free the existing string value first.
1914 CV_Free_cvar_string_param( cvar, pp->string );
1915 }
1916 else
1917 {
1918 // Not found, make a new one.
1919 pp = create_pushed_cvar( cvar );
1920 if( pp == NULL ) return;
1921 }
1922 // assert (pp->string == NULL or invalid)
1923 // Move the temp_cvar string to the pushed record.
1924 pp->string = temp_cvar->string;
1925 temp_cvar->string = NULL;
1926
1927 pp->value = temp_cvar->value;
1928 pp->state = (temp_cvar->state & ~CS_CONFIG) | cfg;
1929 cvar->state |= CS_PUSHED; // Mark existance of pushed cfg.
1930 return;
1931
1932 update_cvar:
1933 // Update current cvar
1934 CV_Free_cvar_string( cvar );
1935 cvar->string = temp_cvar->string; // move the string
1936 temp_cvar->string = NULL;
1937
1938 // If this happens during a demo or other usage, protections have already been applied.
1939 cvar->value = temp_cvar->value;
1940 cvar->EV = temp_cvar->value;
1941 // preserve CS_PUSHED
1942 cvar->state = (cvar->state & ~CS_CONFIG) | cfg | (temp_cvar->state & CS_MODIFIED);
1943
1944 // Current cvar always does CV_CALL.
1945 CV_cvar_call( cvar, 1 ); // user_enable detected in new config.
1946 return;
1947 }
1948
1949
1950 // Return the string value of the config var, current or pushed.
1951 // Return NULL if not found.
CV_Get_Config_string(consvar_t * cvar,byte cfg)1952 const char * CV_Get_Config_string( consvar_t * cvar, byte cfg )
1953 {
1954 // Check the current cvar first.
1955 if( (cvar->state & CS_CONFIG) == cfg )
1956 return cvar->string;
1957
1958 if( cvar->state & CS_PUSHED )
1959 {
1960 cv_pushed_t * pp = find_pushed_cvar( cvar, cfg );
1961 if( pp )
1962 return pp->string;
1963 }
1964
1965 return NULL; // not found
1966 }
1967
1968 // Put the string value to the pushed or current cvar.
1969 // This will create or push, as needed.
1970 // str : str value, will be copied. Will set numeric value too.
CV_Put_Config_string(consvar_t * cvar,byte cfg,const char * str)1971 void CV_Put_Config_string( consvar_t * cvar, byte cfg, const char * str )
1972 {
1973 consvar_t new_cvar;
1974
1975 // Copy the parent cvar, need PossibleValue and flags.
1976 memcpy( &new_cvar, cvar, sizeof(consvar_t) );
1977 new_cvar.string = NULL;
1978 if( (cvar->state & CS_CONFIG) > cfg )
1979 {
1980 // Create as pushed cvar value.
1981 // Kill any effects that only the current cvar should perform.
1982 // Pushed cvar does not save these flags, flags will be gotten from parent.
1983 new_cvar.flags &= ~( CV_CALL | CV_NETVAR | CV_SHOWMODIF | CV_SHOWMODIF_ONCE );
1984 }
1985
1986 // Copy the new value into the temp cvar.
1987 CV_set_str_value( &new_cvar, str, 0, 1 );
1988
1989 // Create the cvar value, even if it is pushed and not current.
1990 // This will steal the string value from temp_cvar. Do not need to Z_Free it.
1991 CV_Put_Config_cvar( cvar, cfg, &new_cvar ); // does CV_CALL
1992 }
1993
1994
1995
1996 // Remove the cvar value for the config.
CV_Delete_Config_cvar(consvar_t * cvar,byte cfg)1997 void CV_Delete_Config_cvar( consvar_t * cvar, byte cfg )
1998 {
1999 // Check current cvar first
2000 if( (cvar->state & CS_CONFIG) == cfg )
2001 {
2002 // Remove the current cvar
2003 if( CV_Pop_Config( cvar ) == 0 )
2004 {
2005 // No pushed value found
2006 cvar->state &= ~CS_CONFIG; // set config to 0
2007 }
2008 }
2009 else
2010 {
2011 // Remove the pushed cvar.
2012 cv_pushed_t * pp = find_pushed_cvar( cvar, cfg );
2013 if( pp )
2014 {
2015 release_pushed_cvar( pp ); // free the string too
2016 }
2017 }
2018 }
2019
2020
2021 // Clear all values that came from the config file.
CV_Clear_Config(byte cfg)2022 void CV_Clear_Config( byte cfg )
2023 {
2024 consvar_t * cv;
2025
2026 // Clear from pushed list
2027 cv_pushed_t * pp = cvar_pushed_list;
2028 while( pp )
2029 {
2030 cv_pushed_t * ppn = pp->next;
2031 if((pp->state & CS_CONFIG) == cfg )
2032 {
2033 cv = pp->parent; // get it before releasing
2034 release_pushed_cvar( pp ); // free the string too
2035 update_pushed_cvar( cv ); // update CS_PUSHED
2036 }
2037 pp = ppn;
2038 }
2039
2040 // Clear from current cv vars.
2041 for( cv = CV_IteratorFirst(); cv ; cv = CV_Iterator(cv))
2042 {
2043 if( (cv->state & CS_CONFIG) == cfg )
2044 CV_Pop_Config(cv);
2045 }
2046 }
2047
2048
2049 // Check for drawmode CV variables.
2050 // Return true if any value of the config is current, or pushed.
CV_Config_check(byte cfg)2051 boolean CV_Config_check( byte cfg )
2052 {
2053 consvar_t * cv;
2054 for( cv = CV_IteratorFirst(); cv ; cv = CV_Iterator( cv ) )
2055 {
2056 if( (cv->flags & CV_SAVE) == 0 ) continue;
2057
2058 if( (cv->state & CS_CONFIG) == cfg )
2059 return true;
2060
2061 if( cv->state & CS_PUSHED )
2062 {
2063 if( CV_Get_Pushed_cvar( cv, cfg, NULL ) )
2064 return true;
2065 }
2066 }
2067 return false;
2068 }
2069
2070
2071
2072 // =================
2073 //
2074
2075
2076 // Allow display of variable content or change from the console
2077 //
2078 // Returns false if the passed command was not recognised as
2079 // console variable.
2080 //
2081 // cfg : cv_config_e
CV_Var_Command(byte cfg)2082 static boolean CV_Var_Command ( byte cfg )
2083 {
2084 consvar_t *cvar;
2085 const char * tstr;
2086 COM_args_t carg;
2087 int tval;
2088
2089 COM_Args( &carg );
2090
2091 // check variables
2092 cvar = CV_FindVar ( carg.arg[0] );
2093 if(!cvar)
2094 return false;
2095
2096 // perform a variable print or set
2097 if ( carg.num == 1 ) goto show_value;
2098
2099 // Set value
2100 cfg &= CS_CONFIG; // only config selection
2101 if( cfg )
2102 {
2103 if( cvar->flags & CV_CFG1 )
2104 {
2105 // A restricted var cannot be loaded from the other config files.
2106 if( cfg != CFG_main )
2107 return false;
2108 }
2109 if( cvar->flags & CV_NETVAR )
2110 {
2111 // A NETVAR cannot be loaded from the drawmode config file.
2112 if( cfg == CFG_drawmode )
2113 return false;
2114 }
2115
2116 byte old_cfg = cvar->state & CS_CONFIG;
2117 if( old_cfg )
2118 {
2119 // current cvar values are not the default values.
2120 if( old_cfg < cfg )
2121 {
2122 // Push the current value
2123 CV_Push_Config( cvar, cfg );
2124 }
2125 else if( old_cfg > cfg )
2126 {
2127 // It likely was pushed already.
2128 // Do not use CV_Set, because this will not become the current value.
2129 CV_Put_Config_string( cvar, cfg, carg.arg[1] );
2130 return true;
2131 }
2132 }
2133
2134 // Record which config file the setting comes from.
2135 cvar->state &= ~CS_CONFIG;
2136 cvar->state |= cfg;
2137 }
2138
2139 CV_Set (cvar, carg.arg[1] );
2140 return true;
2141
2142 show_value:
2143 if( cvar->flags & CV_STRING ) goto std_show_str;
2144 if( cvar->flags & CV_VALUE )
2145 {
2146 if( cvar->value == atoi(cvar->string) ) goto std_show_str;
2147 tval = cvar->value;
2148 }
2149 else if( (cvar->state & CS_EV_PARAM) // command line param in EV
2150 || (cvar->EV != (byte)cvar->value) )
2151 {
2152 tval = cvar->EV;
2153 }
2154 else goto std_show_str;
2155
2156 if( cvar->PossibleValue )
2157 {
2158 // Search the PossibleValue for the value.
2159 CV_PossibleValue_t * pv;
2160 for( pv = cvar->PossibleValue; pv->strvalue; pv++)
2161 {
2162 if( pv->value == tval )
2163 {
2164 tstr = pv->strvalue;
2165 goto show_by_str;
2166 }
2167 }
2168 }
2169
2170 // show by value
2171 CONS_Printf ("\"%s\" is \"%i\" config \"%s\" default is \"%s\"\n",
2172 cvar->name, tval, cvar->string, cvar->defaultvalue);
2173 return true;
2174
2175 show_by_str:
2176 CONS_Printf ("\"%s\" is \"%s\" config \"%s\" default is \"%s\"\n",
2177 cvar->name, tstr, cvar->string, cvar->defaultvalue);
2178 return true;
2179
2180 std_show_str:
2181 CONS_Printf ("\"%s\" is \"%s\" default is \"%s\"\n",
2182 cvar->name, cvar->string, cvar->defaultvalue);
2183 return true;
2184 }
2185
2186
2187
2188 // Support for saving the console variables that have the CV_SAVE flag set.
2189 // This has less splitting of the logic between three functions,
2190 // and does not require passing a FILE ptr around.
CV_IteratorFirst(void)2191 consvar_t * CV_IteratorFirst( void )
2192 {
2193 return consvar_vars;
2194 }
2195
CV_Iterator(consvar_t * cv)2196 consvar_t * CV_Iterator( consvar_t * cv )
2197 {
2198 return cv->next;
2199 }
2200
2201
2202
2203 //============================================================================
2204 // SCRIPT PARSE
2205 //============================================================================
2206
2207 // Parse a token out of a string, handles script files too
2208 // returns the data pointer after the token
2209 // Do not mangle filenames, set script only where strings might have '\' escapes.
COM_Parse(const char * data,boolean script)2210 static const char * COM_Parse (const char * data, boolean script)
2211 {
2212 int c;
2213 int len = 0;
2214 com_token[0] = '\0';
2215
2216 if (!data)
2217 return NULL;
2218
2219 // skip whitespace
2220 skipwhite:
2221 while ( (c = *data) <= ' ')
2222 {
2223 if (!c)
2224 return NULL; // end of file;
2225 data++;
2226 }
2227
2228 // skip // comments
2229 // Also may be Linux filename: //home/user/.legacy
2230 if ( script && (c == '/' && data[1] == '/'))
2231 {
2232 while (*data && *data != '\n')
2233 data++;
2234 goto skipwhite;
2235 }
2236
2237
2238 // handle quoted strings specially
2239 if (c == '"')
2240 {
2241 data++;
2242 while ( len < COM_TOKEN_MAX-1 )
2243 {
2244 c = *data++;
2245 if (!c)
2246 {
2247 // NUL in the middle of a quoted string. Missing closing quote?
2248 CONS_Printf("Error: Quoted string ended prematurely.\n");
2249 goto term_done;
2250 }
2251
2252 if (c == '"') // closing quote
2253 goto term_done;
2254
2255 if ( script && (c == '\\')) // c-like escape sequence
2256 {
2257 switch (*data)
2258 {
2259 case '\\': // backslash
2260 com_token[len++] = '\\'; break;
2261
2262 case '"': // double quote
2263 com_token[len++] = '"'; break;
2264
2265 case 't': // tab
2266 com_token[len++] = '\t'; break;
2267
2268 case 'n': // newline
2269 com_token[len++] = '\n'; break;
2270
2271 default:
2272 CONS_Printf("Error: Unknown escape sequence '\\%c'\n", *data);
2273 break;
2274 }
2275
2276 data++;
2277 continue;
2278 }
2279
2280 // normal char
2281 com_token[len++] = c;
2282 }
2283 }
2284
2285 // parse single characters
2286 // Also ':' can appear in WIN path names
2287 if (script && (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':'))
2288 {
2289 if( len >= COM_TOKEN_MAX-2 ) goto term_done;
2290 com_token[len++] = c;
2291 data++;
2292 goto term_done;
2293 }
2294
2295 // parse a regular word
2296 do
2297 {
2298 if( len >= COM_TOKEN_MAX-2 ) goto term_done;
2299 com_token[len++] = c;
2300 data++;
2301 c = *data;
2302 if (script && (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':'))
2303 break;
2304 } while (c > ' ');
2305
2306 term_done:
2307 com_token[len] = '\0';
2308 return data;
2309 }
2310