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"};
49 cvar_t sys_specialcharactertranslation = {0, "sys_specialcharactertranslation", "1", "terminal console conchars to ASCII translation (set to 0 if your conchars.tga is for an 8bit character set or if you want raw output)"};
51 cvar_t sys_colortranslation = {0, "sys_colortranslation", "0", "terminal console color translation (supported values: 0 = strip color codes, 1 = translate to ANSI codes, 2 = no translation)"};
53 cvar_t sys_colortranslation = {0, "sys_colortranslation", "1", "terminal console color translation (supported values: 0 = strip color codes, 1 = translate to ANSI codes, 2 = no translation)"};
56 #define MAX_NOTIFYLINES 32
57 // cl.time time the line was generated for transparent notify lines
58 float con_times[MAX_NOTIFYLINES];
62 qboolean con_initialized;
64 // used for server replies to rcon command
65 qboolean rcon_redirect = false;
66 int rcon_redirect_bufferpos = 0;
67 char rcon_redirect_buffer[1400];
71 ==============================================================================
75 ==============================================================================
78 cvar_t log_file = {0, "log_file","", "filename to log messages to"};
79 char crt_log_file [MAX_OSPATH] = "";
80 qfile_t* logfile = NULL;
82 unsigned char* logqueue = NULL;
86 void Log_ConPrint (const char *msg);
93 const char* Log_Timestamp (const char *desc)
95 static char timestamp [128];
97 const struct tm *crt_tm;
100 // Build the time stamp (ex: "Wed Jun 30 21:49:08 1993");
102 crt_tm = localtime (&crt_time);
103 strftime (timestring, sizeof (timestring), "%a %b %d %H:%M:%S %Y", crt_tm);
106 dpsnprintf (timestamp, sizeof (timestamp), "====== %s (%s) ======\n", desc, timestring);
108 dpsnprintf (timestamp, sizeof (timestamp), "====== %s ======\n", timestring);
121 if (logfile != NULL || log_file.string[0] == '\0')
124 logfile = FS_Open (log_file.string, "ab", false, false);
127 strlcpy (crt_log_file, log_file.string, sizeof (crt_log_file));
128 FS_Print (logfile, Log_Timestamp ("Log started"));
138 void Log_Close (void)
143 FS_Print (logfile, Log_Timestamp ("Log stopped"));
144 FS_Print (logfile, "\n");
148 crt_log_file[0] = '\0';
157 void Log_Start (void)
161 // Dump the contents of the log queue into the log file and free it
162 if (logqueue != NULL)
164 unsigned char *temp = logqueue;
166 if (logfile != NULL && logq_ind != 0)
167 FS_Write (logfile, temp, logq_ind);
180 void Log_ConPrint (const char *msg)
182 static qboolean inprogress = false;
184 // don't allow feedback loops with memory error reports
189 // Until the host is completely initialized, we maintain a log queue
190 // to store the messages, since the log can't be started before
191 if (logqueue != NULL)
193 size_t remain = logq_size - logq_ind;
194 size_t len = strlen (msg);
196 // If we need to enlarge the log queue
199 size_t factor = ((logq_ind + len) / logq_size) + 1;
200 unsigned char* newqueue;
203 newqueue = (unsigned char *)Mem_Alloc (tempmempool, logq_size);
204 memcpy (newqueue, logqueue, logq_ind);
207 remain = logq_size - logq_ind;
209 memcpy (&logqueue[logq_ind], msg, len);
216 // Check if log_file has changed
217 if (strcmp (crt_log_file, log_file.string) != 0)
223 // If a log file is available
225 FS_Print (logfile, msg);
235 void Log_Printf (const char *logfilename, const char *fmt, ...)
239 file = FS_Open (logfilename, "ab", true, false);
244 va_start (argptr, fmt);
245 FS_VPrintf (file, fmt, argptr);
254 ==============================================================================
258 ==============================================================================
266 void Con_ToggleConsole_f (void)
268 // toggle the 'user wants console' bit
269 key_consoleactive ^= KEY_CONSOLEACTIVE_USER;
270 memset (con_times, 0, sizeof(con_times));
278 void Con_Clear_f (void)
281 memset (con_text, ' ', CON_TEXTSIZE);
290 void Con_ClearNotify (void)
294 for (i=0 ; i<MAX_NOTIFYLINES ; i++)
304 void Con_MessageMode_f (void)
306 key_dest = key_message;
316 void Con_MessageMode2_f (void)
318 key_dest = key_message;
327 If the line width has changed, reformat the buffer.
330 void Con_CheckResize (void)
332 int i, j, width, oldwidth, oldtotallines, numlines, numchars;
334 char tbuf[CON_TEXTSIZE];
336 f = bound(1, con_textsize.value, 128);
337 if(f != con_textsize.value)
338 Cvar_SetValueQuick(&con_textsize, f);
339 width = (int)floor(vid_conwidth.value / con_textsize.value);
340 width = bound(1, width, CON_TEXTSIZE/4);
342 if (width == con_linewidth)
345 oldwidth = con_linewidth;
346 con_linewidth = width;
347 oldtotallines = con_totallines;
348 con_totallines = CON_TEXTSIZE / con_linewidth;
349 numlines = oldtotallines;
351 if (con_totallines < numlines)
352 numlines = con_totallines;
356 if (con_linewidth < numchars)
357 numchars = con_linewidth;
359 memcpy (tbuf, con_text, CON_TEXTSIZE);
360 memset (con_text, ' ', CON_TEXTSIZE);
362 for (i=0 ; i<numlines ; i++)
364 for (j=0 ; j<numchars ; j++)
366 con_text[(con_totallines - 1 - i) * con_linewidth + j] =
367 tbuf[((con_current - i + oldtotallines) %
368 oldtotallines) * oldwidth + j];
375 con_current = con_totallines - 1;
378 //[515]: the simplest command ever
379 //LordHavoc: not so simple after I made it print usage...
380 static void Con_Maps_f (void)
384 Con_Printf("usage: maps [mapnameprefix]\n");
387 else if (Cmd_Argc() == 2)
388 GetMapList(Cmd_Argv(1), NULL, 0);
390 GetMapList("", NULL, 0);
393 void Con_ConDump_f (void)
396 qboolean allblankssofar;
399 char temp[MAX_INPUTLINE+2];
402 Con_Printf("usage: condump <filename>\n");
405 file = FS_Open(Cmd_Argv(1), "wb", false, false);
408 Con_Printf("condump: unable to write file \"%s\"\n", Cmd_Argv(1));
411 // iterate over the entire console history buffer line by line
412 allblankssofar = true;
413 for (i = 0;i < con_totallines;i++)
415 text = con_text + ((con_current + 1 + i) % con_totallines)*con_linewidth;
416 // count the used characters on this line
417 for (l = min(con_linewidth, (int)sizeof(temp));l > 0 && text[l-1] == ' ';l--);
418 // if not a blank line, begin output
420 allblankssofar = false;
421 // output the current line to the file
425 memcpy(temp, text, l);
428 FS_Print(file, temp);
441 memset (con_text, ' ', CON_TEXTSIZE);
443 con_totallines = CON_TEXTSIZE / con_linewidth;
445 // Allocate a log queue, this will be freed after configs are parsed
446 logq_size = MAX_INPUTLINE;
447 logqueue = (unsigned char *)Mem_Alloc (tempmempool, logq_size);
450 Cvar_RegisterVariable (&sys_colortranslation);
451 Cvar_RegisterVariable (&sys_specialcharactertranslation);
453 Cvar_RegisterVariable (&log_file);
455 // support for the classic Quake option
456 // COMMANDLINEOPTION: Console: -condebug logs console messages to qconsole.log, see also log_file
457 if (COM_CheckParm ("-condebug") != 0)
458 Cvar_SetQuick (&log_file, "qconsole.log");
460 // register our cvars
461 Cvar_RegisterVariable (&con_notifytime);
462 Cvar_RegisterVariable (&con_notify);
463 Cvar_RegisterVariable (&con_textsize);
465 // register our commands
466 Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f, "opens or closes the console");
467 Cmd_AddCommand ("messagemode", Con_MessageMode_f, "input a chat message to say to everyone");
468 Cmd_AddCommand ("messagemode2", Con_MessageMode2_f, "input a chat message to say to only your team");
469 Cmd_AddCommand ("clear", Con_Clear_f, "clear console history");
470 Cmd_AddCommand ("maps", Con_Maps_f, "list information about available maps");
471 Cmd_AddCommand ("condump", Con_ConDump_f, "output console history to a file (see also log_file)");
473 con_initialized = true;
474 Con_Print("Console initialized.\n");
483 void Con_Linefeed (void)
490 memset (&con_text[(con_current%con_totallines)*con_linewidth], ' ', con_linewidth);
497 Handles cursor positioning, line wrapping, etc
498 All console printing must go through this in order to be displayed
499 If no console is visible, the notify window will pop up.
502 void Con_PrintToHistory(const char *txt, int mask)
510 for (l=0 ; l< con_linewidth ; l++)
515 if (l != con_linewidth && (con_x + l > con_linewidth) )
530 // mark time for transparent overlay
531 if (con_current >= 0)
533 if (con_notify.integer < 0)
534 Cvar_SetValueQuick(&con_notify, 0);
535 if (con_notify.integer > MAX_NOTIFYLINES)
536 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
537 if (con_notify.integer > 0)
538 con_times[con_current % con_notify.integer] = cl.time;
553 default: // display character and advance
554 y = con_current % con_totallines;
555 con_text[y*con_linewidth+con_x] = c | mask;
557 if (con_x >= con_linewidth)
565 /* The translation table between the graphical font and plain ASCII --KB */
566 static char qfont_table[256] = {
567 '\0', '#', '#', '#', '#', '.', '#', '#',
568 '#', 9, 10, '#', ' ', 13, '.', '.',
569 '[', ']', '0', '1', '2', '3', '4', '5',
570 '6', '7', '8', '9', '.', '<', '=', '>',
571 ' ', '!', '"', '#', '$', '%', '&', '\'',
572 '(', ')', '*', '+', ',', '-', '.', '/',
573 '0', '1', '2', '3', '4', '5', '6', '7',
574 '8', '9', ':', ';', '<', '=', '>', '?',
575 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
576 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
577 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
578 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
579 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
580 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
581 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
582 'x', 'y', 'z', '{', '|', '}', '~', '<',
584 '<', '=', '>', '#', '#', '.', '#', '#',
585 '#', '#', ' ', '#', ' ', '>', '.', '.',
586 '[', ']', '0', '1', '2', '3', '4', '5',
587 '6', '7', '8', '9', '.', '<', '=', '>',
588 ' ', '!', '"', '#', '$', '%', '&', '\'',
589 '(', ')', '*', '+', ',', '-', '.', '/',
590 '0', '1', '2', '3', '4', '5', '6', '7',
591 '8', '9', ':', ';', '<', '=', '>', '?',
592 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
593 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
594 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
595 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
596 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
597 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
598 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
599 'x', 'y', 'z', '{', '|', '}', '~', '<'
606 Prints to all appropriate console targets, and adds timestamps
609 extern cvar_t timestamps;
610 extern cvar_t timeformat;
611 extern qboolean sys_nostdout;
612 void Con_Print(const char *msg)
615 static int index = 0;
616 static char line[MAX_INPUTLINE];
620 // if this print is in response to an rcon command, add the character
621 // to the rcon redirect buffer
622 if (rcon_redirect && rcon_redirect_bufferpos < (int)sizeof(rcon_redirect_buffer) - 1)
623 rcon_redirect_buffer[rcon_redirect_bufferpos++] = *msg;
624 // if this is the beginning of a new line, print timestamp
627 const char *timestamp = timestamps.integer ? Sys_TimeString(timeformat.string) : "";
629 // FIXME: 1. perhaps we should use a terminal system 2. use a constant instead of 7!
630 line[index++] = STRING_COLOR_TAG;
631 // assert( STRING_COLOR_DEFAULT < 10 )
632 line[index++] = STRING_COLOR_DEFAULT + '0';
633 // special color codes for chat messages must always come first
634 // for Con_PrintToHistory to work properly
635 if (*msg == 1 || *msg == 2)
639 S_LocalSound ("sound/misc/talk.wav");
640 line[index++] = STRING_COLOR_TAG;
645 for (;*timestamp;index++, timestamp++)
646 if (index < (int)sizeof(line) - 2)
647 line[index] = *timestamp;
649 // append the character
650 line[index++] = *msg;
651 // if this is a newline character, we have a complete line to print
652 if (*msg == '\n' || index >= (int)sizeof(line) / 2)
654 // terminate the line
658 // send to scrollable buffer
659 if (con_initialized && cls.state != ca_dedicated)
660 Con_PrintToHistory(line, mask);
661 // send to terminal or dedicated server window
665 if(sys_specialcharactertranslation.integer)
667 for (p = (unsigned char *) line;*p; p++)
668 *p = qfont_table[*p];
671 if(sys_colortranslation.integer == 1) // ANSI
673 static char printline[MAX_INPUTLINE * 4 + 3];
674 // 2 can become 7 bytes, rounding that up to 8, and 3 bytes are added at the end
675 // a newline can transform into four bytes, but then prevents the three extra bytes from appearing
679 for(in = line, out = printline; *in; ++in)
683 case STRING_COLOR_TAG:
686 case STRING_COLOR_TAG:
688 *out++ = STRING_COLOR_TAG;
694 if(lastcolor == 0) break; else lastcolor = 0;
695 *out++ = 0x1B; *out++ = '['; *out++ = 'm';
700 if(lastcolor == 1) break; else lastcolor = 1;
701 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '1'; *out++ = 'm';
706 if(lastcolor == 2) break; else lastcolor = 2;
707 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '2'; *out++ = 'm';
712 if(lastcolor == 3) break; else lastcolor = 3;
713 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '3'; *out++ = 'm';
718 if(lastcolor == 4) break; else lastcolor = 4;
719 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '4'; *out++ = 'm';
724 if(lastcolor == 5) break; else lastcolor = 5;
725 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '6'; *out++ = 'm';
730 if(lastcolor == 6) break; else lastcolor = 6;
731 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '5'; *out++ = 'm';
738 if(lastcolor == 8) break; else lastcolor = 8;
739 *out++ = 0x1B; *out++ = '['; *out++ = '0'; *out++ = ';'; *out++ = '1'; *out++ = 'm';
742 *out++ = STRING_COLOR_TAG;
749 *out++ = 0x1B; *out++ = '['; *out++ = 'm';
766 Sys_PrintToTerminal(printline);
768 else if(sys_colortranslation.integer == 2) // Quake
770 Sys_PrintToTerminal(line);
774 static char printline[MAX_INPUTLINE]; // it can only get shorter here
777 for(in = line, out = printline; *in; ++in)
781 case STRING_COLOR_TAG:
784 case STRING_COLOR_TAG:
786 *out++ = STRING_COLOR_TAG;
801 *out++ = STRING_COLOR_TAG;
811 Sys_PrintToTerminal(printline);
814 // empty the line buffer
825 Prints to all appropriate console targets
828 void Con_Printf(const char *fmt, ...)
831 char msg[MAX_INPUTLINE];
833 va_start(argptr,fmt);
834 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
844 A Con_Print that only shows up if the "developer" cvar is set
847 void Con_DPrint(const char *msg)
849 if (!developer.integer)
850 return; // don't confuse non-developers with techie stuff...
858 A Con_Printf that only shows up if the "developer" cvar is set
861 void Con_DPrintf(const char *fmt, ...)
864 char msg[MAX_INPUTLINE];
866 if (!developer.integer)
867 return; // don't confuse non-developers with techie stuff...
869 va_start(argptr,fmt);
870 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
878 ==============================================================================
882 ==============================================================================
889 The input line scrolls horizontally if typing goes beyond the right edge
891 Modified by EvilTypeGuy eviltypeguy@qeradiant.com
894 void Con_DrawInput (void)
898 char editlinecopy[MAX_INPUTLINE+1], *text;
900 if (!key_consoleactive)
901 return; // don't draw anything
903 strlcpy(editlinecopy, key_lines[edit_line], sizeof(editlinecopy));
906 // Advanced Console Editing by Radix radix@planetquake.com
907 // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
908 // use strlen of edit_line instead of key_linepos to allow editing
909 // of early characters w/o erasing
911 y = (int)strlen(text);
913 // fill out remainder with spaces
914 for (i = y; i < (int)sizeof(editlinecopy)-1; i++)
917 // add the cursor frame
918 if ((int)(realtime*con_cursorspeed) & 1) // cursor is visible
919 text[key_linepos] = 11 + 130 * key_insert; // either solid or triangle facing right
921 // text[key_linepos + 1] = 0;
923 // prestep if horizontally scrolling
924 if (key_linepos >= con_linewidth)
925 text += 1 + key_linepos - con_linewidth;
928 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 );
931 // key_lines[edit_line][key_linepos] = 0;
939 Draws the last few lines of output transparently over the game top
942 void Con_DrawNotify (void)
948 char temptext[MAX_INPUTLINE];
949 int colorindex = -1; //-1 for default
951 if (con_notify.integer < 0)
952 Cvar_SetValueQuick(&con_notify, 0);
953 if (con_notify.integer > MAX_NOTIFYLINES)
954 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
955 if (gamemode == GAME_TRANSFUSION)
959 // 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
961 for (i= stop-con_notify.integer+1 ; i<=stop ; i++)
966 time = con_times[i % con_notify.integer];
969 time = cl.time - time;
970 if (time > con_notifytime.value)
972 text = con_text + (i % con_totallines)*con_linewidth;
974 if (gamemode == GAME_NEXUIZ) {
979 // count up to the last non-whitespace, and ignore color codes
980 for (j = 0;j < con_linewidth && text[j];j++)
982 if (text[j] == STRING_COLOR_TAG && (text[j+1] >= '0' && text[j+1] <= '9'))
992 // center the line using the calculated width
993 x = (vid_conwidth.integer - finalchars * con_textsize.value) * 0.5;
997 DrawQ_ColoredString( x, v, text, con_linewidth, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
999 v += con_textsize.value;
1003 if (key_dest == key_message)
1005 int colorindex = -1;
1009 // LordHavoc: speedup, and other improvements
1011 sprintf(temptext, "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
1013 sprintf(temptext, "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
1014 while ((int)strlen(temptext) >= con_linewidth)
1016 DrawQ_ColoredString( 0, v, temptext, con_linewidth, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
1017 strlcpy(temptext, &temptext[con_linewidth], sizeof(temptext));
1018 v += con_textsize.value;
1020 if (strlen(temptext) > 0)
1022 DrawQ_ColoredString( 0, v, temptext, 0, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
1023 v += con_textsize.value;
1032 Draws the console with the solid background
1033 The typing input line at the bottom should only be drawn if typing is allowed
1036 void Con_DrawConsole (int lines)
1038 int i, rows, j, stop;
1041 int colorindex = -1;
1046 // draw the background
1047 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);
1048 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);
1051 con_vislines = lines;
1053 rows = (int)ceil((lines/con_textsize.value)-2); // rows of text to draw
1054 y = lines - (rows+2)*con_textsize.value; // may start slightly negative
1056 // 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
1058 for (i = stop - rows + 1;i <= stop;i++, y += con_textsize.value)
1060 j = max(i - con_backscroll, 0);
1061 text = con_text + (j % con_totallines)*con_linewidth;
1063 DrawQ_ColoredString( 0, y, text, con_linewidth, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
1066 // draw the input prompt, user text, and cursor if desired
1074 Prints not only map filename, but also
1075 its format (q1/q2/q3/hl) and even its message
1077 //[515]: here is an ugly hack.. two gotos... oh my... *but it works*
1078 //LordHavoc: rewrote bsp type detection, added mcbsp support and rewrote message extraction to do proper worldspawn parsing
1079 //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
1080 //LordHavoc: FIXME: man this GetMapList is STILL ugly code even after my cleanups...
1081 qboolean GetMapList (const char *s, char *completedname, int completednamebufferlength)
1085 int i, k, max, p, o, min;
1088 unsigned char buf[1024];
1090 sprintf(message, "maps/%s*.bsp", s);
1091 t = FS_Search(message, 1, true);
1094 if (t->numfilenames > 1)
1095 Con_Printf("^1 %i maps found :\n", t->numfilenames);
1096 len = (unsigned char *)Z_Malloc(t->numfilenames);
1098 for(max=i=0;i<t->numfilenames;i++)
1100 k = (int)strlen(t->filenames[i]);
1110 for(i=0;i<t->numfilenames;i++)
1112 int lumpofs = 0, lumplen = 0;
1113 char *entities = NULL;
1114 const char *data = NULL;
1116 char entfilename[MAX_QPATH];
1117 strlcpy(message, "^1**ERROR**^7", sizeof(message));
1119 f = FS_Open(t->filenames[i], "rb", true, false);
1122 memset(buf, 0, 1024);
1123 FS_Read(f, buf, 1024);
1124 if (!memcmp(buf, "IBSP", 4))
1126 p = LittleLong(((int *)buf)[1]);
1127 if (p == Q3BSPVERSION)
1129 q3dheader_t *header = (q3dheader_t *)buf;
1130 lumpofs = LittleLong(header->lumps[Q3LUMP_ENTITIES].fileofs);
1131 lumplen = LittleLong(header->lumps[Q3LUMP_ENTITIES].filelen);
1133 else if (p == Q2BSPVERSION)
1135 q2dheader_t *header = (q2dheader_t *)buf;
1136 lumpofs = LittleLong(header->lumps[Q2LUMP_ENTITIES].fileofs);
1137 lumplen = LittleLong(header->lumps[Q2LUMP_ENTITIES].filelen);
1140 else if (!memcmp(buf, "MCBSPpad", 8))
1142 p = LittleLong(((int *)buf)[2]);
1143 if (p == MCBSPVERSION)
1145 int numhulls = LittleLong(((int *)buf)[3]);
1146 lumpofs = LittleLong(((int *)buf)[3 + numhulls + LUMP_ENTITIES*2+0]);
1147 lumplen = LittleLong(((int *)buf)[3 + numhulls + LUMP_ENTITIES*2+1]);
1150 else if((p = LittleLong(((int *)buf)[0])) == BSPVERSION || p == 30)
1152 dheader_t *header = (dheader_t *)buf;
1153 lumpofs = LittleLong(header->lumps[LUMP_ENTITIES].fileofs);
1154 lumplen = LittleLong(header->lumps[LUMP_ENTITIES].filelen);
1158 strlcpy(entfilename, t->filenames[i], sizeof(entfilename));
1159 memcpy(entfilename + strlen(entfilename) - 4, ".ent", 5);
1160 entities = (char *)FS_LoadFile(entfilename, tempmempool, true, NULL);
1161 if (!entities && lumplen >= 10)
1163 FS_Seek(f, lumpofs, SEEK_SET);
1164 entities = (char *)Z_Malloc(lumplen + 1);
1165 FS_Read(f, entities, lumplen);
1169 // if there are entities to parse, a missing message key just
1170 // means there is no title, so clear the message string now
1176 if (!COM_ParseTokenConsole(&data))
1178 if (com_token[0] == '{')
1180 if (com_token[0] == '}')
1182 // skip leading whitespace
1183 for (k = 0;com_token[k] && com_token[k] <= ' ';k++);
1184 for (l = 0;l < (int)sizeof(keyname) - 1 && com_token[k+l] && com_token[k+l] > ' ';l++)
1185 keyname[l] = com_token[k+l];
1187 if (!COM_ParseTokenConsole(&data))
1189 if (developer.integer >= 100)
1190 Con_Printf("key: %s %s\n", keyname, com_token);
1191 if (!strcmp(keyname, "message"))
1193 // get the message contents
1194 strlcpy(message, com_token, sizeof(message));
1204 *(t->filenames[i]+len[i]+5) = 0;
1207 case Q3BSPVERSION: strlcpy((char *)buf, "Q3", sizeof(buf));break;
1208 case Q2BSPVERSION: strlcpy((char *)buf, "Q2", sizeof(buf));break;
1209 case BSPVERSION: strlcpy((char *)buf, "Q1", sizeof(buf));break;
1210 case MCBSPVERSION: strlcpy((char *)buf, "MC", sizeof(buf));break;
1211 case 30: strlcpy((char *)buf, "HL", sizeof(buf));break;
1212 default: strlcpy((char *)buf, "??", sizeof(buf));break;
1214 Con_Printf("%16s (%s) %s\n", t->filenames[i]+5, buf, message);
1219 k = *(t->filenames[0]+5+p);
1222 for(i=1;i<t->numfilenames;i++)
1223 if(*(t->filenames[i]+5+p) != k)
1229 memset(completedname, 0, completednamebufferlength);
1230 memcpy(completedname, (t->filenames[0]+5), min(p, completednamebufferlength - 1));
1240 New function for tab-completion system
1241 Added by EvilTypeGuy
1242 MEGA Thanks to Taniwha
1245 void Con_DisplayList(const char **list)
1247 int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
1248 const char **walk = list;
1251 len = (int)strlen(*walk);
1259 len = (int)strlen(*list);
1260 if (pos + maxlen >= width) {
1266 for (i = 0; i < (maxlen - len); i++)
1278 Con_CompleteCommandLine
1280 New function for tab-completion system
1281 Added by EvilTypeGuy
1282 Thanks to Fett erich@heintz.com
1284 Enhanced to tab-complete map names by [515]
1287 void Con_CompleteCommandLine (void)
1289 const char *cmd = "";
1291 const char **list[3] = {0, 0, 0};
1293 int c, v, a, i, cmd_len, pos, k;
1295 //find what we want to complete
1299 k = key_lines[edit_line][pos];
1300 if(k == '\"' || k == ';' || k == ' ' || k == '\'')
1305 s = key_lines[edit_line] + pos;
1306 strlcpy(s2, key_lines[edit_line] + key_linepos, sizeof(s2)); //save chars after cursor
1307 key_lines[edit_line][key_linepos] = 0; //hide them
1310 for(k=pos-1;k>2;k--)
1311 if(key_lines[edit_line][k] != ' ')
1313 if(key_lines[edit_line][k] == '\"' || key_lines[edit_line][k] == ';' || key_lines[edit_line][k] == '\'')
1315 if ((pos+k > 2 && !strncmp(key_lines[edit_line]+k-2, "map", 3))
1316 || (pos+k > 10 && !strncmp(key_lines[edit_line]+k-10, "changelevel", 11)))
1319 if (GetMapList(s, t, sizeof(t)))
1321 // first move the cursor
1322 key_linepos += (int)strlen(t) - (int)strlen(s);
1324 // and now do the actual work
1326 strlcat(key_lines[edit_line], t, MAX_INPUTLINE);
1327 strlcat(key_lines[edit_line], s2, MAX_INPUTLINE); //add back chars after cursor
1329 // and fix the cursor
1330 if(key_linepos > (int) strlen(key_lines[edit_line]))
1331 key_linepos = (int) strlen(key_lines[edit_line]);
1337 // Count number of possible matches and print them
1338 c = Cmd_CompleteCountPossible(s);
1341 Con_Printf("\n%i possible command%s\n", c, (c > 1) ? "s: " : ":");
1342 Cmd_CompleteCommandPrint(s);
1344 v = Cvar_CompleteCountPossible(s);
1347 Con_Printf("\n%i possible variable%s\n", v, (v > 1) ? "s: " : ":");
1348 Cvar_CompleteCvarPrint(s);
1350 a = Cmd_CompleteAliasCountPossible(s);
1353 Con_Printf("\n%i possible aliases%s\n", a, (a > 1) ? "s: " : ":");
1354 Cmd_CompleteAliasPrint(s);
1357 if (!(c + v + a)) // No possible matches
1360 strlcpy(&key_lines[edit_line][key_linepos], s2, sizeof(key_lines[edit_line]) - key_linepos);
1365 cmd = *(list[0] = Cmd_CompleteBuildList(s));
1367 cmd = *(list[1] = Cvar_CompleteBuildList(s));
1369 cmd = *(list[2] = Cmd_CompleteAliasBuildList(s));
1371 for (cmd_len = (int)strlen(s);;cmd_len++)
1374 for (i = 0; i < 3; i++)
1376 for (l = list[i];*l;l++)
1377 if ((*l)[cmd_len] != cmd[cmd_len])
1379 // all possible matches share this character, so we continue...
1382 // if all matches ended at the same position, stop
1383 // (this means there is only one match)
1389 // prevent a buffer overrun by limiting cmd_len according to remaining space
1390 cmd_len = min(cmd_len, (int)sizeof(key_lines[edit_line]) - 1 - pos);
1394 memcpy(&key_lines[edit_line][key_linepos], cmd, cmd_len);
1395 key_linepos += cmd_len;
1396 // if there is only one match, add a space after it
1397 if (c + v + a == 1 && key_linepos < (int)sizeof(key_lines[edit_line]) - 1)
1398 key_lines[edit_line][key_linepos++] = ' ';
1401 // use strlcat to avoid a buffer overrun
1402 key_lines[edit_line][key_linepos] = 0;
1403 strlcat(key_lines[edit_line], s2, sizeof(key_lines[edit_line]));
1405 // free the command, cvar, and alias lists
1406 for (i = 0; i < 3; i++)
1408 Mem_Free((void *)list[i]);