2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 #if !defined(WIN32) || defined(__MINGW32__)
30 float con_cursorspeed = 4;
32 #define CON_TEXTSIZE 131072
34 // total lines in console scrollback
36 // lines up from bottom to display
38 // where next message will be printed
40 // offset in current line for next print
42 char con_text[CON_TEXTSIZE];
44 cvar_t con_notifytime = {CVAR_SAVE, "con_notifytime","3", "how long notify lines last, in seconds"};
45 cvar_t con_notify = {CVAR_SAVE, "con_notify","4", "how many notify lines to show (0-32)"};
46 cvar_t con_textsize = {CVAR_SAVE, "con_textsize","8", "console text size in virtual 2D pixels"}; //[515]: console text size in pixels
48 #define MAX_NOTIFYLINES 32
49 // cl.time time the line was generated for transparent notify lines
50 float con_times[MAX_NOTIFYLINES];
54 qboolean con_initialized;
56 // used for server replies to rcon command
57 qboolean rcon_redirect = false;
58 int rcon_redirect_bufferpos = 0;
59 char rcon_redirect_buffer[1400];
63 ==============================================================================
67 ==============================================================================
70 cvar_t log_file = {0, "log_file","", "filename to log messages to"};
71 char crt_log_file [MAX_OSPATH] = "";
72 qfile_t* logfile = NULL;
74 unsigned char* logqueue = NULL;
78 void Log_ConPrint (const char *msg);
85 const char* Log_Timestamp (const char *desc)
87 static char timestamp [128];
89 const struct tm *crt_tm;
92 // Build the time stamp (ex: "Wed Jun 30 21:49:08 1993");
94 crt_tm = localtime (&crt_time);
95 strftime (timestring, sizeof (timestring), "%a %b %d %H:%M:%S %Y", crt_tm);
98 dpsnprintf (timestamp, sizeof (timestamp), "====== %s (%s) ======\n", desc, timestring);
100 dpsnprintf (timestamp, sizeof (timestamp), "====== %s ======\n", timestring);
113 if (logfile != NULL || log_file.string[0] == '\0')
116 logfile = FS_Open (log_file.string, "ab", false, false);
119 strlcpy (crt_log_file, log_file.string, sizeof (crt_log_file));
120 FS_Print (logfile, Log_Timestamp ("Log started"));
130 void Log_Close (void)
135 FS_Print (logfile, Log_Timestamp ("Log stopped"));
136 FS_Print (logfile, "\n");
140 crt_log_file[0] = '\0';
149 void Log_Start (void)
153 // Dump the contents of the log queue into the log file and free it
154 if (logqueue != NULL)
156 unsigned char *temp = logqueue;
158 if (logfile != NULL && logq_ind != 0)
159 FS_Write (logfile, temp, logq_ind);
172 void Log_ConPrint (const char *msg)
174 static qboolean inprogress = false;
176 // don't allow feedback loops with memory error reports
181 // Until the host is completely initialized, we maintain a log queue
182 // to store the messages, since the log can't be started before
183 if (logqueue != NULL)
185 size_t remain = logq_size - logq_ind;
186 size_t len = strlen (msg);
188 // If we need to enlarge the log queue
191 size_t factor = ((logq_ind + len) / logq_size) + 1;
192 unsigned char* newqueue;
195 newqueue = (unsigned char *)Mem_Alloc (tempmempool, logq_size);
196 memcpy (newqueue, logqueue, logq_ind);
199 remain = logq_size - logq_ind;
201 memcpy (&logqueue[logq_ind], msg, len);
208 // Check if log_file has changed
209 if (strcmp (crt_log_file, log_file.string) != 0)
215 // If a log file is available
217 FS_Print (logfile, msg);
227 void Log_Printf (const char *logfilename, const char *fmt, ...)
231 file = FS_Open (logfilename, "ab", true, false);
236 va_start (argptr, fmt);
237 FS_VPrintf (file, fmt, argptr);
246 ==============================================================================
250 ==============================================================================
258 void Con_ToggleConsole_f (void)
260 // toggle the 'user wants console' bit
261 key_consoleactive ^= KEY_CONSOLEACTIVE_USER;
262 memset (con_times, 0, sizeof(con_times));
270 void Con_Clear_f (void)
273 memset (con_text, ' ', CON_TEXTSIZE);
282 void Con_ClearNotify (void)
286 for (i=0 ; i<MAX_NOTIFYLINES ; i++)
296 void Con_MessageMode_f (void)
298 key_dest = key_message;
308 void Con_MessageMode2_f (void)
310 key_dest = key_message;
319 If the line width has changed, reformat the buffer.
322 void Con_CheckResize (void)
324 int i, j, width, oldwidth, oldtotallines, numlines, numchars;
326 char tbuf[CON_TEXTSIZE];
328 f = bound(1, con_textsize.value, 128);
329 if(f != con_textsize.value)
330 Cvar_SetValueQuick(&con_textsize, f);
331 width = (int)floor(vid_conwidth.value / con_textsize.value);
332 width = bound(1, width, CON_TEXTSIZE/4);
334 if (width == con_linewidth)
337 oldwidth = con_linewidth;
338 con_linewidth = width;
339 oldtotallines = con_totallines;
340 con_totallines = CON_TEXTSIZE / con_linewidth;
341 numlines = oldtotallines;
343 if (con_totallines < numlines)
344 numlines = con_totallines;
348 if (con_linewidth < numchars)
349 numchars = con_linewidth;
351 memcpy (tbuf, con_text, CON_TEXTSIZE);
352 memset (con_text, ' ', CON_TEXTSIZE);
354 for (i=0 ; i<numlines ; i++)
356 for (j=0 ; j<numchars ; j++)
358 con_text[(con_totallines - 1 - i) * con_linewidth + j] =
359 tbuf[((con_current - i + oldtotallines) %
360 oldtotallines) * oldwidth + j];
367 con_current = con_totallines - 1;
370 //[515]: the simplest command ever
371 //LordHavoc: not so simple after I made it print usage...
372 static void Con_Maps_f (void)
376 Con_Printf("usage: maps [mapnameprefix]\n");
379 else if (Cmd_Argc() == 2)
380 GetMapList(Cmd_Argv(1), NULL, 0);
382 GetMapList("", NULL, 0);
392 memset (con_text, ' ', CON_TEXTSIZE);
394 con_totallines = CON_TEXTSIZE / con_linewidth;
396 // Allocate a log queue, this will be freed after configs are parsed
397 logq_size = MAX_INPUTLINE;
398 logqueue = (unsigned char *)Mem_Alloc (tempmempool, logq_size);
401 Cvar_RegisterVariable (&log_file);
403 // support for the classic Quake option
404 // COMMANDLINEOPTION: Console: -condebug logs console messages to qconsole.log, see also log_file
405 if (COM_CheckParm ("-condebug") != 0)
406 Cvar_SetQuick (&log_file, "qconsole.log");
409 void Con_Init_Commands (void)
411 // register our cvars
412 Cvar_RegisterVariable (&con_notifytime);
413 Cvar_RegisterVariable (&con_notify);
414 Cvar_RegisterVariable (&con_textsize);
416 // register our commands
417 Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f, "opens or closes the console");
418 Cmd_AddCommand ("messagemode", Con_MessageMode_f, "input a chat message to say to everyone");
419 Cmd_AddCommand ("messagemode2", Con_MessageMode2_f, "input a chat message to say to only your team");
420 Cmd_AddCommand ("clear", Con_Clear_f, "clear console history");
421 Cmd_AddCommand ("maps", Con_Maps_f, "list information about available maps"); // By [515]
423 con_initialized = true;
424 Con_Print("Console initialized.\n");
433 void Con_Linefeed (void)
440 memset (&con_text[(con_current%con_totallines)*con_linewidth], ' ', con_linewidth);
447 Handles cursor positioning, line wrapping, etc
448 All console printing must go through this in order to be displayed
449 If no console is visible, the notify window will pop up.
452 void Con_PrintToHistory(const char *txt, int mask)
460 for (l=0 ; l< con_linewidth ; l++)
465 if (l != con_linewidth && (con_x + l > con_linewidth) )
480 // mark time for transparent overlay
481 if (con_current >= 0)
483 if (con_notify.integer < 0)
484 Cvar_SetValueQuick(&con_notify, 0);
485 if (con_notify.integer > MAX_NOTIFYLINES)
486 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
487 if (con_notify.integer > 0)
488 con_times[con_current % con_notify.integer] = cl.time;
503 default: // display character and advance
504 y = con_current % con_totallines;
505 con_text[y*con_linewidth+con_x] = c | mask;
507 if (con_x >= con_linewidth)
515 /* The translation table between the graphical font and plain ASCII --KB */
516 static char qfont_table[256] = {
517 '\0', '#', '#', '#', '#', '.', '#', '#',
518 '#', 9, 10, '#', ' ', 13, '.', '.',
519 '[', ']', '0', '1', '2', '3', '4', '5',
520 '6', '7', '8', '9', '.', '<', '=', '>',
521 ' ', '!', '"', '#', '$', '%', '&', '\'',
522 '(', ')', '*', '+', ',', '-', '.', '/',
523 '0', '1', '2', '3', '4', '5', '6', '7',
524 '8', '9', ':', ';', '<', '=', '>', '?',
525 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
526 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
527 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
528 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
529 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
530 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
531 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
532 'x', 'y', 'z', '{', '|', '}', '~', '<',
534 '<', '=', '>', '#', '#', '.', '#', '#',
535 '#', '#', ' ', '#', ' ', '>', '.', '.',
536 '[', ']', '0', '1', '2', '3', '4', '5',
537 '6', '7', '8', '9', '.', '<', '=', '>',
538 ' ', '!', '"', '#', '$', '%', '&', '\'',
539 '(', ')', '*', '+', ',', '-', '.', '/',
540 '0', '1', '2', '3', '4', '5', '6', '7',
541 '8', '9', ':', ';', '<', '=', '>', '?',
542 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
543 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
544 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
545 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
546 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
547 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
548 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
549 'x', 'y', 'z', '{', '|', '}', '~', '<'
556 Prints to all appropriate console targets, and adds timestamps
559 extern cvar_t timestamps;
560 extern cvar_t timeformat;
561 extern qboolean sys_nostdout;
562 void Con_Print(const char *msg)
565 static int index = 0;
566 static char line[MAX_INPUTLINE];
570 // if this print is in response to an rcon command, add the character
571 // to the rcon redirect buffer
572 if (rcon_redirect && rcon_redirect_bufferpos < (int)sizeof(rcon_redirect_buffer) - 1)
573 rcon_redirect_buffer[rcon_redirect_bufferpos++] = *msg;
574 // if this is the beginning of a new line, print timestamp
577 const char *timestamp = timestamps.integer ? Sys_TimeString(timeformat.string) : "";
579 // FIXME: 1. perhaps we should use a terminal system 2. use a constant instead of 7!
580 line[index++] = STRING_COLOR_TAG;
581 // assert( STRING_COLOR_DEFAULT < 10 )
582 line[index++] = STRING_COLOR_DEFAULT + '0';
583 // special color codes for chat messages must always come first
584 // for Con_PrintToHistory to work properly
585 if (*msg == 1 || *msg == 2)
589 S_LocalSound ("sound/misc/talk.wav");
590 line[index++] = STRING_COLOR_TAG;
595 for (;*timestamp;index++, timestamp++)
596 if (index < (int)sizeof(line) - 2)
597 line[index] = *timestamp;
599 // append the character
600 line[index++] = *msg;
601 // if this is a newline character, we have a complete line to print
602 if (*msg == '\n' || index >= (int)sizeof(line) / 2)
604 // terminate the line
608 // send to scrollable buffer
609 if (con_initialized && cls.state != ca_dedicated)
610 Con_PrintToHistory(line, mask);
611 // send to terminal or dedicated server window
615 for (p = (unsigned char *) line;*p; p++)
616 *p = qfont_table[*p];
617 Sys_PrintToTerminal(line);
619 // empty the line buffer
630 Prints to all appropriate console targets
633 void Con_Printf(const char *fmt, ...)
636 char msg[MAX_INPUTLINE];
638 va_start(argptr,fmt);
639 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
649 A Con_Print that only shows up if the "developer" cvar is set
652 void Con_DPrint(const char *msg)
654 if (!developer.integer)
655 return; // don't confuse non-developers with techie stuff...
663 A Con_Printf that only shows up if the "developer" cvar is set
666 void Con_DPrintf(const char *fmt, ...)
669 char msg[MAX_INPUTLINE];
671 if (!developer.integer)
672 return; // don't confuse non-developers with techie stuff...
674 va_start(argptr,fmt);
675 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
683 ==============================================================================
687 ==============================================================================
694 The input line scrolls horizontally if typing goes beyond the right edge
696 Modified by EvilTypeGuy eviltypeguy@qeradiant.com
699 void Con_DrawInput (void)
703 char editlinecopy[MAX_INPUTLINE+1], *text;
705 if (!key_consoleactive)
706 return; // don't draw anything
708 strlcpy(editlinecopy, key_lines[edit_line], sizeof(editlinecopy));
711 // Advanced Console Editing by Radix radix@planetquake.com
712 // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
713 // use strlen of edit_line instead of key_linepos to allow editing
714 // of early characters w/o erasing
716 y = (int)strlen(text);
718 // fill out remainder with spaces
719 for (i = y; i < (int)sizeof(editlinecopy)-1; i++)
722 // add the cursor frame
723 if ((int)(realtime*con_cursorspeed) & 1) // cursor is visible
724 text[key_linepos] = 11 + 130 * key_insert; // either solid or triangle facing right
726 // text[key_linepos + 1] = 0;
728 // prestep if horizontally scrolling
729 if (key_linepos >= con_linewidth)
730 text += 1 + key_linepos - con_linewidth;
733 DrawQ_ColoredString(0, con_vislines - con_textsize.value*2, text, con_linewidth, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, NULL );
736 // key_lines[edit_line][key_linepos] = 0;
744 Draws the last few lines of output transparently over the game top
747 void Con_DrawNotify (void)
753 char temptext[MAX_INPUTLINE];
754 int colorindex = -1; //-1 for default
756 if (con_notify.integer < 0)
757 Cvar_SetValueQuick(&con_notify, 0);
758 if (con_notify.integer > MAX_NOTIFYLINES)
759 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
760 if (gamemode == GAME_TRANSFUSION)
764 // make a copy of con_current here so that we can't get in a runaway loop printing new messages while drawing the notify text
766 for (i= stop-con_notify.integer+1 ; i<=stop ; i++)
771 time = con_times[i % con_notify.integer];
774 time = cl.time - time;
775 if (time > con_notifytime.value)
777 text = con_text + (i % con_totallines)*con_linewidth;
779 if (gamemode == GAME_NEXUIZ) {
784 // count up to the last non-whitespace, and ignore color codes
785 for (j = 0;j < con_linewidth && text[j];j++)
787 if (text[j] == '^' && (text[j+1] >= '0' && text[j+1] <= '9'))
797 // center the line using the calculated width
798 x = (vid_conwidth.integer - finalchars * con_textsize.value) * 0.5;
802 DrawQ_ColoredString( x, v, text, con_linewidth, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
804 v += con_textsize.value;
808 if (key_dest == key_message)
814 // LordHavoc: speedup, and other improvements
816 sprintf(temptext, "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
818 sprintf(temptext, "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
819 while ((int)strlen(temptext) >= con_linewidth)
821 DrawQ_ColoredString( 0, v, temptext, con_linewidth, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
822 strlcpy(temptext, &temptext[con_linewidth], sizeof(temptext));
823 v += con_textsize.value;
825 if (strlen(temptext) > 0)
827 DrawQ_ColoredString( 0, v, temptext, 0, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
828 v += con_textsize.value;
837 Draws the console with the solid background
838 The typing input line at the bottom should only be drawn if typing is allowed
841 void Con_DrawConsole (int lines)
843 int i, rows, j, stop;
851 // draw the background
852 DrawQ_Pic(0, lines - vid_conheight.integer, scr_conbrightness.value >= 0.01f ? Draw_CachePic("gfx/conback", true) : NULL, vid_conwidth.integer, vid_conheight.integer, scr_conbrightness.value, scr_conbrightness.value, scr_conbrightness.value, scr_conalpha.value, 0);
853 DrawQ_String(vid_conwidth.integer - strlen(engineversion) * con_textsize.value - con_textsize.value, lines - con_textsize.value, engineversion, 0, con_textsize.value, con_textsize.value, 1, 0, 0, 1, 0);
856 con_vislines = lines;
858 rows = (int)ceil((lines/con_textsize.value)-2); // rows of text to draw
859 y = lines - (rows+2)*con_textsize.value; // may start slightly negative
861 // make a copy of con_current here so that we can't get in a runaway loop printing new messages while drawing the notify text
863 for (i = stop - rows + 1;i <= stop;i++, y += con_textsize.value)
865 j = max(i - con_backscroll, 0);
866 text = con_text + (j % con_totallines)*con_linewidth;
868 DrawQ_ColoredString( 0, y, text, con_linewidth, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
871 // draw the input prompt, user text, and cursor if desired
879 Prints not only map filename, but also
880 its format (q1/q2/q3/hl) and even its message
882 //[515]: here is an ugly hack.. two gotos... oh my... *but it works*
883 //LordHavoc: rewrote bsp type detection, added mcbsp support and rewrote message extraction to do proper worldspawn parsing
884 //LordHavoc: added .ent file loading, and redesigned error handling to still try the .ent file even if the map format is not recognized, this also eliminated one goto
885 //LordHavoc: FIXME: man this GetMapList is STILL ugly code even after my cleanups...
886 qboolean GetMapList (const char *s, char *completedname, int completednamebufferlength)
890 int i, k, max, p, o, min;
893 unsigned char buf[1024];
895 sprintf(message, "maps/%s*.bsp", s);
896 t = FS_Search(message, 1, true);
899 if (t->numfilenames > 1)
900 Con_Printf("^1 %i maps found :\n", t->numfilenames);
901 len = (unsigned char *)Z_Malloc(t->numfilenames);
903 for(max=i=0;i<t->numfilenames;i++)
905 k = (int)strlen(t->filenames[i]);
915 for(i=0;i<t->numfilenames;i++)
917 int lumpofs = 0, lumplen = 0;
918 char *entities = NULL;
919 const char *data = NULL;
921 char entfilename[MAX_QPATH];
922 strlcpy(message, "^1**ERROR**^7", sizeof(message));
924 f = FS_Open(t->filenames[i], "rb", true, false);
927 memset(buf, 0, 1024);
928 FS_Read(f, buf, 1024);
929 if (!memcmp(buf, "IBSP", 4))
931 p = LittleLong(((int *)buf)[1]);
932 if (p == Q3BSPVERSION)
934 q3dheader_t *header = (q3dheader_t *)buf;
935 lumpofs = LittleLong(header->lumps[Q3LUMP_ENTITIES].fileofs);
936 lumplen = LittleLong(header->lumps[Q3LUMP_ENTITIES].filelen);
938 else if (p == Q2BSPVERSION)
940 q2dheader_t *header = (q2dheader_t *)buf;
941 lumpofs = LittleLong(header->lumps[Q2LUMP_ENTITIES].fileofs);
942 lumplen = LittleLong(header->lumps[Q2LUMP_ENTITIES].filelen);
945 else if (!memcmp(buf, "MCBSPpad", 8))
947 p = LittleLong(((int *)buf)[2]);
948 if (p == MCBSPVERSION)
950 int numhulls = LittleLong(((int *)buf)[3]);
951 lumpofs = LittleLong(((int *)buf)[3 + numhulls + LUMP_ENTITIES*2+0]);
952 lumplen = LittleLong(((int *)buf)[3 + numhulls + LUMP_ENTITIES*2+1]);
955 else if((p = LittleLong(((int *)buf)[0])) == BSPVERSION || p == 30)
957 dheader_t *header = (dheader_t *)buf;
958 lumpofs = LittleLong(header->lumps[LUMP_ENTITIES].fileofs);
959 lumplen = LittleLong(header->lumps[LUMP_ENTITIES].filelen);
963 strlcpy(entfilename, t->filenames[i], sizeof(entfilename));
964 memcpy(entfilename + strlen(entfilename) - 4, ".ent", 5);
965 entities = (char *)FS_LoadFile(entfilename, tempmempool, true, NULL);
966 if (!entities && lumplen >= 10)
968 FS_Seek(f, lumpofs, SEEK_SET);
969 entities = (char *)Z_Malloc(lumplen + 1);
970 FS_Read(f, entities, lumplen);
974 // if there are entities to parse, a missing message key just
975 // means there is no title, so clear the message string now
981 if (!COM_ParseTokenConsole(&data))
983 if (com_token[0] == '{')
985 if (com_token[0] == '}')
987 // skip leading whitespace
988 for (k = 0;com_token[k] && com_token[k] <= ' ';k++);
989 for (l = 0;l < (int)sizeof(keyname) - 1 && com_token[k+l] && com_token[k+l] > ' ';l++)
990 keyname[l] = com_token[k+l];
992 if (!COM_ParseTokenConsole(&data))
994 if (developer.integer >= 100)
995 Con_Printf("key: %s %s\n", keyname, com_token);
996 if (!strcmp(keyname, "message"))
998 // get the message contents
999 strlcpy(message, com_token, sizeof(message));
1009 *(t->filenames[i]+len[i]+5) = 0;
1012 case Q3BSPVERSION: strlcpy((char *)buf, "Q3", sizeof(buf));break;
1013 case Q2BSPVERSION: strlcpy((char *)buf, "Q2", sizeof(buf));break;
1014 case BSPVERSION: strlcpy((char *)buf, "Q1", sizeof(buf));break;
1015 case MCBSPVERSION: strlcpy((char *)buf, "MC", sizeof(buf));break;
1016 case 30: strlcpy((char *)buf, "HL", sizeof(buf));break;
1017 default: strlcpy((char *)buf, "??", sizeof(buf));break;
1019 Con_Printf("%16s (%s) %s\n", t->filenames[i]+5, buf, message);
1024 k = *(t->filenames[0]+5+p);
1027 for(i=1;i<t->numfilenames;i++)
1028 if(*(t->filenames[i]+5+p) != k)
1034 memset(completedname, 0, completednamebufferlength);
1035 memcpy(completedname, (t->filenames[0]+5), min(p, completednamebufferlength - 1));
1045 New function for tab-completion system
1046 Added by EvilTypeGuy
1047 MEGA Thanks to Taniwha
1050 void Con_DisplayList(const char **list)
1052 int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
1053 const char **walk = list;
1056 len = (int)strlen(*walk);
1064 len = (int)strlen(*list);
1065 if (pos + maxlen >= width) {
1071 for (i = 0; i < (maxlen - len); i++)
1083 Con_CompleteCommandLine
1085 New function for tab-completion system
1086 Added by EvilTypeGuy
1087 Thanks to Fett erich@heintz.com
1089 Enhanced to tab-complete map names by [515]
1092 void Con_CompleteCommandLine (void)
1094 const char *cmd = "";
1096 const char **list[3] = {0, 0, 0};
1098 int c, v, a, i, cmd_len, pos, k;
1100 //find what we want to complete
1104 k = key_lines[edit_line][pos];
1105 if(k == '\"' || k == ';' || k == ' ' || k == '\'')
1110 s = key_lines[edit_line] + pos;
1111 strlcpy(s2, key_lines[edit_line] + key_linepos, sizeof(s2)); //save chars after cursor
1112 key_lines[edit_line][key_linepos] = 0; //hide them
1115 for(k=pos-1;k>2;k--)
1116 if(key_lines[edit_line][k] != ' ')
1118 if(key_lines[edit_line][k] == '\"' || key_lines[edit_line][k] == ';' || key_lines[edit_line][k] == '\'')
1120 if ((pos+k > 2 && !strncmp(key_lines[edit_line]+k-2, "map", 3))
1121 || (pos+k > 10 && !strncmp(key_lines[edit_line]+k-10, "changelevel", 11)))
1124 if (GetMapList(s, t, sizeof(t)))
1126 // first move the cursor
1127 key_linepos += (int)strlen(t) - (int)strlen(s);
1129 // and now do the actual work
1131 strlcat(key_lines[edit_line], t, MAX_INPUTLINE);
1132 strlcat(key_lines[edit_line], s2, MAX_INPUTLINE); //add back chars after cursor
1134 // and fix the cursor
1135 if(key_linepos > (int) strlen(key_lines[edit_line]))
1136 key_linepos = (int) strlen(key_lines[edit_line]);
1142 // Count number of possible matches and print them
1143 c = Cmd_CompleteCountPossible(s);
1146 Con_Printf("\n%i possible command%s\n", c, (c > 1) ? "s: " : ":");
1147 Cmd_CompleteCommandPrint(s);
1149 v = Cvar_CompleteCountPossible(s);
1152 Con_Printf("\n%i possible variable%s\n", v, (v > 1) ? "s: " : ":");
1153 Cvar_CompleteCvarPrint(s);
1155 a = Cmd_CompleteAliasCountPossible(s);
1158 Con_Printf("\n%i possible aliases%s\n", a, (a > 1) ? "s: " : ":");
1159 Cmd_CompleteAliasPrint(s);
1162 if (!(c + v + a)) // No possible matches
1165 strlcpy(&key_lines[edit_line][key_linepos], s2, sizeof(key_lines[edit_line]) - key_linepos);
1170 cmd = *(list[0] = Cmd_CompleteBuildList(s));
1172 cmd = *(list[1] = Cvar_CompleteBuildList(s));
1174 cmd = *(list[2] = Cmd_CompleteAliasBuildList(s));
1176 for (cmd_len = (int)strlen(s);;cmd_len++)
1179 for (i = 0; i < 3; i++)
1181 for (l = list[i];*l;l++)
1182 if ((*l)[cmd_len] != cmd[cmd_len])
1184 // all possible matches share this character, so we continue...
1187 // if all matches ended at the same position, stop
1188 // (this means there is only one match)
1194 // prevent a buffer overrun by limiting cmd_len according to remaining space
1195 cmd_len = min(cmd_len, (int)sizeof(key_lines[edit_line]) - 1 - pos);
1199 memcpy(&key_lines[edit_line][key_linepos], cmd, cmd_len);
1200 key_linepos += cmd_len;
1201 // if there is only one match, add a space after it
1202 if (c + v + a == 1 && key_linepos < (int)sizeof(key_lines[edit_line]) - 1)
1203 key_lines[edit_line][key_linepos++] = ' ';
1206 // use strlcat to avoid a buffer overrun
1207 key_lines[edit_line][key_linepos] = 0;
1208 strlcat(key_lines[edit_line], s2, sizeof(key_lines[edit_line]));
1210 // free the command, cvar, and alias lists
1211 for (i = 0; i < 3; i++)
1213 Mem_Free((void *)list[i]);