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 console.c
/// \brief Console drawing and input
#ifdef __GNUC__
#include <unistd.h>
#ifdef _XBOX
#include <openxdk/debug.h>
#endif
#endif
#include "doomdef.h"
#include "console.h"
#include "g_game.h"
#include "g_input.h"
#include "hu_stuff.h"
#include "keys.h"
#include "r_defs.h"
#include "sounds.h"
#include "st_stuff.h"
#include "s_sound.h"
#include "v_video.h"
#include "i_video.h"
#include "z_zone.h"
#include "i_system.h"
#ifdef _WINDOWS
#include "win32/win_main.h"
#endif
#ifdef HWRENDER
#include "hardware/hw_main.h"
#endif
#define MAXHUDLINES 20
#ifdef HAVE_THREADS
I_mutex con_mutex;
# define Lock_state() I_lock_mutex(&con_mutex)
# define Unlock_state() I_unlock_mutex(con_mutex)
#else/*HAVE_THREADS*/
# define Lock_state()
# define Unlock_state()
#endif/*HAVE_THREADS*/
static boolean con_started = false; // console has been initialised
boolean con_startup = false; // true at game startup, screen need refreshing
static boolean con_forcepic = true; // at startup toggle console translucency when first off
boolean con_recalc; // set true when screen size has changed
static tic_t con_tick; // console ticker for anim or blinking prompt cursor
// con_scrollup should use time (currenttime - lasttime)..
static boolean consoletoggle; // true when console key pushed, ticker will handle
static boolean consoleready; // console prompt is ready
INT32 con_destlines; // vid lines used by console at final position
static INT32 con_curlines; // vid lines currently used by console
static INT32 con_hudlines; // number of console heads up message lines
static INT32 con_hudtime[MAXHUDLINES]; // remaining time of display for hud msg lines
INT32 con_clearlines; // top screen lines to refresh when view reduced
boolean con_hudupdate; // when messages scroll, we need a backgrnd refresh
// console text output
static char *con_line; // console text output current line
static size_t con_cx; // cursor position in current line
static size_t con_cy; // cursor line number in con_buffer, is always
// increasing, and wrapped around in the text
// buffer using modulo.
static size_t con_totallines; // lines of console text into the console buffer
static size_t con_width; // columns of chars, depend on vid mode width
static size_t con_scrollup; // how many rows of text to scroll up (pgup/pgdn)
UINT32 con_scalefactor; // text size scale factor
// hold 32 last lines of input for history
#define CON_MAXPROMPTCHARS 256
static char inputlines[32][CON_MAXPROMPTCHARS]; // hold last 32 prompt lines
static INT32 inputline; // current input line number
static INT32 inputhist; // line number of history input line to restore
static size_t input_cur; // position of cursor in line
static size_t input_sel; // position of selection marker (I.E.: anything between this and input_cur is "selected")
static size_t input_len; // length of current line, used to bound cursor and such
// notice: input does NOT include the "$" at the start of the line. - 11/3/16
// protos.
static void CON_InputInit(void);
static void CON_RecalcSize(void);
static void CONS_backcolor_Change(void);
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
//======================================================================
// CONSOLE VARS AND COMMANDS
//======================================================================
#ifdef macintosh
#define CON_BUFFERSIZE 4096 // my compiler can't handle local vars >32k
#else
#define CON_BUFFERSIZE 16384
#endif
static char con_buffer[CON_BUFFERSIZE];
// how many seconds the hud messages lasts on the screen
static consvar_t cons_msgtimeout = {"con_hudtime", "5", CV_SAVE, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL};
// number of lines displayed on the HUD
static consvar_t cons_hudlines = {"con_hudlines", "5", CV_CALL|CV_SAVE, CV_Unsigned, CONS_hudlines_Change, 0, NULL, NULL, 0, 0, NULL};
// number of lines console move per frame
// (con_speed needs a limit, apparently)
static CV_PossibleValue_t speed_cons_t[] = {{0, "MIN"}, {64, "MAX"}, {0, NULL}};
static consvar_t cons_speed = {"con_speed", "8", CV_SAVE, speed_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
// percentage of screen height to use for console
static consvar_t cons_height = {"con_height", "50", CV_SAVE, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t backpic_cons_t[] = {{0, "translucent"}, {1, "picture"}, {0, NULL}};
// whether to use console background picture, or translucent mode
static consvar_t cons_backpic = {"con_backpic", "translucent", CV_SAVE, backpic_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};

Latapostrophe
committed
static CV_PossibleValue_t backcolor_cons_t[] = {{0, "White"}, {1, "Black"}, {2, "Sepia"},
{3, "Brown"}, {4, "Pink"}, {5, "Raspberry"},
{6, "Red"}, {7, "Creamsicle"}, {8, "Orange"},
{9, "Gold"}, {10,"Yellow"}, {11,"Emerald"},
{12,"Green"}, {13,"Cyan"}, {14,"Steel"},
{15,"Periwinkle"}, {16,"Blue"}, {17,"Purple"},
{18,"Lavender"},

Latapostrophe
committed
consvar_t cons_backcolor = {"con_backcolor", "Black", CV_CALL|CV_SAVE, backcolor_cons_t, CONS_backcolor_Change, 0, NULL, NULL, 0, 0, NULL};
{0, "Game type"},
{V_YELLOWMAP, "Always yellow"},
{V_PURPLEMAP, "Always purple"},
{V_GREENMAP, "Always green"},
{V_BLUEMAP, "Always blue"},
{V_REDMAP, "Always red"},
{V_GRAYMAP, "Always gray"},
{V_ORANGEMAP, "Always orange"},
{V_SKYMAP, "Always sky-blue"},
{V_GOLDMAP, "Always gold"},
{V_LAVENDERMAP, "Always lavender"},
{V_TEAMAP, "Always tea-green"},
{V_PINKMAP, "Always pink"},
{V_BROWNMAP, "Always brown"},
{V_PEACHMAP, "Always peach"},
consvar_t cons_menuhighlight = {"menuhighlight", "Game type", CV_SAVE, menuhighlight_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
static void CON_Print(char *msg);
//
//
static void CONS_hudlines_Change(void)
{
INT32 i;
// Clear the currently displayed lines
for (i = 0; i < con_hudlines; i++)
con_hudtime[i] = 0;
if (cons_hudlines.value < 1)
cons_hudlines.value = 1;
else if (cons_hudlines.value > MAXHUDLINES)
cons_hudlines.value = MAXHUDLINES;
con_hudlines = cons_hudlines.value;
CONS_Printf(M_GetText("Number of console HUD lines is now %d\n"), con_hudlines);
}
// Clear console text buffer
//
static void CONS_Clear_f(void)
{
memset(con_buffer, 0, CON_BUFFERSIZE);
con_cx = 0;
con_cy = con_totallines-1;
con_line = &con_buffer[con_cy*con_width];
con_scrollup = 0;
/*static void CONS_English_f(void)
{
shiftxform = english_shiftxform;
CONS_Printf(M_GetText("%s keymap.\n"), M_GetText("English"));
}*/
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
static char *bindtable[NUMINPUTS];
static void CONS_Bind_f(void)
{
size_t na;
INT32 key;
na = COM_Argc();
if (na != 2 && na != 3)
{
CONS_Printf(M_GetText("bind <keyname> [<command>]: create shortcut keys to command(s)\n"));
CONS_Printf("\x82%s", M_GetText("Bind table :\n"));
na = 0;
for (key = 0; key < NUMINPUTS; key++)
if (bindtable[key])
{
CONS_Printf("%s : \"%s\"\n", G_KeynumToString(key), bindtable[key]);
na = 1;
}
if (!na)
CONS_Printf(M_GetText("(empty)\n"));
return;
}
key = G_KeyStringtoNum(COM_Argv(1));
if (key <= 0 || key >= NUMINPUTS)
{
CONS_Alert(CONS_NOTICE, M_GetText("Invalid key name\n"));
return;
}
Z_Free(bindtable[key]);
bindtable[key] = NULL;
if (na == 3)
bindtable[key] = Z_StrDup(COM_Argv(2));
}
//======================================================================
// CONSOLE SETUP
//======================================================================
// Console BG color
UINT8 *consolebgmap = NULL;
void CON_SetupBackColormap(void)
UINT16 i, palsum;
UINT8 j, palindex;
UINT8 *pal = W_CacheLumpName(GetPalette(), PU_CACHE);
if (!consolebgmap)
consolebgmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
switch (cons_backcolor.value)
{

Latapostrophe
committed
case 0: palindex = 15; break; // White
case 1: palindex = 31; break; // Gray
case 2: palindex = 47; break; // Sepia
case 3: palindex = 63; break; // Brown
case 4: palindex = 150; shift = 7; break; // Pink
case 5: palindex = 127; shift = 7; break; // Raspberry
case 6: palindex = 143; break; // Red
case 7: palindex = 86; shift = 7; break; // Creamsicle
case 8: palindex = 95; break; // Orange
case 9: palindex = 119; shift = 7; break; // Gold
case 10: palindex = 111; break; // Yellow
case 11: palindex = 191; shift = 7; break; // Emerald
case 12: palindex = 175; break; // Green
case 13: palindex = 219; break; // Cyan
case 14: palindex = 207; shift = 7; break; // Steel
case 15: palindex = 230; shift = 7; break; // Periwinkle
case 16: palindex = 239; break; // Blue
case 17: palindex = 199; shift = 7; break; // Purple
case 18: palindex = 255; shift = 7; break; // Lavender
// Default green
default: palindex = 175; break;
// setup background colormap
for (i = 0, j = 0; i < 768; i += 3, j++)
palsum = (pal[i] + pal[i+1] + pal[i+2]) >> shift;
consolebgmap[j] = (UINT8)(palindex - palsum);
static void CONS_backcolor_Change(void)
CON_SetupBackColormap();
}
// Font colormap colors
// TODO: This could probably be improved somehow...
// These colormaps are 99% identical, with just a few changed bytes
UINT8 *yellowmap, *purplemap, *greenmap, *bluemap, *graymap, *redmap, *orangemap,\
*skymap, *goldmap, *lavendermap, *teamap, *steelmap, *pinkmap, *brownmap, *peachmap;
static void CON_SetupColormaps(void)
{
INT32 i;
UINT8 *memorysrc = (UINT8 *)Z_Malloc((256*15), PU_STATIC, NULL);
purplemap = memorysrc;
yellowmap = (purplemap+256);
greenmap = (yellowmap+256);
bluemap = (greenmap+256);
redmap = (bluemap+256);
graymap = (redmap+256);
orangemap = (graymap+256);
skymap = (orangemap+256);
lavendermap = (skymap+256);
goldmap = (lavendermap+256);
teamap = (goldmap+256);
steelmap = (teamap+256);
pinkmap = (steelmap+256);
brownmap = (pinkmap+256);
peachmap = (brownmap+256);
// setup the other colormaps, for console text
// these don't need to be aligned, unless you convert the
// V_DrawMappedPatch() into optimised asm.
for (i = 0; i < (256*15); i++, ++memorysrc)
purplemap[120] = (UINT8)194;
yellowmap[120] = (UINT8)103;
greenmap[120] = (UINT8)162;
bluemap[120] = (UINT8)228;
redmap[120] = (UINT8)126; // battle
graymap[120] = (UINT8)10;
orangemap[120] = (UINT8)85; // record attack
skymap[120] = (UINT8)214; // race
lavendermap[120] = (UINT8)248;
goldmap[120] = (UINT8)114;
teamap[120] = (UINT8)177;
steelmap[120] = (UINT8)201;
pinkmap[120] = (UINT8)145;
brownmap[120] = (UINT8)48;
peachmap[120] = (UINT8)69; // nice
// Init back colormap
CON_SetupBackColormap();
}
// Setup the console text buffer
//
// for WII, libogc already has a CON_Init function, we must rename it here
#ifdef _WII
void CON_InitWii(void)
#else
void CON_Init(void)
#endif
{
INT32 i;
for (i = 0; i < NUMINPUTS; i++)
bindtable[i] = NULL;
// clear all lines
memset(con_buffer, 0, CON_BUFFERSIZE);
// make sure it is ready for the loading screen
con_width = 0;
//note: CON_Ticker should always execute at least once before D_Display()
con_clipviewtop = -1; // -1 does not clip
con_hudlines = atoi(cons_hudlines.defaultvalue);
// setup console input filtering
CON_InputInit();
// register our commands
//
COM_AddCommand("cls", CONS_Clear_f);
//COM_AddCommand("english", CONS_English_f);
// set console full screen for game startup MAKE SURE VID_Init() done !!!
con_destlines = vid.height;
con_curlines = vid.height;
con_started = true;
con_startup = true; // need explicit screen refresh until we are in Doom loop
consoletoggle = false;
CV_RegisterVar(&cons_msgtimeout);
CV_RegisterVar(&cons_hudlines);
CV_RegisterVar(&cons_speed);
CV_RegisterVar(&cons_height);
CV_RegisterVar(&cons_backpic);
CV_RegisterVar(&cons_backcolor);
COM_AddCommand("bind", CONS_Bind_f);
}
else
{
con_started = true;
con_startup = false; // need explicit screen refresh until we are in Doom loop
consoletoggle = true;
}
}
// Console input initialization
//
static void CON_InputInit(void)
{
// prepare the first prompt line
memset(inputlines, 0, sizeof (inputlines));
inputline = 0;
input_cur = input_sel = input_len = 0;
}
//======================================================================
// CONSOLE EXECUTION
//======================================================================
// Called at screen size change to set the rows and line size of the
// console text buffer.
//
static void CON_RecalcSize(void)
{
size_t conw, oldcon_width, oldnumlines, i, oldcon_cy;
char *tmp_buffer;
char *string;
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
switch (cv_constextsize.value)
{
case V_NOSCALEPATCH:
con_scalefactor = 1;
break;
case V_SMALLSCALEPATCH:
con_scalefactor = vid.smalldupx;
break;
case V_MEDSCALEPATCH:
con_scalefactor = vid.meddupx;
break;
default: // Full scaling
con_scalefactor = vid.dupx;
break;
}
con_recalc = false;
if (dedicated)
conw = 1;
else
conw = (vid.width>>3) / con_scalefactor - 2;
if (con_curlines == vid.height) // first init
{
con_curlines = vid.height;
con_destlines = vid.height;
}
if (con_destlines > 0) // Resize console if already open
{
CON_ChangeHeight();
con_curlines = con_destlines;
}
// check for change of video width
if (conw == con_width)
tmp_buffer = Z_Malloc(CON_BUFFERSIZE, PU_STATIC, NULL);
string = Z_Malloc(CON_BUFFERSIZE, PU_STATIC, NULL); // BP: it is a line but who know
oldcon_width = con_width;
oldnumlines = con_totallines;
oldcon_cy = con_cy;
M_Memcpy(tmp_buffer, con_buffer, CON_BUFFERSIZE);
if (conw < 1)
con_width = (BASEVIDWIDTH>>3) - 2;
else
con_width = conw;
con_width += 11; // Graue 06-19-2004 up to 11 control chars per line
con_totallines = CON_BUFFERSIZE / con_width;
memset(con_buffer, ' ', CON_BUFFERSIZE);
con_cx = 0;
con_cy = con_totallines-1;
con_line = &con_buffer[con_cy*con_width];
con_scrollup = 0;
// re-arrange console text buffer to keep text
if (oldcon_width) // not the first time
{
for (i = oldcon_cy + 1; i < oldcon_cy + oldnumlines; i++)
{
if (tmp_buffer[(i%oldnumlines)*oldcon_width])
{
M_Memcpy(string, &tmp_buffer[(i%oldnumlines)*oldcon_width], oldcon_width);
conw = oldcon_width - 1;
while (string[conw] == ' ' && conw)
conw--;
string[conw+1] = '\n';
string[conw+2] = '\0';
CON_Print(string);
}
}
}
Z_Free(string);
Z_Free(tmp_buffer);
}
static void CON_ChangeHeight(void)
{
INT32 minheight;
Lock_state();
minheight = 20 * con_scalefactor; // 20 = 8+8+4
// toggle console in
con_destlines = (cons_height.value*vid.height)/100;
if (con_destlines < minheight)
con_destlines = minheight;
else if (con_destlines > vid.height)
con_destlines = vid.height;
con_destlines &= ~0x3; // multiple of text row height
// Handles Console moves in/out of screen (per frame)
//
static void CON_MoveConsole(void)
{
fixed_t conspeed;
Lock_state();
conspeed = FixedDiv(cons_speed.value*vid.fdupy, FRACUNIT);
// instant
if (!cons_speed.value)
{
con_curlines = con_destlines;
return;
}
// up/down move to dest
if (con_curlines < con_destlines)
{
con_curlines += FixedInt(conspeed);
if (con_curlines > con_destlines)
con_curlines = con_destlines;
}
else if (con_curlines > con_destlines)
{
con_curlines -= FixedInt(conspeed);
if (con_curlines < con_destlines)
con_curlines = con_destlines;
}
INT32 CON_ShiftChar(INT32 ch)
{
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))
{
if (shiftdown ^ capslock)
ch = shiftxform[ch];
}
else // if we're holding shift we should still shift non letter symbols
{
if (shiftdown)
ch = shiftxform[ch];
}
return ch;
}
// Clear time of console heads up messages
//
void CON_ClearHUD(void)
{
INT32 i;
for (i = 0; i < con_hudlines; i++)
con_hudtime[i] = 0;
}
// Force console to move out immediately
// note: con_ticker will set consoleready false
void CON_ToggleOff(void)
{
con_destlines = 0;
con_curlines = 0;
CON_ClearHUD();
con_forcepic = 0;
con_clipviewtop = -1; // remove console clipping of view
boolean ready;
Lock_state();
{
ready = consoleready;
}
Unlock_state();
return ready;
}
// Console ticker: handles console move in/out, cursor blinking
//
void CON_Ticker(void)
{
INT32 i;
INT32 minheight;
Lock_state();
minheight = 20 * con_scalefactor; // 20 = 8+8+4
// cursor blinking
con_tick++;
con_tick &= 7;
// if the menu is open then close the console.
if (menuactive && con_destlines)
{
consoletoggle = false;
con_destlines = 0;
CON_ClearHUD();
}
// console key was pushed
if (consoletoggle)
{
consoletoggle = false;
// toggle off console
if (con_destlines > 0)
{
con_destlines = 0;
CON_ClearHUD();
}
else
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
}
// console movement
if (con_destlines != con_curlines)
CON_MoveConsole();
// clip the view, so that the part under the console is not drawn
con_clipviewtop = -1;
if (cons_backpic.value) // clip only when using an opaque background
{
if (con_curlines > 0)
con_clipviewtop = con_curlines - viewwindowy - 1 - 10;
// NOTE: BIG HACK::SUBTRACT 10, SO THAT WATER DON'T COPY LINES OF THE CONSOLE
// WINDOW!!! (draw some more lines behind the bottom of the console)
if (con_clipviewtop < 0)
con_clipviewtop = -1; // maybe not necessary, provided it's < 0
}
// check if console ready for prompt
if (con_destlines >= minheight)
consoleready = true;
else
consoleready = false;
// make overlay messages disappear after a while
for (i = 0; i < con_hudlines; i++)
{
con_hudtime[i]--;
if (con_hudtime[i] < 0)
con_hudtime[i] = 0;
}
//
// ----
//
// Shortcuts for adding and deleting characters, strings, and sections
// Necessary due to moving cursor
//
memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS);
input_cur = input_sel = input_len = 0;
static void CON_InputSetString(const char *c)
memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS);
strcpy(inputlines[inputline], c);
input_cur = input_sel = input_len = strlen(c);
static void CON_InputAddString(const char *c)
if (input_len + csize > CON_MAXPROMPTCHARS-1)
if (input_cur != input_len)
memmove(&inputlines[inputline][input_cur+csize], &inputlines[inputline][input_cur], input_len-input_cur);
memcpy(&inputlines[inputline][input_cur], c, csize);
input_len += csize;
input_sel = (input_cur += csize);
if (input_cur > input_sel)
{
start = input_sel;
end = input_cur;
}
else
{
start = input_cur;
end = input_sel;
}
len = (end - start);
if (end != input_len)
memmove(&inputlines[inputline][start], &inputlines[inputline][end], input_len-end);
memset(&inputlines[inputline][input_len - len], 0, len);
input_len -= len;
input_sel = input_cur = start;
{
if (input_len >= CON_MAXPROMPTCHARS-1)
return;
if (input_cur != input_len)
memmove(&inputlines[inputline][input_cur+1], &inputlines[inputline][input_cur], input_len-input_cur);
inputlines[inputline][input_cur++] = c;
inputlines[inputline][++input_len] = 0;
input_sel = input_cur;
{
if (!input_cur)
return;
if (input_cur != input_len)
memmove(&inputlines[inputline][input_cur-1], &inputlines[inputline][input_cur], input_len-input_cur);
inputlines[inputline][--input_len] = 0;
input_sel = --input_cur;
}
//
// ----
//
// Handles console key input
//
boolean CON_Responder(event_t *ev)
{
static UINT8 consdown = false; // console is treated differently due to rare usage
// sequential completions a la 4dos
static char completion[80];
static INT32 comskips, varskips;
const char *cmd = "";
INT32 key;
if (chat_on)
return false;
// let go keyup events, don't eat them
if (ev->type != ev_keydown && ev->type != ev_console)
{
if (ev->data1 == gamecontrol[gc_console][0] || ev->data1 == gamecontrol[gc_console][1])
consdown = false;
return false;
}
key = ev->data1;
// check for console toggle key
if (ev->type != ev_console)
{
if (modeattacking || metalrecording)
return false;
for (i = 0; i < num_gamecontrols; i++)
{
if (gamecontrol[i][0] == ev->data1 || gamecontrol[i][1] == ev->data1)
break;
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
if (key == gamecontrol[gc_console][0] || key == gamecontrol[gc_console][1])
{
if (consdown) // ignore repeat
return true;
consoletoggle = true;
consdown = true;
return true;
}
// check other keys only if console prompt is active
if (!consoleready && key < NUMINPUTS) // metzgermeister: boundary check!!
{
if (bindtable[key])
{
COM_BufAddText(bindtable[key]);
COM_BufAddText("\n");
return true;
}
return false;
}
// escape key toggle off console
if (key == KEY_ESCAPE)
{
consoletoggle = true;
return true;
}
}
// Always eat ctrl/shift/alt if console open, so the menu doesn't get ideas
if (key == KEY_LSHIFT || key == KEY_RSHIFT
|| key == KEY_LCTRL || key == KEY_RCTRL
|| key == KEY_LALT || key == KEY_RALT)
return true;
// ctrl modifier -- changes behavior, adds shortcuts
if (ctrldown)
{
// show all cvars/commands that match what we have inputted
if (!completion[0])
{
if (!input_len || input_len >= 40 || strchr(inputlines[inputline], ' '))
return true;
strcpy(completion, inputlines[inputline]);
comskips = varskips = 0;
}
len = strlen(completion);
//first check commands
CONS_Printf("\nCommands:\n");
for (i = 0, cmd = COM_CompleteCommand(completion, i); cmd; cmd = COM_CompleteCommand(completion, ++i))
CONS_Printf(" \x83" "%s" "\x80" "%s\n", completion, cmd+len);
if (i == 0) CONS_Printf(" (none)\n");
//now we move on to CVARs
CONS_Printf("Variables:\n");
for (i = 0, cmd = CV_CompleteVar(completion, i); cmd; cmd = CV_CompleteVar(completion, ++i))
CONS_Printf(" \x83" "%s" "\x80" "%s\n", completion, cmd+len);
if (i == 0) CONS_Printf(" (none)\n");