Newer
Older
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file command.c
/// \brief Parse and execute commands from console input/scripts and remote server
///
/// Handles console variables, which is a simplified version
/// of commands, each consvar can have a function called when
/// it is modified.. thus it acts nearly as commands.
///
/// code shamelessly inspired by the QuakeC sources, thanks Id :)
#include "doomdef.h"
#include "doomstat.h"
#include "command.h"
#include "console.h"
#include "z_zone.h"
#include "m_menu.h"
#include "m_misc.h"
#include "m_fixed.h"
#include "m_argv.h"
#include "byteptr.h"
#include "p_saveg.h"
#include "g_game.h" // for player_names
#include "m_cond.h" // for encore mode
#include "d_netcmd.h"
#include "hu_stuff.h"
#include "p_setup.h"
#include "lua_script.h"
//========
// protos.
//========
static boolean COM_Exists(const char *com_name);
static void COM_ExecuteString(char *com_text);
static void COM_Alias_f(void);
static void COM_Echo_f(void);
static void COM_CEcho_f(void);
static void COM_CEchoFlags_f(void);
static void COM_CEchoDuration_f(void);
static void COM_Exec_f(void);
static void COM_Wait_f(void);
static void COM_Help_f(void);
static void COM_Toggle_f(void);
static void CV_EnforceExecVersion(void);
Marco Z
committed
static boolean CV_FilterVarByVersion(consvar_t *v, const char *valstr);
static const char *CV_StringValue(const char *var_name);
static consvar_t *consvar_vars; // list of registered console variables
static char com_token[1024];
static char *COM_Parse(char *data);
CV_PossibleValue_t CV_OnOff[] = {{0, "Off"}, {1, "On"}, {0, NULL}};
CV_PossibleValue_t CV_YesNo[] = {{0, "No"}, {1, "Yes"}, {0, NULL}};
CV_PossibleValue_t CV_Unsigned[] = {{0, "MIN"}, {999999999, "MAX"}, {0, NULL}};
CV_PossibleValue_t CV_Natural[] = {{1, "MIN"}, {999999999, "MAX"}, {0, NULL}};
{0, "Easy"}, {1, "Normal"}, {2, "Hard"},
// Filter consvars by EXECVERSION
// First implementation is 2 (1.0.2), so earlier configs default at 1 (1.0.0)
Marco Z
committed
// Also set CV_HIDEN during runtime, after config is loaded
static boolean execversion_enabled = false;
consvar_t cv_execversion = {"execversion","1",CV_CALL,CV_Unsigned, CV_EnforceExecVersion, 0, NULL, NULL, 0, 0, NULL};
Marco Z
committed
// for default joyaxis detection
static boolean joyaxis_default[4] = {false,false,false,false};
static INT32 joyaxis_count[4] = {0,0,0,0};
#endif
Marco Z
committed
#define COM_BUF_SIZE 0x4000 // command buffer size, 0x4000 = 16384
Monster Iestyn
committed
#define MAX_ALIAS_RECURSION 100 // max recursion allowed for aliases
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
static INT32 com_wait; // one command per frame (for cmd sequences)
// command aliases
//
typedef struct cmdalias_s
{
struct cmdalias_s *next;
char *name;
char *value; // the command string to replace the alias
} cmdalias_t;
static cmdalias_t *com_alias; // aliases list
// =========================================================================
// COMMAND BUFFER
// =========================================================================
static vsbuf_t com_text; // variable sized buffer
/** Adds text into the command buffer for later execution.
*
* \param ptext The text to add.
* \sa COM_BufInsertText
*/
void COM_BufAddText(const char *ptext)
{
size_t l;
l = strlen(ptext);
if (com_text.cursize + l >= com_text.maxsize)
{
CONS_Alert(CONS_WARNING, M_GetText("Command buffer full!\n"));
return;
}
VS_Write(&com_text, ptext, l);
}
/** Adds command text and executes it immediately.
*
* \param ptext The text to execute. A newline is automatically added.
* \sa COM_BufAddText
*/
void COM_BufInsertText(const char *ptext)
{
char *temp = NULL;
size_t templen;
// copy off any commands still remaining in the exec buffer
templen = com_text.cursize;
if (templen)
{
temp = M_Memcpy(ZZ_Alloc(templen), com_text.data, templen);
VS_Clear(&com_text);
}
// add the entire text of the file (or alias)
COM_BufAddText(ptext);
COM_BufExecute(); // do it right away
// add the copied off data
if (templen)
{
VS_Write(&com_text, temp, templen);
Z_Free(temp);
}
}
/** Progress the wait timer and flush waiting console commands when ready.
*/
void
COM_BufTicker(void)
{
if (com_wait)
{
com_wait--;
return;
}
COM_BufExecute();
}
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
/** Flushes (executes) console commands in the buffer.
*/
void COM_BufExecute(void)
{
size_t i;
char *ptext;
char line[1024] = "";
INT32 quotes;
while (com_text.cursize)
{
// find a '\n' or; line break
ptext = (char *)com_text.data;
quotes = 0;
for (i = 0; i < com_text.cursize; i++)
{
if (ptext[i] == '\"' && !quotes && i > 0 && ptext[i-1] != ' ') // Malformed command
break;
if (ptext[i] == '\"')
quotes++;
if (!(quotes & 1) && ptext[i] == ';')
break; // don't break if inside a quoted string
if (ptext[i] == '\n' || ptext[i] == '\r')
break;
}
M_Memcpy(line, ptext, i);
line[i] = 0;
// flush the command text from the command buffer, _BEFORE_
// executing, to avoid that 'recursive' aliases overflow the
// command text buffer, in that case, new commands are inserted
// at the beginning, in place of the actual, so it doesn't
// overflow
if (i == com_text.cursize)
// the last command was just flushed
com_text.cursize = 0;
else
{
i++;
com_text.cursize -= i;
//memcpy(ptext, ptext+i, com_text.cursize); // Use memmove if the memory areas do overlap.
memmove(ptext, ptext+i, com_text.cursize);
}
// execute the command line
COM_ExecuteString(line);
// delay following commands if a wait was encountered
if (com_wait)
{
com_wait--;
break;
}
}
}
/** Executes a string immediately. Used for skirting around WAIT commands.
*/
void COM_ImmedExecute(const char *ptext)
{
size_t i = 0, j = 0;
char line[1024] = "";
INT32 quotes;
while (i < strlen(ptext))
{
quotes = 0;
for (j = 0; i < strlen(ptext); i++,j++)
{
if (ptext[i] == '\"' && !quotes && i > 0 && ptext[i-1] != ' ') // Malformed command
return;
if (ptext[i] == '\"')
quotes++;
// don't break if inside a quoted string
if ((!(quotes & 1) && ptext[i] == ';') || ptext[i] == '\n' || ptext[i] == '\r')
break;
}
memcpy(line, ptext+(i-j), j);
line[j] = 0;
// execute the command line
COM_ExecuteString(line);
i++; // move to next character
}
}
// =========================================================================
// COMMAND EXECUTION
// =========================================================================
typedef struct xcommand_s
{
const char *name;
struct xcommand_s *next;
com_func_t function;
} xcommand_t;
static xcommand_t *com_commands = NULL; // current commands
#define MAX_ARGS 80
static size_t com_argc;
static char *com_argv[MAX_ARGS];
static const char *com_null_string = "";
static char *com_args = NULL; // current command args or NULL
static void Got_NetVar(UINT8 **p, INT32 playernum);
/** Initializes command buffer and adds basic commands.
*/
void COM_Init(void)
{
// allocate command buffer
VS_Alloc(&com_text, COM_BUF_SIZE);
// add standard commands
COM_AddCommand("alias", COM_Alias_f);
COM_AddCommand("echo", COM_Echo_f);
COM_AddCommand("cecho", COM_CEcho_f);
COM_AddCommand("cechoflags", COM_CEchoFlags_f);
COM_AddCommand("cechoduration", COM_CEchoDuration_f);
COM_AddCommand("exec", COM_Exec_f);
COM_AddCommand("wait", COM_Wait_f);
COM_AddCommand("help", COM_Help_f);
COM_AddCommand("toggle", COM_Toggle_f);
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
RegisterNetXCmd(XD_NETVAR, Got_NetVar);
}
/** Gets a console command argument count.
*
* \return Number of arguments for the last command.
* \sa COM_Argv
*/
size_t COM_Argc(void)
{
return com_argc;
}
/** Gets a console command argument.
*
* \param arg Index of the argument (0 to COM_Argc() - 1).
* \return String pointer to the indicated argument.
* \sa COM_Argc, COM_Args
*/
const char *COM_Argv(size_t arg)
{
if (arg >= com_argc || (signed)arg < 0)
return com_null_string;
return com_argv[arg];
}
/** Gets all console command arguments.
*
* \return String pointer to all arguments for the last command.
* \sa COM_Argv
*/
char *COM_Args(void)
{
return com_args;
}
/** Checks if a parameter was passed to a console command.
*
* \param check The parameter to look for, e.g. "-noerror".
* \return The index of the argument containing the parameter,
* or 0 if the parameter was not found.
*/
size_t COM_CheckParm(const char *check)
{
size_t i;
for (i = 1; i < com_argc; i++)
if (!strcasecmp(check, com_argv[i]))
return i;
return 0;
}
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
/** \brief COM_CheckParm, but checks only the start of each argument.
* E.g. checking for "-no" would match "-noerror" too.
*/
size_t COM_CheckPartialParm(const char *check)
{
int len;
size_t i;
len = strlen(check);
for (i = 1; i < com_argc; i++)
{
if (strncasecmp(check, com_argv[i], len) == 0)
return i;
}
return 0;
}
/** Find the first argument that starts with a hyphen (-).
* \return The index of the argument, or 0
* if there are no such arguments.
*/
size_t COM_FirstOption(void)
{
size_t i;
for (i = 1; i < com_argc; i++)
{
if (com_argv[i][0] == '-')/* options start with a hyphen */
return i;
}
return 0;
}
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
/** Parses a string into command-line tokens.
*
* \param ptext A null-terminated string. Does not need to be
* newline-terminated.
*/
static void COM_TokenizeString(char *ptext)
{
size_t i;
// Clear the args from the last string.
for (i = 0; i < com_argc; i++)
Z_Free(com_argv[i]);
com_argc = 0;
com_args = NULL;
while (com_argc < MAX_ARGS)
{
// Skip whitespace up to a newline.
while (*ptext != '\0' && *ptext <= ' ' && *ptext != '\n')
ptext++;
// A newline means end of command in buffer,
// thus end of this command's args too.
if (*ptext == '\n' || *ptext == '\0')
break;
if (com_argc == 1)
com_args = ptext;
ptext = COM_Parse(ptext);
if (ptext == NULL)
break;
com_argv[com_argc] = Z_StrDup(com_token);
com_argc++;
}
}
/** Adds a console command.
*
* \param name Name of the command.
* \param func Function called when the command is run.
*/
void COM_AddCommand(const char *name, com_func_t func)
{
xcommand_t *cmd;
// fail if the command is a variable name
if (CV_StringValue(name)[0] != '\0')
{
I_Error("%s is a variable name\n", name);
return;
}
// fail if the command already exists
for (cmd = com_commands; cmd; cmd = cmd->next)
{
if (!stricmp(name, cmd->name)) //case insensitive now that we have lower and uppercase!
{
// don't I_Error for Lua commands
// Lua commands can replace game commands, and they have priority.
// BUT, if for some reason we screwed up and made two console commands with the same name,
// it's good to have this here so we find out.
if (cmd->function != COM_Lua_f)
return;
}
}
cmd = ZZ_Alloc(sizeof *cmd);
cmd->name = name;
cmd->function = func;
cmd->next = com_commands;
com_commands = cmd;
}
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
/** Adds a console command for Lua.
* No I_Errors allowed; return a negative code instead.
*
* \param name Name of the command.
*/
int COM_AddLuaCommand(const char *name)
{
xcommand_t *cmd;
// fail if the command is a variable name
if (CV_StringValue(name)[0] != '\0')
return -1;
// command already exists
for (cmd = com_commands; cmd; cmd = cmd->next)
{
if (!stricmp(name, cmd->name)) //case insensitive now that we have lower and uppercase!
{
// replace the built in command.
cmd->function = COM_Lua_f;
return 1;
}
}
// Add a new command.
cmd = ZZ_Alloc(sizeof *cmd);
cmd->name = name;
cmd->function = COM_Lua_f;
cmd->next = com_commands;
com_commands = cmd;
return 0;
}
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
/** Tests if a command exists.
*
* \param com_name Name to test for.
* \return True if a command by the given name exists.
*/
static boolean COM_Exists(const char *com_name)
{
xcommand_t *cmd;
for (cmd = com_commands; cmd; cmd = cmd->next)
if (!stricmp(com_name, cmd->name))
return true;
return false;
}
/** Does command completion for the console.
*
* \param partial The partial name of the command (potentially).
* \param skips Number of commands to skip.
* \return The complete command name, or NULL.
* \sa CV_CompleteVar
*/
const char *COM_CompleteCommand(const char *partial, INT32 skips)
{
xcommand_t *cmd;
size_t len;
len = strlen(partial);
if (!len)
return NULL;
// check functions
for (cmd = com_commands; cmd; cmd = cmd->next)
if (!strncmp(partial, cmd->name, len))
if (!skips--)
return cmd->name;
return NULL;
}
/** Parses a single line of text into arguments and tries to execute it.
* The text can come from the command buffer, a remote client, or stdin.
*
* \param ptext A single line of text.
*/
static void COM_ExecuteString(char *ptext)
{
xcommand_t *cmd;
cmdalias_t *a;
Monster Iestyn
committed
static INT32 recursion = 0; // detects recursion and stops it if it goes too far
COM_TokenizeString(ptext);
// execute the command line
if (COM_Argc() == 0)
return; // no tokens
// check functions
for (cmd = com_commands; cmd; cmd = cmd->next)
{
if (!stricmp(com_argv[0], cmd->name)) //case insensitive now that we have lower and uppercase!
{
cmd->function();
return;
}
}
// check aliases
for (a = com_alias; a; a = a->next)
{
if (!stricmp(com_argv[0], a->name))
{
Monster Iestyn
committed
if (recursion > MAX_ALIAS_RECURSION)
CONS_Alert(CONS_WARNING, M_GetText("Alias recursion cycle detected!\n"));
else
{
char buf[1024];
char *write = buf, *read = a->value, *seek = read;
if (*seek >= '1' && *seek <= '9')
{
if (com_argc > (size_t)(*seek - '0'))
memcpy(write, com_argv[*seek - '0'], strlen(com_argv[*seek - '0']));
write += strlen(com_argv[*seek - '0']);
Monster Iestyn
committed
// Monster Iestyn: keep track of how many levels of recursion we're in
recursion++;
COM_BufInsertText(buf);
Monster Iestyn
committed
recursion--;
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
return;
}
}
// check cvars
// Hurdler: added at Ebola's request ;)
// (don't flood the console in software mode with bad gr_xxx command)
if (!CV_Command() && con_destlines)
CONS_Printf(M_GetText("Unknown command '%s'\n"), COM_Argv(0));
}
// =========================================================================
// SCRIPT COMMANDS
// =========================================================================
/** Creates a command name that replaces another command.
*/
static void COM_Alias_f(void)
{
cmdalias_t *a;
if (COM_Argc() < 3)
{
CONS_Printf(M_GetText("alias <name> <command>: create a shortcut command that executes other command(s)\n"));
return;
}
a = ZZ_Alloc(sizeof *a);
a->next = com_alias;
com_alias = a;
a->name = Z_StrDup(COM_Argv(1));
// Just use arg 2 if it's the only other argument, in case the alias is wrapped in quotes (backward compat, or multiple commands in one string).
// Otherwise pull the whole string and seek to the end of the alias name. The strctr is in case the alias is quoted.
a->value = Z_StrDup(COM_Argc() == 3 ? COM_Argv(2) : (strchr(COM_Args() + strlen(a->name), ' ') + 1));
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
}
/** Prints a line of text to the console.
*/
static void COM_Echo_f(void)
{
size_t i;
for (i = 1; i < COM_Argc(); i++)
CONS_Printf("%s ", COM_Argv(i));
CONS_Printf("\n");
}
/** Displays text on the center of the screen for a short time.
*/
static void COM_CEcho_f(void)
{
size_t i;
char cechotext[1024] = "";
for (i = 1; i < COM_Argc(); i++)
{
strncat(cechotext, COM_Argv(i), sizeof(cechotext)-1);
strncat(cechotext, " ", sizeof(cechotext)-1);
}
cechotext[sizeof(cechotext) - 1] = '\0';
HU_DoCEcho(cechotext);
}
/** Sets drawing flags for the CECHO command.
*/
static void COM_CEchoFlags_f(void)
{
if (COM_Argc() > 1)
{
const char *arg = COM_Argv(1);
if (arg[0] && arg[0] == '0' &&
arg[1] && arg[1] == 'x') // Use hexadecimal!
HU_SetCEchoFlags(axtoi(arg+2));
else
HU_SetCEchoFlags(atoi(arg));
}
else
CONS_Printf(M_GetText("cechoflags <flags>: set CEcho flags, prepend with 0x to use hexadecimal\n"));
}
/** Sets the duration for CECHO commands to stay on the screen
*/
static void COM_CEchoDuration_f(void)
{
if (COM_Argc() > 1)
HU_SetCEchoDuration(atoi(COM_Argv(1)));
}
/** Executes a script file.
*/
static void COM_Exec_f(void)
{
UINT8 *buf = NULL;
if (COM_Argc() < 2 || COM_Argc() > 3)
{
CONS_Printf(M_GetText("exec <filename>: run a script file\n"));
return;
}
// load file
// Try with Argv passed verbatim first, for back compat
FIL_ReadFile(COM_Argv(1), &buf);
if (!buf)
{
// Now try by searching the file path
// filename is modified with the full found path
strcpy(filename, COM_Argv(1));
if (findfile(filename, NULL, true) != FS_NOTFOUND)
FIL_ReadFile(filename, &buf);
if (!buf)
{
if (!COM_CheckParm("-noerror"))
CONS_Printf(M_GetText("couldn't execute file %s\n"), COM_Argv(1));
return;
}
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
}
if (!COM_CheckParm("-silent"))
CONS_Printf(M_GetText("executing %s\n"), COM_Argv(1));
// insert text file into the command buffer
COM_BufAddText((char *)buf);
COM_BufAddText("\n");
// free buffer
Z_Free(buf);
}
/** Delays execution of the rest of the commands until the next frame.
* Allows sequences of commands like "jump; fire; backward".
*/
static void COM_Wait_f(void)
{
if (COM_Argc() > 1)
com_wait = atoi(COM_Argv(1));
else
com_wait = 1; // 1 frame
}
/** Prints help on variables and commands.
*/
static void COM_Help_f(void)
{
xcommand_t *cmd;
consvar_t *cvar;
INT32 i = 0;
if (COM_Argc() > 1)
{
const char *help = COM_Argv(1);
cvar = CV_FindVar(help);
CONS_Printf("\x82""Variable %s:\n", cvar->name);
CONS_Printf(M_GetText(" flags :"));
if (cvar->flags & CV_SAVE)
CONS_Printf("AUTOSAVE ");
if (cvar->flags & CV_FLOAT)
CONS_Printf("FLOAT ");
if (cvar->flags & CV_NETVAR)
CONS_Printf("NETVAR ");
if (cvar->flags & CV_CALL)
CONS_Printf("ACTION ");
if (cvar->flags & CV_CHEAT)
CONS_Printf("CHEAT ");
CONS_Printf("\n");
if (cvar->PossibleValue)
{
if (!stricmp(cvar->PossibleValue[0].strvalue, "MIN") && !stricmp(cvar->PossibleValue[1].strvalue, "MAX"))
CONS_Printf(" range from %d to %d\n", cvar->PossibleValue[0].value,
cvar->PossibleValue[1].value);
i = 2;
//CONS_Printf(M_GetText(" possible value : %s\n"), cvar->name);
CONS_Printf(" %-2d : %s\n", cvar->PossibleValue[i].value,
cvar->PossibleValue[i].strvalue);
if (cvar->PossibleValue[i].value == cvar->value)
cvalue = cvar->PossibleValue[i].strvalue;
i++;
}
if (cvalue)
CONS_Printf(" Current value: %s\n", cvalue);
CONS_Printf(" Current value: %d\n", cvar->value);
CONS_Printf(" Current value: %d\n", cvar->value);
{
for (cmd = com_commands; cmd; cmd = cmd->next)
{
if (strcmp(cmd->name, help))
continue;
CONS_Printf("\x82""Command %s:\n", cmd->name);
CONS_Printf(" help is not available for commands");
CONS_Printf("\x82""\nCheck wiki.srb2.org for more or try typing <name> without arguments\n");
return;
}
CONS_Printf("No exact match, searching...\n");
// commands
CONS_Printf("\x82""Commands:\n");
for (cmd = com_commands; cmd; cmd = cmd->next)
{
if (!strstr(cmd->name, help))
continue;
CONS_Printf("%s ",cmd->name);
i++;
}
// variables
CONS_Printf("\x82""\nVariables:\n");
for (cvar = consvar_vars; cvar; cvar = cvar->next)
{
if ((cvar->flags & CV_NOSHOWHELP) || (!strstr(cvar->name, help)))
continue;
CONS_Printf("%s ", cvar->name);
i++;
}
CONS_Printf("\x82""\nCheck wiki.srb2.org for more or type help <command or variable>\n");
CONS_Debug(DBG_GAMELOGIC, "\x87Total : %d\n", i);
}
return;
CONS_Printf("\x82""Commands:\n");
for (cmd = com_commands; cmd; cmd = cmd->next)
{
CONS_Printf("%s ",cmd->name);
i++;
}
// variables
CONS_Printf("\x82""\nVariables:\n");
for (cvar = consvar_vars; cvar; cvar = cvar->next)
{
if (cvar->flags & CV_NOSHOWHELP)
continue;
CONS_Printf("%s ", cvar->name);
CONS_Printf("\x82""\nCheck wiki.srb2.org for more or type help <command or variable>\n");
CONS_Debug(DBG_GAMELOGIC, "\x82Total : %d\n", i);
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
}
}
/** Toggles a console variable. Useful for on/off values.
*
* This works on on/off, yes/no values only
*/
static void COM_Toggle_f(void)
{
consvar_t *cvar;
if (COM_Argc() != 2)
{
CONS_Printf(M_GetText("Toggle <cvar_name>: Toggle the value of a cvar\n"));
return;
}
cvar = CV_FindVar(COM_Argv(1));
if (!cvar)
{
CONS_Alert(CONS_NOTICE, M_GetText("%s is not a cvar\n"), COM_Argv(1));
return;
}
if (!(cvar->PossibleValue == CV_YesNo || cvar->PossibleValue == CV_OnOff))
{
CONS_Alert(CONS_NOTICE, M_GetText("%s is not a boolean value\n"), COM_Argv(1));
return;
}
// netcvar don't change imediately
cvar->flags |= CV_SHOWMODIFONETIME;
CV_AddValue(cvar, +1);
}
/** Command variant of CV_AddValue
*/
static void COM_Add_f(void)
{
consvar_t *cvar;
if (COM_Argc() != 3)
{
CONS_Printf(M_GetText("Add <cvar_name> <value>: Add to the value of a cvar. Negative values work too!\n"));
return;
}
cvar = CV_FindVar(COM_Argv(1));
if (!cvar)
{
CONS_Alert(CONS_NOTICE, M_GetText("%s is not a cvar\n"), COM_Argv(1));
return;
}
if (( cvar->flags & CV_FLOAT ))
CV_Set(cvar, va("%f", FIXED_TO_FLOAT (cvar->value) + atof(COM_Argv(2))));
else
CV_AddValue(cvar, atoi(COM_Argv(2)));
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// =========================================================================
// VARIABLE SIZE BUFFERS
// =========================================================================
/** Initializes a variable size buffer.
*
* \param buf Buffer to initialize.
* \param initsize Initial size for the buffer.
*/
void VS_Alloc(vsbuf_t *buf, size_t initsize)
{
#define VSBUFMINSIZE 256
if (initsize < VSBUFMINSIZE)
initsize = VSBUFMINSIZE;
buf->data = Z_Malloc(initsize, PU_STATIC, NULL);
buf->maxsize = initsize;
buf->cursize = 0;
#undef VSBUFMINSIZE
}
/** Frees a variable size buffer.
*
* \param buf Buffer to free.
*/
void VS_Free(vsbuf_t *buf)
{
buf->cursize = 0;
}
/** Clears a variable size buffer.
*
* \param buf Buffer to clear.
*/
void VS_Clear(vsbuf_t *buf)
{
buf->cursize = 0;
}
/** Makes sure a variable size buffer has enough space for data of a
* certain length.
*
* \param buf The buffer. It is enlarged if necessary.
* \param length The length of data we need to add.
* \return Pointer to where the new data can go.
*/
void *VS_GetSpace(vsbuf_t *buf, size_t length)
{
void *data;
if (buf->cursize + length > buf->maxsize)
{
if (!buf->allowoverflow)
I_Error("overflow 111");
if (length > buf->maxsize)
I_Error("overflow l%s 112", sizeu1(length));
buf->overflowed = true;
CONS_Printf("VS buffer overflow");
VS_Clear(buf);