1 // SONIC ROBO BLAST 2
2 //-----------------------------------------------------------------------------
3 // Copyright (C) 1998-2000 by DooM Legacy Team.
4 // Copyright (C) 1999-2020 by Sonic Team Junior.
5 //
6 // This program is free software distributed under the
7 // terms of the GNU General Public License, version 2.
8 // See the 'LICENSE' file for more details.
9 //-----------------------------------------------------------------------------
10 /// \file  command.c
11 /// \brief Parse and execute commands from console input/scripts and remote server
12 ///
13 ///        Handles console variables, which is a simplified version
14 ///        of commands, each consvar can have a function called when
15 ///        it is modified.. thus it acts nearly as commands.
16 ///
17 ///        code shamelessly inspired by the QuakeC sources, thanks Id :)
18 
19 #include "doomdef.h"
20 #include "doomstat.h"
21 #include "command.h"
22 #include "console.h"
23 #include "z_zone.h"
24 #include "m_menu.h"
25 #include "m_misc.h"
26 #include "m_fixed.h"
27 #include "m_argv.h"
28 #include "byteptr.h"
29 #include "p_saveg.h"
30 #include "g_game.h" // for player_names
31 #include "d_netcmd.h"
32 #include "hu_stuff.h"
33 #include "p_setup.h"
34 #include "lua_script.h"
35 #include "d_netfil.h" // findfile
36 #include "r_data.h" // Color_cons_t
37 
38 //========
39 // protos.
40 //========
41 static boolean COM_Exists(const char *com_name);
42 static void COM_ExecuteString(char *com_text);
43 
44 static void COM_Alias_f(void);
45 static void COM_Echo_f(void);
46 static void COM_CEcho_f(void);
47 static void COM_CEchoFlags_f(void);
48 static void COM_CEchoDuration_f(void);
49 static void COM_Exec_f(void);
50 static void COM_Wait_f(void);
51 static void COM_Help_f(void);
52 static void COM_Toggle_f(void);
53 static void COM_Add_f(void);
54 
55 static void CV_EnforceExecVersion(void);
56 static boolean CV_FilterVarByVersion(consvar_t *v, const char *valstr);
57 static boolean CV_Command(void);
58 consvar_t *CV_FindVar(const char *name);
59 static const char *CV_StringValue(const char *var_name);
60 
61 static consvar_t *consvar_vars; // list of registered console variables
62 static UINT16     consvar_number_of_netids = 0;
63 
64 #ifdef OLD22DEMOCOMPAT
65 static old_demo_var_t *consvar_old_demo_vars;
66 #endif
67 
68 static char com_token[1024];
69 static char *COM_Parse(char *data);
70 
71 static char * COM_Purge (char *text, int *lenp);
72 
73 CV_PossibleValue_t CV_OnOff[] = {{0, "Off"}, {1, "On"}, {0, NULL}};
74 CV_PossibleValue_t CV_YesNo[] = {{0, "No"}, {1, "Yes"}, {0, NULL}};
75 CV_PossibleValue_t CV_Unsigned[] = {{0, "MIN"}, {999999999, "MAX"}, {0, NULL}};
76 CV_PossibleValue_t CV_Natural[] = {{1, "MIN"}, {999999999, "MAX"}, {0, NULL}};
77 
78 // Filter consvars by EXECVERSION
79 // First implementation is 26 (2.1.21), so earlier configs default at 25 (2.1.20)
80 // Also set CV_HIDEN during runtime, after config is loaded
81 static boolean execversion_enabled = false;
82 consvar_t cv_execversion = CVAR_INIT ("execversion","25",CV_CALL,CV_Unsigned, CV_EnforceExecVersion);
83 
84 // for default joyaxis detection
85 static boolean joyaxis_default = false;
86 static boolean joyaxis2_default = false;
87 static INT32 joyaxis_count = 0;
88 static INT32 joyaxis2_count = 0;
89 
90 #define COM_BUF_SIZE (32<<10) // command buffer size
91 #define MAX_ALIAS_RECURSION 100 // max recursion allowed for aliases
92 
93 static INT32 com_wait; // one command per frame (for cmd sequences)
94 
95 // command aliases
96 //
97 typedef struct cmdalias_s
98 {
99 	struct cmdalias_s *next;
100 	char *name;
101 	char *value; // the command string to replace the alias
102 } cmdalias_t;
103 
104 static cmdalias_t *com_alias; // aliases list
105 
106 // =========================================================================
107 //                            COMMAND BUFFER
108 // =========================================================================
109 
110 static vsbuf_t com_text; // variable sized buffer
111 
112 /** Purges control characters out of some text.
113   *
114   * \param s The text.
115   * \param np Optionally a pointer to fill with the new string length.
116   * \return The text.
117   * \sa COM_ExecuteString
118   */
119 static char *
COM_Purge(char * s,int * np)120 COM_Purge (char *s, int *np)
121 {
122 	char *t;
123 	char *p;
124 	int n;
125 	n = strlen(s);
126 	t = s + n + 1;
127 	p = s;
128 	while (( p = strchr(p, '\033') ))
129 	{
130 		memmove(p, &p[1], t - p - 1);
131 		n--;
132 	}
133 	if (np)
134 		(*np) = n;
135 	return s;
136 }
137 
138 /** Adds text into the command buffer for later execution.
139   *
140   * \param ptext The text to add.
141   * \sa COM_BufInsertTextEx
142   */
COM_BufAddTextEx(const char * ptext,int flags)143 void COM_BufAddTextEx(const char *ptext, int flags)
144 {
145 	int l;
146 	char *text;
147 
148 	text = COM_Purge(Z_StrDup(ptext), &l);
149 
150 	if (com_text.cursize + 2 + l >= com_text.maxsize)
151 	{
152 		CONS_Alert(CONS_WARNING, M_GetText("Command buffer full!\n"));
153 		return;
154 	}
155 
156 	VS_WriteEx(&com_text, text, l, flags);
157 
158 	Z_Free(text);
159 }
160 
161 /** Adds command text and executes it immediately.
162   *
163   * \param ptext The text to execute. A newline is automatically added.
164   * \sa COM_BufAddTextEx
165   */
COM_BufInsertTextEx(const char * ptext,int flags)166 void COM_BufInsertTextEx(const char *ptext, int flags)
167 {
168 	const INT32 old_wait = com_wait;
169 
170 	char *temp = NULL;
171 	size_t templen;
172 
173 	// copy off any commands still remaining in the exec buffer
174 	templen = com_text.cursize;
175 	if (templen)
176 	{
177 		temp = M_Memcpy(ZZ_Alloc(templen), com_text.data, templen);
178 		VS_Clear(&com_text);
179 	}
180 
181 	com_wait = 0;
182 
183 	// add the entire text of the file (or alias)
184 	COM_BufAddTextEx(ptext, flags);
185 	COM_BufExecute(); // do it right away
186 
187 	com_wait += old_wait;
188 
189 	// add the copied off data
190 	if (templen)
191 	{
192 		VS_Write(&com_text, temp, templen);
193 		Z_Free(temp);
194 	}
195 }
196 
197 /** Progress the wait timer and flush waiting console commands when ready.
198   */
199 void
COM_BufTicker(void)200 COM_BufTicker(void)
201 {
202 	if (com_wait)
203 	{
204 		com_wait--;
205 		return;
206 	}
207 
208 	COM_BufExecute();
209 }
210 
211 /** Flushes (executes) console commands in the buffer.
212   */
COM_BufExecute(void)213 void COM_BufExecute(void)
214 {
215 	size_t i;
216 	char *ptext;
217 	char line[1024] = "";
218 	INT32 quotes;
219 
220 	while (com_text.cursize)
221 	{
222 		// find a '\n' or; line break
223 		ptext = (char *)com_text.data;
224 
225 		quotes = 0;
226 		for (i = 0; i < com_text.cursize; i++)
227 		{
228 			if (ptext[i] == '\"' && !quotes && i > 0 && ptext[i-1] != ' ') // Malformed command
229 				break;
230 			if (ptext[i] == '\"')
231 				quotes++;
232 			if (!(quotes & 1) && ptext[i] == ';')
233 				break; // don't break if inside a quoted string
234 			if (ptext[i] == '\n' || ptext[i] == '\r')
235 				break;
236 		}
237 
238 		M_Memcpy(line, ptext, i);
239 		line[i] = 0;
240 
241 		// flush the command text from the command buffer, _BEFORE_
242 		// executing, to avoid that 'recursive' aliases overflow the
243 		// command text buffer, in that case, new commands are inserted
244 		// at the beginning, in place of the actual, so it doesn't
245 		// overflow
246 		if (i == com_text.cursize)
247 			// the last command was just flushed
248 			com_text.cursize = 0;
249 		else
250 		{
251 			i++;
252 			com_text.cursize -= i;
253 			//memcpy(ptext, ptext+i, com_text.cursize); // Use memmove if the memory areas do overlap.
254 			memmove(ptext, ptext+i, com_text.cursize);
255 		}
256 
257 		// execute the command line
258 		COM_ExecuteString(line);
259 
260 		// delay following commands if a wait was encountered
261 		if (com_wait)
262 		{
263 			com_wait--;
264 			break;
265 		}
266 	}
267 }
268 
269 /** Executes a string immediately.  Used for skirting around WAIT commands.
270   */
COM_ImmedExecute(const char * ptext)271 void COM_ImmedExecute(const char *ptext)
272 {
273 	size_t i = 0, j = 0;
274 	char line[1024] = "";
275 	INT32 quotes;
276 
277 	while (i < strlen(ptext))
278 	{
279 
280 		quotes = 0;
281 		for (j = 0; i < strlen(ptext); i++,j++)
282 		{
283 			if (ptext[i] == '\"' && !quotes && i > 0 && ptext[i-1] != ' ') // Malformed command
284 				return;
285 			if (ptext[i] == '\"')
286 				quotes++;
287 			// don't break if inside a quoted string
288 			if ((!(quotes & 1) && ptext[i] == ';') || ptext[i] == '\n' || ptext[i] == '\r')
289 				break;
290 		}
291 
292 		memcpy(line, ptext+(i-j), j);
293 		line[j] = 0;
294 
295 		// execute the command line
296 		COM_ExecuteString(line);
297 
298 		i++; // move to next character
299 	}
300 }
301 
302 // =========================================================================
303 //                            COMMAND EXECUTION
304 // =========================================================================
305 
306 typedef struct xcommand_s
307 {
308 	const char *name;
309 	struct xcommand_s *next;
310 	com_func_t function;
311 } xcommand_t;
312 
313 static xcommand_t *com_commands = NULL; // current commands
314 
315 #define MAX_ARGS 80
316 static size_t com_argc;
317 static char *com_argv[MAX_ARGS];
318 static const char *com_null_string = "";
319 static char *com_args = NULL; // current command args or NULL
320 static int com_flags;
321 
322 static void Got_NetVar(UINT8 **p, INT32 playernum);
323 
324 /** Initializes command buffer and adds basic commands.
325   */
COM_Init(void)326 void COM_Init(void)
327 {
328 	// allocate command buffer
329 	VS_Alloc(&com_text, COM_BUF_SIZE);
330 
331 	// add standard commands
332 	COM_AddCommand("alias", COM_Alias_f);
333 	COM_AddCommand("echo", COM_Echo_f);
334 	COM_AddCommand("cecho", COM_CEcho_f);
335 	COM_AddCommand("cechoflags", COM_CEchoFlags_f);
336 	COM_AddCommand("cechoduration", COM_CEchoDuration_f);
337 	COM_AddCommand("exec", COM_Exec_f);
338 	COM_AddCommand("wait", COM_Wait_f);
339 	COM_AddCommand("help", COM_Help_f);
340 	COM_AddCommand("toggle", COM_Toggle_f);
341 	COM_AddCommand("add", COM_Add_f);
342 	RegisterNetXCmd(XD_NETVAR, Got_NetVar);
343 }
344 
345 /** Gets a console command argument count.
346   *
347   * \return Number of arguments for the last command.
348   * \sa COM_Argv
349   */
COM_Argc(void)350 size_t COM_Argc(void)
351 {
352 	return com_argc;
353 }
354 
355 /** Gets a console command argument.
356   *
357   * \param arg Index of the argument (0 to COM_Argc() - 1).
358   * \return String pointer to the indicated argument.
359   * \sa COM_Argc, COM_Args
360   */
COM_Argv(size_t arg)361 const char *COM_Argv(size_t arg)
362 {
363 	if (arg >= com_argc || (signed)arg < 0)
364 		return com_null_string;
365 	return com_argv[arg];
366 }
367 
368 /** Gets all console command arguments.
369   *
370   * \return String pointer to all arguments for the last command.
371   * \sa COM_Argv
372   */
COM_Args(void)373 char *COM_Args(void)
374 {
375 	return com_args;
376 }
377 
378 /** Checks if a parameter was passed to a console command.
379   *
380   * \param check The parameter to look for, e.g. "-noerror".
381   * \return The index of the argument containing the parameter,
382   *         or 0 if the parameter was not found.
383   */
COM_CheckParm(const char * check)384 size_t COM_CheckParm(const char *check)
385 {
386 	size_t i;
387 
388 	for (i = 1; i < com_argc; i++)
389 		if (!strcasecmp(check, com_argv[i]))
390 			return i;
391 	return 0;
392 }
393 
394 /** \brief COM_CheckParm, but checks only the start of each argument.
395   *        E.g. checking for "-no" would match "-noerror" too.
396   */
COM_CheckPartialParm(const char * check)397 size_t COM_CheckPartialParm(const char *check)
398 {
399 	int  len;
400 	size_t i;
401 
402 	len = strlen(check);
403 
404 	for (i = 1; i < com_argc; i++)
405 	{
406 		if (strncasecmp(check, com_argv[i], len) == 0)
407 			return i;
408 	}
409 	return 0;
410 }
411 
412 /** Find the first argument that starts with a hyphen (-).
413   * \return The index of the argument, or 0
414   *         if there are no such arguments.
415   */
COM_FirstOption(void)416 size_t COM_FirstOption(void)
417 {
418 	size_t i;
419 
420 	for (i = 1; i < com_argc; i++)
421 	{
422 		if (com_argv[i][0] == '-')/* options start with a hyphen */
423 			return i;
424 	}
425 	return 0;
426 }
427 
428 /** Parses a string into command-line tokens.
429   *
430   * \param ptext A null-terminated string. Does not need to be
431   *             newline-terminated.
432   */
COM_TokenizeString(char * ptext)433 static void COM_TokenizeString(char *ptext)
434 {
435 	size_t i;
436 
437 	// Clear the args from the last string.
438 	for (i = 0; i < com_argc; i++)
439 		Z_Free(com_argv[i]);
440 
441 	com_argc = 0;
442 	com_args = NULL;
443 	com_flags = 0;
444 
445 	while (com_argc < MAX_ARGS)
446 	{
447 		// Skip whitespace up to a newline.
448 		while (*ptext != '\0' && *ptext <= ' ' && *ptext != '\n')
449 		{
450 			if (ptext[0] == '\033')
451 			{
452 				com_flags = (unsigned)ptext[1];
453 				ptext += 2;
454 			}
455 			else
456 				ptext++;
457 		}
458 
459 		// A newline means end of command in buffer,
460 		// thus end of this command's args too.
461 		if (*ptext == '\n' || *ptext == '\0')
462 			break;
463 
464 		if (com_argc == 1)
465 			com_args = ptext;
466 
467 		ptext = COM_Parse(ptext);
468 		if (ptext == NULL)
469 			break;
470 
471 		com_argv[com_argc] = Z_StrDup(com_token);
472 		com_argc++;
473 	}
474 }
475 
476 /** Adds a console command.
477   *
478   * \param name Name of the command.
479   * \param func Function called when the command is run.
480   */
COM_AddCommand(const char * name,com_func_t func)481 void COM_AddCommand(const char *name, com_func_t func)
482 {
483 	xcommand_t *cmd;
484 
485 	// fail if the command is a variable name
486 	if (CV_StringValue(name)[0] != '\0')
487 	{
488 		I_Error("%s is a variable name\n", name);
489 		return;
490 	}
491 
492 	// fail if the command already exists
493 	for (cmd = com_commands; cmd; cmd = cmd->next)
494 	{
495 		if (!stricmp(name, cmd->name)) //case insensitive now that we have lower and uppercase!
496 		{
497 			// don't I_Error for Lua commands
498 			// Lua commands can replace game commands, and they have priority.
499 			// BUT, if for some reason we screwed up and made two console commands with the same name,
500 			// it's good to have this here so we find out.
501 			if (cmd->function != COM_Lua_f)
502 				I_Error("Command %s already exists\n", name);
503 
504 			return;
505 		}
506 	}
507 
508 	cmd = ZZ_Alloc(sizeof *cmd);
509 	cmd->name = name;
510 	cmd->function = func;
511 	cmd->next = com_commands;
512 	com_commands = cmd;
513 }
514 
515 /** Adds a console command for Lua.
516   * No I_Errors allowed; return a negative code instead.
517   *
518   * \param name Name of the command.
519   */
COM_AddLuaCommand(const char * name)520 int COM_AddLuaCommand(const char *name)
521 {
522 	xcommand_t *cmd;
523 
524 	// fail if the command is a variable name
525 	if (CV_StringValue(name)[0] != '\0')
526 		return -1;
527 
528 	// command already exists
529 	for (cmd = com_commands; cmd; cmd = cmd->next)
530 	{
531 		if (!stricmp(name, cmd->name)) //case insensitive now that we have lower and uppercase!
532 		{
533 			// replace the built in command.
534 			cmd->function = COM_Lua_f;
535 			return 1;
536 		}
537 	}
538 
539 	// Add a new command.
540 	cmd = ZZ_Alloc(sizeof *cmd);
541 	cmd->name = name;
542 	cmd->function = COM_Lua_f;
543 	cmd->next = com_commands;
544 	com_commands = cmd;
545 	return 0;
546 }
547 
548 /** Tests if a command exists.
549   *
550   * \param com_name Name to test for.
551   * \return True if a command by the given name exists.
552   */
COM_Exists(const char * com_name)553 static boolean COM_Exists(const char *com_name)
554 {
555 	xcommand_t *cmd;
556 
557 	for (cmd = com_commands; cmd; cmd = cmd->next)
558 		if (!stricmp(com_name, cmd->name))
559 			return true;
560 
561 	return false;
562 }
563 
564 /** Does command completion for the console.
565   *
566   * \param partial The partial name of the command (potentially).
567   * \param skips   Number of commands to skip.
568   * \return The complete command name, or NULL.
569   * \sa CV_CompleteAlias, CV_CompleteVar
570   */
COM_CompleteCommand(const char * partial,INT32 skips)571 const char *COM_CompleteCommand(const char *partial, INT32 skips)
572 {
573 	xcommand_t *cmd;
574 	size_t len;
575 
576 	len = strlen(partial);
577 
578 	if (!len)
579 		return NULL;
580 
581 	// check functions
582 	for (cmd = com_commands; cmd; cmd = cmd->next)
583 		if (!strncmp(partial, cmd->name, len))
584 			if (!skips--)
585 				return cmd->name;
586 
587 	return NULL;
588 }
589 
590 /** Completes the name of an alias.
591   *
592   * \param partial The partial name of the alias (potentially).
593   * \param skips   Number of aliases to skip.
594   * \return The complete alias name, or NULL.
595   * \sa CV_CompleteCommand, CV_CompleteVar
596   */
COM_CompleteAlias(const char * partial,INT32 skips)597 const char *COM_CompleteAlias(const char *partial, INT32 skips)
598 {
599 	cmdalias_t *a;
600 	size_t len;
601 
602 	len = strlen(partial);
603 
604 	if (!len)
605 		return NULL;
606 
607 	// check functions
608 	for (a = com_alias; a; a = a->next)
609 		if (!strncmp(partial, a->name, len))
610 			if (!skips--)
611 				return a->name;
612 
613 	return NULL;
614 }
615 
616 /** Parses a single line of text into arguments and tries to execute it.
617   * The text can come from the command buffer, a remote client, or stdin.
618   *
619   * \param ptext A single line of text.
620   */
COM_ExecuteString(char * ptext)621 static void COM_ExecuteString(char *ptext)
622 {
623 	xcommand_t *cmd;
624 	cmdalias_t *a;
625 	static INT32 recursion = 0; // detects recursion and stops it if it goes too far
626 
627 	COM_TokenizeString(ptext);
628 
629 	// execute the command line
630 	if (COM_Argc() == 0)
631 		return; // no tokens
632 
633 	// check functions
634 	for (cmd = com_commands; cmd; cmd = cmd->next)
635 	{
636 		if (!stricmp(com_argv[0], cmd->name)) //case insensitive now that we have lower and uppercase!
637 		{
638 			cmd->function();
639 			return;
640 		}
641 	}
642 
643 	// check aliases
644 	for (a = com_alias; a; a = a->next)
645 	{
646 		if (!stricmp(com_argv[0], a->name))
647 		{
648 			if (recursion > MAX_ALIAS_RECURSION)
649 				CONS_Alert(CONS_WARNING, M_GetText("Alias recursion cycle detected!\n"));
650 			else
651 			{ // Monster Iestyn: keep track of how many levels of recursion we're in
652 				recursion++;
653 				COM_BufInsertText(a->value);
654 				recursion--;
655 			}
656 			return;
657 		}
658 	}
659 
660 	// check cvars
661 	// Hurdler: added at Ebola's request ;)
662 	// (don't flood the console in software mode with bad gl_xxx command)
663 	if (!CV_Command() && con_destlines)
664 		CONS_Printf(M_GetText("Unknown command '%s'\n"), COM_Argv(0));
665 }
666 
667 // =========================================================================
668 //                            SCRIPT COMMANDS
669 // =========================================================================
670 
671 /** Creates a command name that replaces another command.
672   */
COM_Alias_f(void)673 static void COM_Alias_f(void)
674 {
675 	cmdalias_t *a;
676 	char cmd[1024];
677 	size_t i, c;
678 
679 	if (COM_Argc() < 3)
680 	{
681 		CONS_Printf(M_GetText("alias <name> <command>: create a shortcut command that executes other command(s)\n"));
682 		return;
683 	}
684 
685 	a = ZZ_Alloc(sizeof *a);
686 	a->next = com_alias;
687 	com_alias = a;
688 
689 	a->name = Z_StrDup(COM_Argv(1));
690 
691 	// copy the rest of the command line
692 	cmd[0] = 0; // start out with a null string
693 	c = COM_Argc();
694 	for (i = 2; i < c; i++)
695 	{
696 		strcat(cmd, COM_Argv(i));
697 		if (i != c)
698 			strcat(cmd, " ");
699 	}
700 	strcat(cmd, "\n");
701 
702 	a->value = Z_StrDup(cmd);
703 }
704 
705 /** Prints a line of text to the console.
706   */
COM_Echo_f(void)707 static void COM_Echo_f(void)
708 {
709 	size_t i;
710 
711 	for (i = 1; i < COM_Argc(); i++)
712 		CONS_Printf("%s ", COM_Argv(i));
713 	CONS_Printf("\n");
714 }
715 
716 /** Displays text on the center of the screen for a short time.
717   */
COM_CEcho_f(void)718 static void COM_CEcho_f(void)
719 {
720 	size_t i;
721 	char cechotext[1024] = "";
722 
723 	for (i = 1; i < COM_Argc(); i++)
724 	{
725 		strncat(cechotext, COM_Argv(i), sizeof(cechotext)-1);
726 		strncat(cechotext, " ", sizeof(cechotext)-1);
727 	}
728 
729 	cechotext[sizeof(cechotext) - 1] = '\0';
730 
731 	HU_DoCEcho(cechotext);
732 }
733 
734 /** Sets drawing flags for the CECHO command.
735   */
COM_CEchoFlags_f(void)736 static void COM_CEchoFlags_f(void)
737 {
738 	if (COM_Argc() > 1)
739 	{
740 		const char *arg = COM_Argv(1);
741 
742 		if (arg[0] && arg[0] == '0' &&
743 			arg[1] && arg[1] == 'x') // Use hexadecimal!
744 			HU_SetCEchoFlags(axtoi(arg+2));
745 		else
746 			HU_SetCEchoFlags(atoi(arg));
747 	}
748 	else
749 		CONS_Printf(M_GetText("cechoflags <flags>: set CEcho flags, prepend with 0x to use hexadecimal\n"));
750 }
751 
752 /** Sets the duration for CECHO commands to stay on the screen
753   */
COM_CEchoDuration_f(void)754 static void COM_CEchoDuration_f(void)
755 {
756 	if (COM_Argc() > 1)
757 		HU_SetCEchoDuration(atoi(COM_Argv(1)));
758 }
759 
760 /** Executes a script file.
761   */
COM_Exec_f(void)762 static void COM_Exec_f(void)
763 {
764 	UINT8 *buf = NULL;
765 	char filename[256];
766 
767 	if (COM_Argc() < 2 || COM_Argc() > 3)
768 	{
769 		CONS_Printf(M_GetText("exec <filename>: run a script file\n"));
770 		return;
771 	}
772 
773 	// load file
774 	// Try with Argv passed verbatim first, for back compat
775 	FIL_ReadFile(COM_Argv(1), &buf);
776 
777 	if (!buf)
778 	{
779 		// Now try by searching the file path
780 		// filename is modified with the full found path
781 		strcpy(filename, COM_Argv(1));
782 		if (findfile(filename, NULL, true) != FS_NOTFOUND)
783 			FIL_ReadFile(filename, &buf);
784 
785 		if (!buf)
786 		{
787 			if (!COM_CheckParm("-noerror"))
788 				CONS_Printf(M_GetText("couldn't execute file %s\n"), COM_Argv(1));
789 			return;
790 		}
791 	}
792 
793 	if (!COM_CheckParm("-silent"))
794 		CONS_Printf(M_GetText("executing %s\n"), COM_Argv(1));
795 
796 	// insert text file into the command buffer
797 	COM_BufAddText((char *)buf);
798 	COM_BufAddText("\n");
799 
800 	// free buffer
801 	Z_Free(buf);
802 }
803 
804 /** Delays execution of the rest of the commands until the next frame.
805   * Allows sequences of commands like "jump; fire; backward".
806   */
COM_Wait_f(void)807 static void COM_Wait_f(void)
808 {
809 	if (COM_Argc() > 1)
810 		com_wait = atoi(COM_Argv(1));
811 	else
812 		com_wait = 1; // 1 frame
813 }
814 
815 /** Prints help on variables and commands.
816   */
COM_Help_f(void)817 static void COM_Help_f(void)
818 {
819 	xcommand_t *cmd;
820 	consvar_t *cvar;
821 	INT32 i = 0;
822 
823 	if (COM_Argc() > 1)
824 	{
825 		const char *help = COM_Argv(1);
826 		cvar = CV_FindVar(help);
827 		if (cvar)
828 		{
829 			boolean floatmode = false;
830 			const char *cvalue = NULL;
831 			CONS_Printf("\x82""Variable %s:\n", cvar->name);
832 			CONS_Printf(M_GetText("  flags :"));
833 			if (cvar->flags & CV_SAVE)
834 				CONS_Printf("AUTOSAVE ");
835 			if (cvar->flags & CV_FLOAT)
836 			{
837 				CONS_Printf("FLOAT ");
838 				floatmode = true;
839 			}
840 			if (cvar->flags & CV_NETVAR)
841 				CONS_Printf("NETVAR ");
842 			if (cvar->flags & CV_CALL)
843 				CONS_Printf("ACTION ");
844 			if (cvar->flags & CV_CHEAT)
845 				CONS_Printf("CHEAT ");
846 			CONS_Printf("\n");
847 			if (cvar->PossibleValue)
848 			{
849 				CONS_Printf(" Possible values:\n");
850 				if (cvar->PossibleValue == CV_YesNo)
851 					CONS_Printf("  Yes or No (On or Off, 1 or 0)\n");
852 				else if (cvar->PossibleValue == CV_OnOff)
853 					CONS_Printf("  On or Off (Yes or No, 1 or 0)\n");
854 				else if (cvar->PossibleValue == Color_cons_t)
855 				{
856 					for (i = 1; i < numskincolors; ++i)
857 					{
858 						if (skincolors[i].accessible)
859 						{
860 							CONS_Printf("  %-2d : %s\n", i, skincolors[i].name);
861 							if (i == cvar->value)
862 								cvalue = skincolors[i].name;
863 						}
864 					}
865 				}
866 				else
867 				{
868 #define MINVAL 0
869 #define MAXVAL 1
870 					if (!stricmp(cvar->PossibleValue[MINVAL].strvalue, "MIN"))
871 					{
872 						if (floatmode)
873 						{
874 							float fu = FIXED_TO_FLOAT(cvar->PossibleValue[MINVAL].value);
875 							float ck = FIXED_TO_FLOAT(cvar->PossibleValue[MAXVAL].value);
876 							CONS_Printf("  range from %ld%s to %ld%s\n",
877 									(long)fu, M_Ftrim(fu),
878 									(long)ck, M_Ftrim(ck));
879 						}
880 						else
881 							CONS_Printf("  range from %d to %d\n", cvar->PossibleValue[MINVAL].value,
882 								cvar->PossibleValue[MAXVAL].value);
883 						i = MAXVAL+1;
884 					}
885 #undef MINVAL
886 #undef MAXVAL
887 
888 					//CONS_Printf(M_GetText("  possible value : %s\n"), cvar->name);
889 					while (cvar->PossibleValue[i].strvalue)
890 					{
891 						if (floatmode)
892 							CONS_Printf("  %-2f : %s\n", FIXED_TO_FLOAT(cvar->PossibleValue[i].value),
893 								cvar->PossibleValue[i].strvalue);
894 						else
895 							CONS_Printf("  %-2d : %s\n", cvar->PossibleValue[i].value,
896 								cvar->PossibleValue[i].strvalue);
897 						if (cvar->PossibleValue[i].value == cvar->value)
898 							cvalue = cvar->PossibleValue[i].strvalue;
899 						i++;
900 					}
901 				}
902 			}
903 
904 			if (cvalue)
905 				CONS_Printf(" Current value: %s\n", cvalue);
906 			else if (cvar->string)
907 				CONS_Printf(" Current value: %s\n", cvar->string);
908 			else
909 				CONS_Printf(" Current value: %d\n", cvar->value);
910 
911 			if (cvar->revert.v.string != NULL && strcmp(cvar->revert.v.string, cvar->string) != 0)
912 				CONS_Printf(" Value before netgame: %s\n", cvar->revert.v.string);
913 		}
914 		else
915 		{
916 			for (cmd = com_commands; cmd; cmd = cmd->next)
917 			{
918 				if (strcmp(cmd->name, help))
919 					continue;
920 
921 				CONS_Printf("\x82""Command %s:\n", cmd->name);
922 				CONS_Printf("  help is not available for commands");
923 				CONS_Printf("\x82""\nCheck wiki.srb2.org for more or try typing <name> without arguments\n");
924 				return;
925 			}
926 
927 			CONS_Printf("No exact match, searching...\n");
928 
929 			// variables
930 			CONS_Printf("\x82""Variables:\n");
931 			for (cvar = consvar_vars; cvar; cvar = cvar->next)
932 			{
933 				if ((cvar->flags & CV_NOSHOWHELP) || (!strstr(cvar->name, help)))
934 					continue;
935 				CONS_Printf("%s ", cvar->name);
936 				i++;
937 			}
938 
939 			// commands
940 			CONS_Printf("\x82""\nCommands:\n");
941 			for (cmd = com_commands; cmd; cmd = cmd->next)
942 			{
943 				if (!strstr(cmd->name, help))
944 					continue;
945 				CONS_Printf("%s ",cmd->name);
946 				i++;
947 			}
948 
949 			CONS_Printf("\x82""\nCheck wiki.srb2.org for more or type help <command or variable>\n");
950 
951 			CONS_Debug(DBG_GAMELOGIC, "\x87Total : %d\n", i);
952 		}
953 		return;
954 	}
955 	else
956 	{
957 		// variables
958 		CONS_Printf("\x82""Variables:\n");
959 		for (cvar = consvar_vars; cvar; cvar = cvar->next)
960 		{
961 			if (cvar->flags & CV_NOSHOWHELP)
962 				continue;
963 			CONS_Printf("%s ", cvar->name);
964 			i++;
965 		}
966 
967 		// commands
968 		CONS_Printf("\x82""\nCommands:\n");
969 		for (cmd = com_commands; cmd; cmd = cmd->next)
970 		{
971 			CONS_Printf("%s ",cmd->name);
972 			i++;
973 		}
974 
975 		CONS_Printf("\x82""\nCheck wiki.srb2.org for more or type help <command or variable>\n");
976 
977 		CONS_Debug(DBG_GAMELOGIC, "\x82Total : %d\n", i);
978 	}
979 }
980 
981 /** Toggles a console variable. Useful for on/off values.
982   *
983   * This works on on/off, yes/no values only
984   */
COM_Toggle_f(void)985 static void COM_Toggle_f(void)
986 {
987 	consvar_t *cvar;
988 
989 	if (COM_Argc() != 2)
990 	{
991 		CONS_Printf(M_GetText("Toggle <cvar_name>: Toggle the value of a cvar\n"));
992 		return;
993 	}
994 	cvar = CV_FindVar(COM_Argv(1));
995 	if (!cvar)
996 	{
997 		CONS_Alert(CONS_NOTICE, M_GetText("%s is not a cvar\n"), COM_Argv(1));
998 		return;
999 	}
1000 
1001 	if (!(cvar->PossibleValue == CV_YesNo || cvar->PossibleValue == CV_OnOff))
1002 	{
1003 		CONS_Alert(CONS_NOTICE, M_GetText("%s is not a boolean value\n"), COM_Argv(1));
1004 		return;
1005 	}
1006 
1007 	// netcvar don't change imediately
1008 	cvar->flags |= CV_SHOWMODIFONETIME;
1009 	CV_AddValue(cvar, +1);
1010 }
1011 
1012 /** Command variant of CV_AddValue
1013   */
COM_Add_f(void)1014 static void COM_Add_f(void)
1015 {
1016 	consvar_t *cvar;
1017 
1018 	if (COM_Argc() != 3)
1019 	{
1020 		CONS_Printf(M_GetText("Add <cvar_name> <value>: Add to the value of a cvar. Negative values work too!\n"));
1021 		return;
1022 	}
1023 	cvar = CV_FindVar(COM_Argv(1));
1024 	if (!cvar)
1025 	{
1026 		CONS_Alert(CONS_NOTICE, M_GetText("%s is not a cvar\n"), COM_Argv(1));
1027 		return;
1028 	}
1029 
1030 	if (( cvar->flags & CV_FLOAT ))
1031 	{
1032 		float n =FIXED_TO_FLOAT (cvar->value) + atof(COM_Argv(2));
1033 		CV_Set(cvar, va("%ld%s", (long)n, M_Ftrim(n)));
1034 	}
1035 	else
1036 		CV_AddValue(cvar, atoi(COM_Argv(2)));
1037 }
1038 
1039 // =========================================================================
1040 //                      VARIABLE SIZE BUFFERS
1041 // =========================================================================
1042 
1043 /** Initializes a variable size buffer.
1044   *
1045   * \param buf      Buffer to initialize.
1046   * \param initsize Initial size for the buffer.
1047   */
VS_Alloc(vsbuf_t * buf,size_t initsize)1048 void VS_Alloc(vsbuf_t *buf, size_t initsize)
1049 {
1050 #define VSBUFMINSIZE 256
1051 	if (initsize < VSBUFMINSIZE)
1052 		initsize = VSBUFMINSIZE;
1053 	buf->data = Z_Malloc(initsize, PU_STATIC, NULL);
1054 	buf->maxsize = initsize;
1055 	buf->cursize = 0;
1056 #undef VSBUFMINSIZE
1057 }
1058 
1059 /** Frees a variable size buffer.
1060   *
1061   * \param buf Buffer to free.
1062   */
VS_Free(vsbuf_t * buf)1063 void VS_Free(vsbuf_t *buf)
1064 {
1065 	buf->cursize = 0;
1066 }
1067 
1068 /** Clears a variable size buffer.
1069   *
1070   * \param buf Buffer to clear.
1071   */
VS_Clear(vsbuf_t * buf)1072 void VS_Clear(vsbuf_t *buf)
1073 {
1074 	buf->cursize = 0;
1075 }
1076 
1077 /** Makes sure a variable size buffer has enough space for data of a
1078   * certain length.
1079   *
1080   * \param buf    The buffer. It is enlarged if necessary.
1081   * \param length The length of data we need to add.
1082   * \return Pointer to where the new data can go.
1083   */
VS_GetSpace(vsbuf_t * buf,size_t length)1084 void *VS_GetSpace(vsbuf_t *buf, size_t length)
1085 {
1086 	void *data;
1087 
1088 	if (buf->cursize + length > buf->maxsize)
1089 	{
1090 		if (!buf->allowoverflow)
1091 			I_Error("overflow 111");
1092 
1093 		if (length > buf->maxsize)
1094 			I_Error("overflow l%s 112", sizeu1(length));
1095 
1096 		buf->overflowed = true;
1097 		CONS_Printf("VS buffer overflow");
1098 		VS_Clear(buf);
1099 	}
1100 
1101 	data = buf->data + buf->cursize;
1102 	buf->cursize += length;
1103 
1104 	return data;
1105 }
1106 
1107 /** Copies data to the end of a variable size buffer.
1108   *
1109   * \param buf    The buffer.
1110   * \param data   The data to copy.
1111   * \param length The length of the data.
1112   * \sa VS_Print
1113   */
VS_Write(vsbuf_t * buf,const void * data,size_t length)1114 void VS_Write(vsbuf_t *buf, const void *data, size_t length)
1115 {
1116 	M_Memcpy(VS_GetSpace(buf, length), data, length);
1117 }
1118 
VS_WriteEx(vsbuf_t * buf,const void * data,size_t length,int flags)1119 void VS_WriteEx(vsbuf_t *buf, const void *data, size_t length, int flags)
1120 {
1121 	char *p;
1122 	p = VS_GetSpace(buf, 2 + length);
1123 	p[0] = '\033';
1124 	p[1] = flags;
1125 	M_Memcpy(&p[2], data, length);
1126 }
1127 
1128 /** Prints text in a variable buffer. Like VS_Write() plus a
1129   * trailing NUL.
1130   *
1131   * \param buf  The buffer.
1132   * \param data The NUL-terminated string.
1133   * \sa VS_Write
1134   */
VS_Print(vsbuf_t * buf,const char * data)1135 void VS_Print(vsbuf_t *buf, const char *data)
1136 {
1137 	size_t len;
1138 
1139 	len = strlen(data) + 1;
1140 
1141 	if (buf->data[buf->cursize-1])
1142 		M_Memcpy((UINT8 *)VS_GetSpace(buf, len), data, len); // no trailing 0
1143 	else
1144 		M_Memcpy((UINT8 *)VS_GetSpace(buf, len-1) - 1, data, len); // write over trailing 0
1145 }
1146 
1147 // =========================================================================
1148 //
1149 //                           CONSOLE VARIABLES
1150 //
1151 //   console variables are a simple way of changing variables of the game
1152 //   through the console or code, at run time.
1153 //
1154 //   console vars acts like simplified commands, because a function can be
1155 //   attached to them, and called whenever a console var is modified
1156 //
1157 // =========================================================================
1158 
1159 static const char *cv_null_string = "";
1160 
1161 /** Searches if a variable has been registered.
1162   *
1163   * \param name Variable to search for.
1164   * \return Pointer to the variable if found, or NULL.
1165   * \sa CV_FindNetVar
1166   */
CV_FindVar(const char * name)1167 consvar_t *CV_FindVar(const char *name)
1168 {
1169 	consvar_t *cvar;
1170 
1171 	for (cvar = consvar_vars; cvar; cvar = cvar->next)
1172 		if (!stricmp(name,cvar->name))
1173 			return cvar;
1174 
1175 	return NULL;
1176 }
1177 
1178 #ifdef OLD22DEMOCOMPAT
1179 /** Builds a unique Net Variable identifier number, which was used
1180   * in network packets and demos instead of the full name.
1181   *
1182   * This function only still exists to keep compatibility with old demos.
1183   *
1184   * \param s Name of the variable.
1185   * \return A new unique identifier.
1186   */
CV_ComputeOldDemoID(const char * s)1187 static inline UINT16 CV_ComputeOldDemoID(const char *s)
1188 {
1189 	UINT16 ret = 0, i = 0;
1190 	static UINT16 premiers[16] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53};
1191 
1192 	while (*s)
1193 	{
1194 		ret = (UINT16)(ret + (*s)*premiers[i]);
1195 		s++;
1196 		i = (UINT16)((i+1) % 16);
1197 	}
1198 	return ret;
1199 }
1200 
1201 /** Finds a net variable based on its old style hash. If a hash collides, a
1202   * warning is printed and this function returns NULL.
1203   *
1204   * \param chk The variable's old style hash.
1205   * \return A pointer to the variable itself if found, or NULL.
1206   */
CV_FindOldDemoVar(UINT16 chk)1207 static old_demo_var_t *CV_FindOldDemoVar(UINT16 chk)
1208 {
1209 	old_demo_var_t *demovar;
1210 
1211 	for (demovar = consvar_old_demo_vars; demovar; demovar = demovar->next)
1212 	{
1213 		if (demovar->checksum == chk)
1214 		{
1215 			if (demovar->collides)
1216 			{
1217 				CONS_Alert(CONS_WARNING,
1218 						"Old demo netvar id %hu is a collision\n", chk);
1219 				return NULL;
1220 			}
1221 
1222 			return demovar;
1223 		}
1224 	}
1225 
1226 	return NULL;
1227 }
1228 #endif/*OLD22DEMOCOMPAT*/
1229 
1230 /** Finds a net variable based on its identifier number.
1231   *
1232   * \param netid The variable's identifier number.
1233   * \return A pointer to the variable itself if found, or NULL.
1234   */
CV_FindNetVar(UINT16 netid)1235 static consvar_t *CV_FindNetVar(UINT16 netid)
1236 {
1237 	consvar_t *cvar;
1238 
1239 	if (netid > consvar_number_of_netids)
1240 		return NULL;
1241 
1242 	for (cvar = consvar_vars; cvar; cvar = cvar->next)
1243 		if (cvar->netid == netid)
1244 			return cvar;
1245 
1246 	return NULL;
1247 }
1248 
1249 static void Setvalue(consvar_t *var, const char *valstr, boolean stealth);
1250 
1251 #ifdef OLD22DEMOCOMPAT
1252 /* Sets up a netvar for compatibility with old demos. */
CV_RegisterOldDemoVar(consvar_t * variable)1253 static void CV_RegisterOldDemoVar(consvar_t *variable)
1254 {
1255 	old_demo_var_t *demovar;
1256 	UINT16 old_demo_id;
1257 
1258 	old_demo_id = CV_ComputeOldDemoID(variable->name);
1259 
1260 	demovar = CV_FindOldDemoVar(old_demo_id);
1261 
1262 	if (demovar)
1263 		demovar->collides = true;
1264 	else
1265 	{
1266 		demovar = ZZ_Calloc(sizeof *demovar);
1267 
1268 		demovar->checksum = old_demo_id;
1269 		demovar->cvar = variable;
1270 
1271 		demovar->next = consvar_old_demo_vars;
1272 		consvar_old_demo_vars = demovar;
1273 	}
1274 }
1275 #endif
1276 
1277 /** Registers a variable for later use from the console.
1278   *
1279   * \param variable The variable to register.
1280   */
CV_RegisterVar(consvar_t * variable)1281 void CV_RegisterVar(consvar_t *variable)
1282 {
1283 	// first check to see if it has already been defined
1284 	if (CV_FindVar(variable->name))
1285 	{
1286 		CONS_Printf(M_GetText("Variable %s is already defined\n"), variable->name);
1287 		return;
1288 	}
1289 
1290 	// check for overlap with a command
1291 	if (COM_Exists(variable->name))
1292 	{
1293 		CONS_Printf(M_GetText("%s is a command name\n"), variable->name);
1294 		return;
1295 	}
1296 
1297 	// check net variables
1298 	if (variable->flags & CV_NETVAR)
1299 	{
1300 		/* in case of overflow... */
1301 		if (consvar_number_of_netids == UINT16_MAX)
1302 			I_Error("Way too many netvars");
1303 
1304 		variable->netid = ++consvar_number_of_netids;
1305 
1306 #ifdef OLD22DEMOCOMPAT
1307 		CV_RegisterOldDemoVar(variable);
1308 #endif
1309 	}
1310 
1311 	// link the variable in
1312 	if (!(variable->flags & CV_HIDEN))
1313 	{
1314 		variable->next = consvar_vars;
1315 		consvar_vars = variable;
1316 	}
1317 	variable->string = variable->zstring = NULL;
1318 	memset(&variable->revert, 0, sizeof variable->revert);
1319 	variable->changed = 0; // new variable has not been modified by the user
1320 
1321 #ifdef PARANOIA
1322 	if ((variable->flags & CV_NOINIT) && !(variable->flags & CV_CALL))
1323 		I_Error("variable %s has CV_NOINIT without CV_CALL\n", variable->name);
1324 	if ((variable->flags & CV_CALL) && !variable->func)
1325 		I_Error("variable %s has CV_CALL without a function\n", variable->name);
1326 #endif
1327 
1328 	if (variable->flags & CV_NOINIT)
1329 		variable->flags &= ~CV_CALL;
1330 
1331 	Setvalue(variable, variable->defaultvalue, false);
1332 
1333 	if (variable->flags & CV_NOINIT)
1334 		variable->flags |= CV_CALL;
1335 
1336 	// the SetValue will set this bit
1337 	variable->flags &= ~CV_MODIFIED;
1338 }
1339 
1340 /** Finds the string value of a console variable.
1341   *
1342   * \param var_name The variable's name.
1343   * \return The string value or "" if the variable is not found.
1344   */
CV_StringValue(const char * var_name)1345 static const char *CV_StringValue(const char *var_name)
1346 {
1347 	consvar_t *var;
1348 
1349 	var = CV_FindVar(var_name);
1350 	if (!var)
1351 		return cv_null_string;
1352 	return var->string;
1353 }
1354 
1355 /** Completes the name of a console variable.
1356   *
1357   * \param partial The partial name of the variable (potentially).
1358   * \param skips   Number of variables to skip.
1359   * \return The complete variable name, or NULL.
1360   * \sa COM_CompleteCommand, CV_CompleteAlias
1361   */
CV_CompleteVar(char * partial,INT32 skips)1362 const char *CV_CompleteVar(char *partial, INT32 skips)
1363 {
1364 	consvar_t *cvar;
1365 	size_t len;
1366 
1367 	len = strlen(partial);
1368 
1369 	if (!len)
1370 		return NULL;
1371 
1372 	// check variables
1373 	for (cvar = consvar_vars; cvar; cvar = cvar->next)
1374 		if (!strncmp(partial, cvar->name, len))
1375 			if (!skips--)
1376 				return cvar->name;
1377 
1378 	return NULL;
1379 }
1380 
1381 /** Sets a value to a variable with less checking. Only for internal use.
1382   *
1383   * \param var    Variable to set.
1384   * \param valstr String value for the variable.
1385   */
Setvalue(consvar_t * var,const char * valstr,boolean stealth)1386 static void Setvalue(consvar_t *var, const char *valstr, boolean stealth)
1387 {
1388 	boolean override = false;
1389 	INT32 overrideval = 0;
1390 
1391 	// If we want messages informing us if cheats have been enabled or disabled,
1392 	// we need to rework the consvars a little bit.  This call crashes the game
1393 	// on load because not all variables will be registered at that time.
1394 /*	boolean prevcheats = false;
1395 	if (var->flags & CV_CHEAT)
1396 		prevcheats = CV_CheatsEnabled(); */
1397 
1398 	if (var->PossibleValue)
1399 	{
1400 		INT32 v;
1401 
1402 		if (var->flags & CV_FLOAT)
1403 		{
1404 			double d = atof(valstr);
1405 			if (fpclassify(d) == FP_ZERO && valstr[0] != '0')
1406 				v = INT32_MIN;
1407 			else
1408 				v = (INT32)(d * FRACUNIT);
1409 		}
1410 		else
1411 		{
1412 			v = atoi(valstr);
1413 			if (!v && valstr[0] != '0')
1414 				v = INT32_MIN; // Invalid integer trigger
1415 		}
1416 
1417 		if (var->PossibleValue[0].strvalue && !stricmp(var->PossibleValue[0].strvalue, "MIN")) // bounded cvar
1418 		{
1419 #define MINVAL 0
1420 #define MAXVAL 1
1421 			INT32 i;
1422 #ifdef PARANOIA
1423 			if (!var->PossibleValue[MAXVAL].strvalue)
1424 				I_Error("Bounded cvar \"%s\" without maximum!\n", var->name);
1425 #endif
1426 
1427 			// search for other
1428 			for (i = MAXVAL+1; var->PossibleValue[i].strvalue; i++)
1429 				if (v == var->PossibleValue[i].value || !stricmp(var->PossibleValue[i].strvalue, valstr))
1430 				{
1431 					if (client && execversion_enabled)
1432 					{
1433 						if (var->revert.allocated)
1434 						{
1435 							Z_Free(var->revert.v.string);
1436 							var->revert.allocated = false; // the below value is not allocated in zone memory, don't try to free it!
1437 						}
1438 
1439 						var->revert.v.const_munge = var->PossibleValue[i].strvalue;
1440 
1441 						return;
1442 					}
1443 
1444 					// free the old value string
1445 					Z_Free(var->zstring);
1446 					var->zstring = NULL;
1447 
1448 					var->value = var->PossibleValue[i].value;
1449 					var->string = var->PossibleValue[i].strvalue;
1450 					goto finish;
1451 				}
1452 
1453 
1454 			if ((v != INT32_MIN && v < var->PossibleValue[MINVAL].value) || !stricmp(valstr, "MIN"))
1455 			{
1456 				v = var->PossibleValue[MINVAL].value;
1457 				valstr = var->PossibleValue[MINVAL].strvalue;
1458 				override = true;
1459 				overrideval = v;
1460 			}
1461 			else if ((v != INT32_MIN && v > var->PossibleValue[MAXVAL].value) || !stricmp(valstr, "MAX"))
1462 			{
1463 				v = var->PossibleValue[MAXVAL].value;
1464 				valstr = var->PossibleValue[MAXVAL].strvalue;
1465 				override = true;
1466 				overrideval = v;
1467 			}
1468 			if (v == INT32_MIN)
1469 				goto badinput;
1470 #undef MINVAL
1471 #undef MAXVAL
1472 		}
1473 		else
1474 		{
1475 			INT32 i;
1476 
1477 			// check first strings
1478 			for (i = 0; var->PossibleValue[i].strvalue; i++)
1479 				if (!stricmp(var->PossibleValue[i].strvalue, valstr))
1480 					goto found;
1481 			if (v != INT32_MIN)
1482 			{
1483 				// check INT32 now
1484 				for (i = 0; var->PossibleValue[i].strvalue; i++)
1485 					if (v == var->PossibleValue[i].value)
1486 						goto found;
1487 			}
1488 			// Not found ... but wait, there's hope!
1489 			if (var->PossibleValue == CV_OnOff || var->PossibleValue == CV_YesNo)
1490 			{
1491 				overrideval = -1;
1492 				if (!stricmp(valstr, "on") || !stricmp(valstr, "yes"))
1493 					overrideval = 1;
1494 				else if (!stricmp(valstr, "off") || !stricmp(valstr, "no"))
1495 					overrideval = 0;
1496 
1497 				if (overrideval != -1)
1498 				{
1499 					for (i = 0; var->PossibleValue[i].strvalue; i++)
1500 						if (overrideval == var->PossibleValue[i].value)
1501 							goto found;
1502 				}
1503 			}
1504 
1505 			// ...or not.
1506 			goto badinput;
1507 found:
1508 			if (client && execversion_enabled)
1509 			{
1510 				var->revert.v.const_munge = var->PossibleValue[i].strvalue;
1511 				return;
1512 			}
1513 
1514 			var->value = var->PossibleValue[i].value;
1515 			var->string = var->PossibleValue[i].strvalue;
1516 			goto finish;
1517 		}
1518 	}
1519 
1520 	if (client && execversion_enabled)
1521 	{
1522 		if (var->revert.allocated)
1523 		{
1524 			Z_Free(var->revert.v.string);
1525 			// Z_StrDup creates a new zone memory block, so we can keep the allocated flag on
1526 		}
1527 
1528 		var->revert.v.string = Z_StrDup(valstr);
1529 
1530 		return;
1531 	}
1532 
1533 	// free the old value string
1534 	Z_Free(var->zstring);
1535 
1536 	var->string = var->zstring = Z_StrDup(valstr);
1537 
1538 	if (override)
1539 		var->value = overrideval;
1540 	else if (var->flags & CV_FLOAT)
1541 	{
1542 		double d = atof(var->string);
1543 		var->value = (INT32)(d * FRACUNIT);
1544 	}
1545 	else
1546 	{
1547 		if (var == &cv_forceskin)
1548 		{
1549 			var->value = R_SkinAvailable(var->string);
1550 			if (!R_SkinUsable(-1, var->value))
1551 				var->value = -1;
1552 		}
1553 		else
1554 			var->value = atoi(var->string);
1555 	}
1556 
1557 finish:
1558 	// See the note above.
1559 /* 	if (var->flags & CV_CHEAT)
1560 	{
1561 		boolean newcheats = CV_CheatsEnabled();
1562 
1563 		if (!prevcheats && newcheats)
1564 			CONS_Printf(M_GetText("Cheats have been enabled.\n"));
1565 		else if (prevcheats && !newcheats)
1566 			CONS_Printf(M_GetText("Cheats have been disabled.\n"));
1567 	} */
1568 
1569 	if (var->flags & CV_SHOWMODIFONETIME || var->flags & CV_SHOWMODIF)
1570 	{
1571 		CONS_Printf(M_GetText("%s set to %s\n"), var->name, var->string);
1572 		var->flags &= ~CV_SHOWMODIFONETIME;
1573 	}
1574 	else // display message in debug file only
1575 	{
1576 		DEBFILE(va("%s set to %s\n", var->name, var->string));
1577 	}
1578 	var->flags |= CV_MODIFIED;
1579 	// raise 'on change' code
1580 	LUA_CVarChanged(var); // let consolelib know what cvar this is.
1581 	if (var->flags & CV_CALL && !stealth)
1582 		var->func();
1583 
1584 	return;
1585 
1586 // landing point for possiblevalue failures
1587 badinput:
1588 
1589 	if (var != &cv_nextmap) // Suppress errors for cv_nextmap
1590 		CONS_Printf(M_GetText("\"%s\" is not a possible value for \"%s\"\n"), valstr, var->name);
1591 
1592 	// default value not valid... ?!
1593 	if (var->defaultvalue == valstr)
1594 		I_Error("Variable %s default value \"%s\" is not a possible value\n", var->name, var->defaultvalue);
1595 }
1596 
1597 //
1598 // Use XD_NETVAR argument:
1599 //      2 byte for variable identification
1600 //      then the value of the variable followed with a 0 byte (like str)
1601 //
1602 
1603 static boolean serverloading = false;
1604 
1605 static consvar_t *
ReadNetVar(UINT8 ** p,char ** return_value,boolean * return_stealth)1606 ReadNetVar (UINT8 **p, char **return_value, boolean *return_stealth)
1607 {
1608 	UINT16  netid;
1609 	char   *val;
1610 	boolean stealth;
1611 
1612 	consvar_t *cvar;
1613 
1614 	netid   = READUINT16 (*p);
1615 	val     = (char *)*p;
1616 	SKIPSTRING (*p);
1617 	stealth = READUINT8  (*p);
1618 
1619 	cvar = CV_FindNetVar(netid);
1620 
1621 	if (cvar)
1622 	{
1623 		(*return_value)   = val;
1624 		(*return_stealth) = stealth;
1625 
1626 		DEBFILE(va("Netvar received: %s [netid=%d] value %s\n", cvar->name, netid, val));
1627 	}
1628 	else
1629 		CONS_Alert(CONS_WARNING, "Netvar not found with netid %hu\n", netid);
1630 
1631 	return cvar;
1632 }
1633 
1634 #ifdef OLD22DEMOCOMPAT
1635 static consvar_t *
ReadOldDemoVar(UINT8 ** p,char ** return_value,boolean * return_stealth)1636 ReadOldDemoVar (UINT8 **p, char **return_value, boolean *return_stealth)
1637 {
1638 	UINT16  id;
1639 	char   *val;
1640 	boolean stealth;
1641 
1642 	old_demo_var_t *demovar;
1643 
1644 	id      = READUINT16 (*p);
1645 	val     = (char *)*p;
1646 	SKIPSTRING (*p);
1647 	stealth = READUINT8  (*p);
1648 
1649 	demovar = CV_FindOldDemoVar(id);
1650 
1651 	if (demovar)
1652 	{
1653 		(*return_value)   = val;
1654 		(*return_stealth) = stealth;
1655 
1656 		return demovar->cvar;
1657 	}
1658 	else
1659 	{
1660 		CONS_Alert(CONS_WARNING, "Netvar not found with old demo id %hu\n", id);
1661 		return NULL;
1662 	}
1663 }
1664 #endif/*OLD22DEMOCOMPAT*/
1665 
1666 static consvar_t *
ReadDemoVar(UINT8 ** p,char ** return_value,boolean * return_stealth)1667 ReadDemoVar (UINT8 **p, char **return_value, boolean *return_stealth)
1668 {
1669 	char   *name;
1670 	char   *val;
1671 	boolean stealth;
1672 
1673 	consvar_t *cvar;
1674 
1675 	name    = (char *)*p;
1676 	SKIPSTRING (*p);
1677 	val     = (char *)*p;
1678 	SKIPSTRING (*p);
1679 	stealth = READUINT8  (*p);
1680 
1681 	cvar = CV_FindVar(name);
1682 
1683 	if (cvar)
1684 	{
1685 		(*return_value)   = val;
1686 		(*return_stealth) = stealth;
1687 	}
1688 	else
1689 		CONS_Alert(CONS_WARNING, "Netvar not found with name %s\n", name);
1690 
1691 	return cvar;
1692 }
1693 
Got_NetVar(UINT8 ** p,INT32 playernum)1694 static void Got_NetVar(UINT8 **p, INT32 playernum)
1695 {
1696 	consvar_t *cvar;
1697 	char *svalue;
1698 	boolean stealth;
1699 
1700 	if (playernum != serverplayer && !IsPlayerAdmin(playernum) && !serverloading)
1701 	{
1702 		// not from server or remote admin, must be hacked/buggy client
1703 		CONS_Alert(CONS_WARNING, M_GetText("Illegal netvar command received from %s\n"), player_names[playernum]);
1704 		if (server)
1705 			SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
1706 		return;
1707 	}
1708 
1709 	cvar = ReadNetVar(p, &svalue, &stealth);
1710 
1711 	if (cvar)
1712 		Setvalue(cvar, svalue, stealth);
1713 }
1714 
CV_SaveVars(UINT8 ** p,boolean in_demo)1715 void CV_SaveVars(UINT8 **p, boolean in_demo)
1716 {
1717 	consvar_t *cvar;
1718 	UINT8 *count_p = *p;
1719 	UINT16 count = 0;
1720 
1721 	// send only changed cvars ...
1722 	// the client will reset all netvars to default before loading
1723 	WRITEUINT16(*p, 0x0000);
1724 	for (cvar = consvar_vars; cvar; cvar = cvar->next)
1725 		if ((cvar->flags & CV_NETVAR) && !CV_IsSetToDefault(cvar))
1726 		{
1727 			if (in_demo)
1728 				WRITESTRING(*p, cvar->name);
1729 			else
1730 				WRITEUINT16(*p, cvar->netid);
1731 			WRITESTRING(*p, cvar->string);
1732 			WRITEUINT8(*p, false);
1733 			++count;
1734 		}
1735 	WRITEUINT16(count_p, count);
1736 }
1737 
CV_LoadVars(UINT8 ** p,consvar_t * (* got)(UINT8 ** p,char ** ret_value,boolean * ret_stealth))1738 static void CV_LoadVars(UINT8 **p,
1739 		consvar_t *(*got)(UINT8 **p, char **ret_value, boolean *ret_stealth))
1740 {
1741 	consvar_t *cvar;
1742 	UINT16 count;
1743 
1744 	char *val;
1745 	boolean stealth;
1746 
1747 	// prevent "invalid command received"
1748 	serverloading = true;
1749 
1750 	for (cvar = consvar_vars; cvar; cvar = cvar->next)
1751 	{
1752 		if (cvar->flags & CV_NETVAR)
1753 		{
1754 			if (client && cvar->revert.v.string == NULL)
1755 			{
1756 				cvar->revert.v.const_munge = cvar->string;
1757 				cvar->revert.allocated = ( cvar->zstring != NULL );
1758 				cvar->zstring = NULL;/* don't free this */
1759 			}
1760 
1761 			Setvalue(cvar, cvar->defaultvalue, true);
1762 		}
1763 	}
1764 
1765 	count = READUINT16(*p);
1766 	while (count--)
1767 	{
1768 		cvar = (*got)(p, &val, &stealth);
1769 
1770 		if (cvar)
1771 			Setvalue(cvar, val, stealth);
1772 	}
1773 
1774 	serverloading = false;
1775 }
1776 
CV_RevertNetVars(void)1777 void CV_RevertNetVars(void)
1778 {
1779 	consvar_t * cvar;
1780 
1781 	for (cvar = consvar_vars; cvar; cvar = cvar->next)
1782 	{
1783 		if (cvar->revert.v.string != NULL)
1784 		{
1785 			Setvalue(cvar, cvar->revert.v.string, false);
1786 
1787 			if (cvar->revert.allocated)
1788 			{
1789 				Z_Free(cvar->revert.v.string);
1790 				cvar->revert.allocated = false; // no value being held now
1791 			}
1792 
1793 			cvar->revert.v.string = NULL;
1794 		}
1795 	}
1796 }
1797 
CV_LoadNetVars(UINT8 ** p)1798 void CV_LoadNetVars(UINT8 **p)
1799 {
1800 	CV_LoadVars(p, ReadNetVar);
1801 }
1802 
1803 #ifdef OLD22DEMOCOMPAT
CV_LoadOldDemoVars(UINT8 ** p)1804 void CV_LoadOldDemoVars(UINT8 **p)
1805 {
1806 	CV_LoadVars(p, ReadOldDemoVar);
1807 }
1808 #endif
1809 
CV_LoadDemoVars(UINT8 ** p)1810 void CV_LoadDemoVars(UINT8 **p)
1811 {
1812 	CV_LoadVars(p, ReadDemoVar);
1813 }
1814 
1815 static void CV_SetCVar(consvar_t *var, const char *value, boolean stealth);
1816 
CV_ResetCheatNetVars(void)1817 void CV_ResetCheatNetVars(void)
1818 {
1819 	consvar_t *cvar;
1820 
1821 	// Stealthset everything back to default.
1822 	for (cvar = consvar_vars; cvar; cvar = cvar->next)
1823 		if (cvar->flags & CV_CHEAT)
1824 			CV_SetCVar(cvar, cvar->defaultvalue, true);
1825 }
1826 
1827 // Returns true if the variable's current value is its default value
CV_IsSetToDefault(consvar_t * v)1828 boolean CV_IsSetToDefault(consvar_t *v)
1829 {
1830 	return (!(strcmp(v->defaultvalue, v->string)));
1831 }
1832 
1833 // If any cheats CVars are not at their default settings, return true.
1834 // Else return false.
1835 // This returns a UINT8 because I'm too lazy to deal with the packet structure.
1836 // Deal with it. =P
CV_CheatsEnabled(void)1837 UINT8 CV_CheatsEnabled(void)
1838 {
1839 	consvar_t *cvar;
1840 
1841 	for (cvar = consvar_vars; cvar; cvar = cvar->next)
1842 		if ((cvar->flags & CV_CHEAT) && strcmp(cvar->defaultvalue, cvar->string))
1843 			return 1;
1844 	return 0;
1845 }
1846 
1847 /** Sets a value to a variable, performing some checks and calling the
1848   * callback function if there is one.
1849   * Does as if "<varname> <value>" is entered at the console.
1850   *
1851   * \param var   The variable.
1852   * \param value The string value.
1853   * \sa CV_StealthSet, CV_SetValue
1854   */
CV_SetCVar(consvar_t * var,const char * value,boolean stealth)1855 static void CV_SetCVar(consvar_t *var, const char *value, boolean stealth)
1856 {
1857 #ifdef PARANOIA
1858 	if (!var)
1859 		I_Error("CV_Set: no variable\n");
1860 	if (!var->string)
1861 		I_Error("CV_Set: %s no string set!\n", var->name);
1862 #endif
1863 	if (!var || !var->string || !value || !stricmp(var->string, value))
1864 		return; // no changes
1865 
1866 	if (var->flags & CV_NETVAR)
1867 	{
1868 		// send the value of the variable
1869 		UINT8 buf[128];
1870 		UINT8 *p = buf;
1871 
1872 		// Loading from a config in a netgame? Set revert value.
1873 		if (client && execversion_enabled)
1874 		{
1875 			Setvalue(var, value, true);
1876 			return;
1877 		}
1878 
1879 		if (!(server || (addedtogame && IsPlayerAdmin(consoleplayer))))
1880 		{
1881 			CONS_Printf(M_GetText("Only the server or admin can change: %s %s\n"), var->name, var->string);
1882 			return;
1883 		}
1884 
1885 		if (var == &cv_forceskin)
1886 		{
1887 			INT32 skin = R_SkinAvailable(value);
1888 			if ((stricmp(value, "None")) && ((skin == -1) || !R_SkinUsable(-1, skin)))
1889 			{
1890 				CONS_Printf("Please provide a valid skin name (\"None\" disables).\n");
1891 				return;
1892 			}
1893 		}
1894 
1895 		// Only add to netcmd buffer if in a netgame, otherwise, just change it.
1896 		if (netgame || multiplayer)
1897 		{
1898 			WRITEUINT16(p, var->netid);
1899 			WRITESTRING(p, value);
1900 			WRITEUINT8(p, stealth);
1901 
1902 			SendNetXCmd(XD_NETVAR, buf, p-buf);
1903 		}
1904 		else
1905 			Setvalue(var, value, stealth);
1906 	}
1907 	else
1908 		if ((var->flags & CV_NOTINNET) && netgame)
1909 		{
1910 			CONS_Printf(M_GetText("This variable can't be changed while in netgame: %s %s\n"), var->name, var->string);
1911 			return;
1912 		}
1913 		else
1914 			Setvalue(var, value, stealth);
1915 }
1916 
1917 /** Sets a value to a variable without calling its callback function.
1918   *
1919   * \param var   The variable.
1920   * \param value The string value.
1921   * \sa CV_Set, CV_StealthSetValue
1922   */
CV_StealthSet(consvar_t * var,const char * value)1923 void CV_StealthSet(consvar_t *var, const char *value)
1924 {
1925 	CV_SetCVar(var, value, true);
1926 }
1927 
1928 /** Sets a numeric value to a variable, sometimes calling its callback
1929   * function.
1930   *
1931   * \param var   The variable.
1932   * \param value The numeric value, converted to a string before setting.
1933   * \param stealth Do we call the callback function or not?
1934   */
CV_SetValueMaybeStealth(consvar_t * var,INT32 value,boolean stealth)1935 static void CV_SetValueMaybeStealth(consvar_t *var, INT32 value, boolean stealth)
1936 {
1937 	char val[SKINNAMESIZE+1];
1938 
1939 	if (var == &cv_forceskin) // Special handling.
1940 	{
1941 		const char *tmpskin = NULL;
1942 		if ((value < 0) || (value >= numskins))
1943 			tmpskin = "None";
1944 		else
1945 			tmpskin = skins[value].name;
1946 		strncpy(val, tmpskin, SKINNAMESIZE);
1947 	}
1948 	else
1949 		sprintf(val, "%d", value);
1950 
1951 	CV_SetCVar(var, val, stealth);
1952 }
1953 
1954 /** Sets a numeric value to a variable without calling its callback
1955   * function.
1956   *
1957   * \param var   The variable.
1958   * \param value The numeric value, converted to a string before setting.
1959   * \sa CV_SetValue, CV_StealthSet
1960   */
CV_StealthSetValue(consvar_t * var,INT32 value)1961 void CV_StealthSetValue(consvar_t *var, INT32 value)
1962 {
1963 	CV_SetValueMaybeStealth(var, value, true);
1964 }
1965 
1966 // New wrapper for what used to be CV_Set()
CV_Set(consvar_t * var,const char * value)1967 void CV_Set(consvar_t *var, const char *value)
1968 {
1969 	CV_SetCVar(var, value, false);
1970 }
1971 
1972 /** Sets a numeric value to a variable, performing some checks and
1973   * calling the callback function if there is one.
1974   *
1975   * \param var   The variable.
1976   * \param value The numeric value, converted to a string before setting.
1977   * \sa CV_Set, CV_StealthSetValue
1978   */
CV_SetValue(consvar_t * var,INT32 value)1979 void CV_SetValue(consvar_t *var, INT32 value)
1980 {
1981 	CV_SetValueMaybeStealth(var, value, false);
1982 }
1983 
1984 /** Adds a value to a console variable.
1985   * Used to increment and decrement variables from the menu.
1986   * Contains special cases to handle pointlimit in some multiplayer modes,
1987   * map number for game hosting, etc.
1988   *
1989   * \param var       The variable to add to.
1990   * \param increment The change in the variable; can be negative for a
1991   *                  decrement.
1992   * \sa CV_SetValue
1993   */
CV_AddValue(consvar_t * var,INT32 increment)1994 void CV_AddValue(consvar_t *var, INT32 increment)
1995 {
1996 	INT32 newvalue, max;
1997 
1998 	if (!increment)
1999 		return;
2000 
2001 	// count pointlimit better
2002 	if (var == &cv_pointlimit && (gametype == GT_MATCH))
2003 		increment *= 50;
2004 
2005 	if (var == &cv_forceskin) // Special handling.
2006 	{
2007 		INT32 oldvalue = var->value;
2008 		newvalue = oldvalue;
2009 		do
2010 		{
2011 			newvalue += increment;
2012 			if (newvalue < -1)
2013 				newvalue = (numskins - 1);
2014 			else if (newvalue >= numskins)
2015 				newvalue = -1;
2016 		} while ((oldvalue != newvalue)
2017 				&& !(R_SkinUsable(-1, newvalue)));
2018 	}
2019 	else
2020 		newvalue = var->value + increment;
2021 
2022 	if (var->PossibleValue)
2023 	{
2024 		if (var == &cv_nextmap)
2025 		{
2026 			// Special case for the nextmap variable, used only directly from the menu
2027 			INT32 oldvalue = var->value - 1, gt;
2028 			gt = cv_newgametype.value;
2029 			{
2030 				newvalue = var->value - 1;
2031 				do
2032 				{
2033 					if(increment > 0) // Going up!
2034 					{
2035 						newvalue++;
2036 						if (newvalue == NUMMAPS)
2037 							newvalue = 0;
2038 					}
2039 					else // Going down!
2040 					{
2041 						newvalue--;
2042 						if (newvalue == -1)
2043 							newvalue = NUMMAPS-1;
2044 					}
2045 
2046 					if (newvalue == oldvalue)
2047 						gt = -1; // don't loop forever if there's none of a certain gametype
2048 
2049 					if(!mapheaderinfo[newvalue])
2050 						continue; // Don't allocate the header.  That just makes memory usage skyrocket.
2051 
2052 				} while (newvalue != oldvalue && !M_CanShowLevelInList(newvalue, gt));
2053 
2054 				var->value = newvalue + 1;
2055 				var->func();
2056 				return;
2057 			}
2058 		}
2059 #define MINVAL 0
2060 #define MAXVAL 1
2061 		else if (var->PossibleValue[MINVAL].strvalue && !strcmp(var->PossibleValue[MINVAL].strvalue, "MIN"))
2062 		{
2063 #ifdef PARANOIA
2064 			if (!var->PossibleValue[MAXVAL].strvalue)
2065 				I_Error("Bounded cvar \"%s\" without maximum!\n", var->name);
2066 #endif
2067 
2068 			if (newvalue < var->PossibleValue[MINVAL].value || newvalue > var->PossibleValue[MAXVAL].value)
2069 			{
2070 				INT32 currentindice = -1, newindice;
2071 				for (max = MAXVAL+1; var->PossibleValue[max].strvalue; max++)
2072 				{
2073 					if (var->PossibleValue[max].value == newvalue)
2074 					{
2075 						increment = 0;
2076 						currentindice = max;
2077 					}
2078 					else if (var->PossibleValue[max].value == var->value)
2079 						currentindice = max;
2080 				}
2081 
2082 				if (increment)
2083 				{
2084 					increment = (increment > 0) ? 1 : -1;
2085 					if (currentindice == -1 && max != MAXVAL+1)
2086 						newindice = ((increment > 0) ? MAXVAL : max) + increment;
2087 					else
2088 						newindice = currentindice + increment;
2089 
2090 					if (newindice >= max || newindice <= MAXVAL)
2091 					{
2092 						if (var == &cv_pointlimit && (gametype == GT_MATCH) && increment > 0)
2093 							CV_SetValue(var, 50);
2094 						else
2095 						{
2096 							newvalue = var->PossibleValue[((increment > 0) ? MINVAL : MAXVAL)].value;
2097 							CV_SetValue(var, newvalue);
2098 						}
2099 					}
2100 					else
2101 						CV_Set(var, var->PossibleValue[newindice].strvalue);
2102 				}
2103 				else
2104 					CV_Set(var, var->PossibleValue[currentindice].strvalue);
2105 			}
2106 			else
2107 				CV_SetValue(var, newvalue);
2108 		}
2109 #undef MINVAL
2110 #undef MAXVAL
2111 		else
2112 		{
2113 			INT32 currentindice = -1, newindice;
2114 
2115 			// this code do not support more than same value for differant PossibleValue
2116 			for (max = 0; var->PossibleValue[max].strvalue; max++)
2117 				if (var->PossibleValue[max].value == var->value)
2118 					currentindice = max;
2119 
2120 			if (var == &cv_chooseskin)
2121 			{
2122 				// Special case for the chooseskin variable, used only directly from the menu
2123 				newvalue = var->value - 1;
2124 				do
2125 				{
2126 					if (increment > 0) // Going up!
2127 					{
2128 						newvalue++;
2129 						if (newvalue == MAXSKINS)
2130 							newvalue = 0;
2131 					}
2132 					else if (increment < 0) // Going down!
2133 					{
2134 						newvalue--;
2135 						if (newvalue == -1)
2136 							newvalue = MAXSKINS-1;
2137 					}
2138 				} while (var->PossibleValue[newvalue].strvalue == NULL);
2139 
2140 				var->value = newvalue + 1;
2141 				var->string = var->PossibleValue[newvalue].strvalue;
2142 				var->func();
2143 				return;
2144 			}
2145 #ifdef PARANOIA
2146 			if (currentindice == -1)
2147 				I_Error("CV_AddValue: current value %d not found in possible value\n",
2148 					var->value);
2149 #endif
2150 
2151 			newindice = (currentindice + increment + max) % max;
2152 			CV_Set(var, var->PossibleValue[newindice].strvalue);
2153 		}
2154 	}
2155 	else
2156 		CV_SetValue(var, newvalue);
2157 
2158 	var->changed = 1; // user has changed it now
2159 }
2160 
CV_InitFilterVar(void)2161 void CV_InitFilterVar(void)
2162 {
2163 	joyaxis_default = joyaxis2_default = true;
2164 	joyaxis_count = joyaxis2_count = 0;
2165 }
2166 
CV_ToggleExecVersion(boolean enable)2167 void CV_ToggleExecVersion(boolean enable)
2168 {
2169 	execversion_enabled = enable;
2170 }
2171 
CV_EnforceExecVersion(void)2172 static void CV_EnforceExecVersion(void)
2173 {
2174 	if (!execversion_enabled)
2175 		CV_StealthSetValue(&cv_execversion, EXECVERSION);
2176 }
2177 
CV_FilterJoyAxisVars(consvar_t * v,const char * valstr)2178 static boolean CV_FilterJoyAxisVars(consvar_t *v, const char *valstr)
2179 {
2180 	// If ALL axis settings are previous defaults, set them to the new defaults
2181 	// EXECVERSION < 26 (2.1.21)
2182 
2183 	if (joyaxis_default)
2184 	{
2185 		if (!stricmp(v->name, "joyaxis_turn"))
2186 		{
2187 			if (joyaxis_count > 6) return false;
2188 			// we're currently setting the new defaults, don't interfere
2189 			else if (joyaxis_count == 6) return true;
2190 
2191 			if (!stricmp(valstr, "X-Axis")) joyaxis_count++;
2192 			else joyaxis_default = false;
2193 		}
2194 		if (!stricmp(v->name, "joyaxis_move"))
2195 		{
2196 			if (joyaxis_count > 6) return false;
2197 			else if (joyaxis_count == 6) return true;
2198 
2199 			if (!stricmp(valstr, "Y-Axis")) joyaxis_count++;
2200 			else joyaxis_default = false;
2201 		}
2202 		if (!stricmp(v->name, "joyaxis_side"))
2203 		{
2204 			if (joyaxis_count > 6) return false;
2205 			else if (joyaxis_count == 6) return true;
2206 
2207 			if (!stricmp(valstr, "Z-Axis")) joyaxis_count++;
2208 			else joyaxis_default = false;
2209 		}
2210 		if (!stricmp(v->name, "joyaxis_look"))
2211 		{
2212 			if (joyaxis_count > 6) return false;
2213 			else if (joyaxis_count == 6) return true;
2214 
2215 			if (!stricmp(valstr, "None")) joyaxis_count++;
2216 			else joyaxis_default = false;
2217 		}
2218 		if (!stricmp(v->name, "joyaxis_fire")
2219 			|| !stricmp(v->name, "joyaxis_firenormal"))
2220 		{
2221 			if (joyaxis_count > 6) return false;
2222 			else if (joyaxis_count == 6) return true;
2223 
2224 			if (!stricmp(valstr, "None")) joyaxis_count++;
2225 			else joyaxis_default = false;
2226 		}
2227 		// reset all axis settings to defaults
2228 		if (joyaxis_count == 6)
2229 		{
2230 			COM_BufInsertText(va("%s \"%s\"\n", cv_turnaxis.name, cv_turnaxis.defaultvalue));
2231 			COM_BufInsertText(va("%s \"%s\"\n", cv_moveaxis.name, cv_moveaxis.defaultvalue));
2232 			COM_BufInsertText(va("%s \"%s\"\n", cv_sideaxis.name, cv_sideaxis.defaultvalue));
2233 			COM_BufInsertText(va("%s \"%s\"\n", cv_lookaxis.name, cv_lookaxis.defaultvalue));
2234 			COM_BufInsertText(va("%s \"%s\"\n", cv_fireaxis.name, cv_fireaxis.defaultvalue));
2235 			COM_BufInsertText(va("%s \"%s\"\n", cv_firenaxis.name, cv_firenaxis.defaultvalue));
2236 			joyaxis_count++;
2237 			return false;
2238 		}
2239 	}
2240 
2241 	if (joyaxis2_default)
2242 	{
2243 		if (!stricmp(v->name, "joyaxis2_turn"))
2244 		{
2245 			if (joyaxis2_count > 6) return false;
2246 			// we're currently setting the new defaults, don't interfere
2247 			else if (joyaxis2_count == 6) return true;
2248 
2249 			if (!stricmp(valstr, "X-Axis")) joyaxis2_count++;
2250 			else joyaxis2_default = false;
2251 		}
2252 		if (!stricmp(v->name, "joyaxis2_move"))
2253 		{
2254 			if (joyaxis2_count > 6) return false;
2255 			else if (joyaxis2_count == 6) return true;
2256 
2257 			if (!stricmp(valstr, "Y-Axis")) joyaxis2_count++;
2258 			else joyaxis2_default = false;
2259 		}
2260 		if (!stricmp(v->name, "joyaxis2_side"))
2261 		{
2262 			if (joyaxis2_count > 6) return false;
2263 			else if (joyaxis2_count == 6) return true;
2264 
2265 			if (!stricmp(valstr, "Z-Axis")) joyaxis2_count++;
2266 			else joyaxis2_default = false;
2267 		}
2268 		if (!stricmp(v->name, "joyaxis2_look"))
2269 		{
2270 			if (joyaxis2_count > 6) return false;
2271 			else if (joyaxis2_count == 6) return true;
2272 
2273 			if (!stricmp(valstr, "None")) joyaxis2_count++;
2274 			else joyaxis2_default = false;
2275 		}
2276 		if (!stricmp(v->name, "joyaxis2_fire")
2277 			|| !stricmp(v->name, "joyaxis2_firenormal"))
2278 		{
2279 			if (joyaxis2_count > 6) return false;
2280 			else if (joyaxis2_count == 6) return true;
2281 
2282 			if (!stricmp(valstr, "None")) joyaxis2_count++;
2283 			else joyaxis2_default = false;
2284 		}
2285 
2286 		// reset all axis settings to defaults
2287 		if (joyaxis2_count == 6)
2288 		{
2289 			COM_BufInsertText(va("%s \"%s\"\n", cv_turnaxis2.name, cv_turnaxis2.defaultvalue));
2290 			COM_BufInsertText(va("%s \"%s\"\n", cv_moveaxis2.name, cv_moveaxis2.defaultvalue));
2291 			COM_BufInsertText(va("%s \"%s\"\n", cv_sideaxis2.name, cv_sideaxis2.defaultvalue));
2292 			COM_BufInsertText(va("%s \"%s\"\n", cv_lookaxis2.name, cv_lookaxis2.defaultvalue));
2293 			COM_BufInsertText(va("%s \"%s\"\n", cv_fireaxis2.name, cv_fireaxis2.defaultvalue));
2294 			COM_BufInsertText(va("%s \"%s\"\n", cv_firenaxis2.name, cv_firenaxis2.defaultvalue));
2295 			joyaxis2_count++;
2296 			return false;
2297 		}
2298 	}
2299 
2300 	// we haven't reached our counts yet, or we're not default
2301 	return true;
2302 }
2303 
CV_FilterVarByVersion(consvar_t * v,const char * valstr)2304 static boolean CV_FilterVarByVersion(consvar_t *v, const char *valstr)
2305 {
2306 	// True means allow the CV change, False means block it
2307 
2308 	// We only care about CV_SAVE because this filters the user's config files
2309 	// We do this same check in CV_Command
2310 	if (!(v->flags & CV_SAVE))
2311 		return true;
2312 
2313 	if (GETMAJOREXECVERSION(cv_execversion.value) < 26) // 26 = 2.1.21
2314 	{
2315 		// MOUSE SETTINGS
2316 		// alwaysfreelook split between first and third person (chasefreelook)
2317 		// mousemove was on by default, which invalidates the current approach
2318 		if (!stricmp(v->name, "alwaysmlook")
2319 			|| !stricmp(v->name, "alwaysmlook2")
2320 			|| !stricmp(v->name, "mousemove")
2321 			|| !stricmp(v->name, "mousemove2"))
2322 			return false;
2323 
2324 		// mousesens was changed from 35 to 20 due to oversensitivity
2325 		if ((!stricmp(v->name, "mousesens")
2326 			|| !stricmp(v->name, "mousesens2")
2327 			|| !stricmp(v->name, "mouseysens")
2328 			|| !stricmp(v->name, "mouseysens2"))
2329 			&& atoi(valstr) == 35)
2330 			return false;
2331 
2332 		// JOYSTICK DEFAULTS
2333 		// use_joystick was changed from 0 to 1 to automatically use a joystick if available
2334 #if defined(HAVE_SDL) || defined(_WINDOWS)
2335 		if ((!stricmp(v->name, "use_joystick")
2336 			|| !stricmp(v->name, "use_joystick2"))
2337 			&& atoi(valstr) == 0)
2338 			return false;
2339 #endif
2340 
2341 		// axis defaults were changed to be friendly to 360 controllers
2342 		// if ALL axis settings are defaults, then change them to new values
2343 		if (!CV_FilterJoyAxisVars(v, valstr))
2344 			return false;
2345 	}
2346 	return true;
2347 }
2348 
2349 /** Displays or changes a variable from the console.
2350   * Since the user is presumed to have been directly responsible
2351   * for this change, the variable is marked as changed this game.
2352   *
2353   * \return False if passed command was not recognized as a console
2354   *         variable, otherwise true.
2355   * \sa CV_ClearChangedFlags
2356   */
CV_Command(void)2357 static boolean CV_Command(void)
2358 {
2359 	consvar_t *v;
2360 
2361 	// check variables
2362 	v = CV_FindVar(COM_Argv(0));
2363 	if (!v)
2364 		return false;
2365 
2366 	if (( com_flags & COM_SAFE ) && ( v->flags & CV_NOLUA ))
2367 		return false;
2368 
2369 	// perform a variable print or set
2370 	if (COM_Argc() == 1)
2371 	{
2372 		CONS_Printf(M_GetText("\"%s\" is \"%s\" default is \"%s\"\n"), v->name, v->string, v->defaultvalue);
2373 		return true;
2374 	}
2375 
2376 	if (!(v->flags & CV_SAVE) || CV_FilterVarByVersion(v, COM_Argv(1)))
2377 	{
2378 		CV_Set(v, COM_Argv(1));
2379 		v->changed = 1; // now it's been changed by (presumably) the user
2380 	}
2381 	return true;
2382 }
2383 
2384 /** Marks all variables as unchanged, indicating they've not been changed
2385   * by the user this game.
2386   *
2387   * \sa CV_Command
2388   * \author Graue <graue@oceanbase.org>
2389   */
CV_ClearChangedFlags(void)2390 void CV_ClearChangedFlags(void)
2391 {
2392 	consvar_t *cvar;
2393 
2394 	for (cvar = consvar_vars; cvar; cvar = cvar->next)
2395 		cvar->changed = 0;
2396 }
2397 
2398 /** Saves console variables to a file if they have the ::CV_SAVE
2399   * flag set.
2400   *
2401   * \param f File to save to.
2402   */
CV_SaveVariables(FILE * f)2403 void CV_SaveVariables(FILE *f)
2404 {
2405 	consvar_t *cvar;
2406 
2407 	for (cvar = consvar_vars; cvar; cvar = cvar->next)
2408 		if (cvar->flags & CV_SAVE)
2409 		{
2410 			char stringtowrite[MAXTEXTCMD+1];
2411 
2412 			const char * string;
2413 
2414 			if (cvar->revert.v.string != NULL)
2415 			{
2416 				string = cvar->revert.v.string;
2417 			}
2418 			else
2419 			{
2420 				string = cvar->string;
2421 			}
2422 
2423 			// Silly hack for Min/Max vars
2424 #define MINVAL 0
2425 #define MAXVAL 1
2426 			if (
2427 					cvar->PossibleValue != NULL &&
2428 					cvar->PossibleValue[0].strvalue &&
2429 					stricmp(cvar->PossibleValue[0].strvalue, "MIN") == 0
2430 			){ // bounded cvar
2431 				int which = stricmp(string, "MAX") == 0;
2432 
2433 				if (which || stricmp(string, "MIN") == 0)
2434 				{
2435 					INT32 value = cvar->PossibleValue[which].value;
2436 
2437 					if (cvar->flags & CV_FLOAT)
2438 						sprintf(stringtowrite, "%f", FIXED_TO_FLOAT(value));
2439 					else
2440 						sprintf(stringtowrite, "%d", value);
2441 
2442 					string = stringtowrite;
2443 				}
2444 			}
2445 #undef MINVAL
2446 #undef MAXVAL
2447 
2448 			fprintf(f, "%s \"%s\"\n", cvar->name, string);
2449 		}
2450 }
2451 
2452 //============================================================================
2453 //                            SCRIPT PARSE
2454 //============================================================================
2455 
2456 /** Parses a token out of a string. Handles script files too.
2457   *
2458   * \param data String to parse.
2459   * \return The data pointer after the token. NULL if no token found.
2460   */
COM_Parse(char * data)2461 static char *COM_Parse(char *data)
2462 {
2463 	char c;
2464 	size_t len = 0;
2465 
2466 	com_token[0] = 0;
2467 
2468 	if (data == NULL)
2469 		return NULL;
2470 
2471 	// skip whitespace
2472 skipwhite:
2473 	while ((c = *data) <= ' ')
2474 	{
2475 		if (c == '\0')
2476 			return NULL; // end of file;
2477 		data++;
2478 	}
2479 
2480 	// skip // comments
2481 	if (c == '/' && data[1] == '/')
2482 	{
2483 		while (*data && *data != '\n')
2484 			data++;
2485 		goto skipwhite;
2486 	}
2487 
2488 	// handle quoted strings specially
2489 	if (c == '\"')
2490 	{
2491 		data++;
2492 		for (;;)
2493 		{
2494 			c = *data++;
2495 			if (c == '\"' || c == '\0')
2496 			{
2497 				com_token[len] = 0;
2498 				return data;
2499 			}
2500 			if (c == '\033')
2501 				data++;
2502 			else
2503 			{
2504 				com_token[len] = c;
2505 				len++;
2506 			}
2507 		}
2508 	}
2509 
2510 	// parse a regular word
2511 	do
2512 	{
2513 		if (c == '\033')
2514 		{
2515 			do
2516 			{
2517 				data += 2;
2518 				c = *data;
2519 			}
2520 			while (c == '\033') ;
2521 		}
2522 		else
2523 		{
2524 			com_token[len] = c;
2525 			data++;
2526 			len++;
2527 			c = *data;
2528 		}
2529 	} while (c > 32);
2530 
2531 	com_token[len] = 0;
2532 	return data;
2533 }
2534