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 cvar_t log_dest_udp = {0, "log_dest_udp","", "UDP address to log messages to (in QW rcon compatible format); multiple destinations can be separated by spaces; DO NOT SPECIFY DNS NAMES HERE"};
80 char log_dest_buffer[1400]; // UDP packet
81 size_t log_dest_buffer_pos;
82 qboolean log_dest_buffer_appending;
83 char crt_log_file [MAX_OSPATH] = "";
84 qfile_t* logfile = NULL;
86 unsigned char* logqueue = NULL;
90 void Log_ConPrint (const char *msg);
97 static void Log_DestBuffer_Init()
99 memcpy(log_dest_buffer, "\377\377\377\377n", 5); // QW rcon print
100 log_dest_buffer_pos = 5;
108 void Log_DestBuffer_Flush()
110 lhnetaddress_t log_dest_addr;
111 lhnetsocket_t *log_dest_socket;
112 const char *s = log_dest_udp.string;
113 qboolean have_opened_temp_sockets = false;
114 if(s) if(log_dest_buffer_pos > 5)
116 ++log_dest_buffer_appending;
117 log_dest_buffer[log_dest_buffer_pos++] = 0;
119 if(!NetConn_HaveServerPorts() && !NetConn_HaveClientPorts()) // then temporarily open one
121 have_opened_temp_sockets = true;
122 NetConn_OpenServerPorts(true);
125 while(COM_ParseToken_Console(&s))
126 if(LHNETADDRESS_FromString(&log_dest_addr, com_token, 26000))
128 log_dest_socket = NetConn_ChooseClientSocketForAddress(&log_dest_addr);
130 log_dest_socket = NetConn_ChooseServerSocketForAddress(&log_dest_addr);
132 NetConn_WriteString(log_dest_socket, log_dest_buffer, &log_dest_addr);
135 if(have_opened_temp_sockets)
136 NetConn_CloseServerPorts();
137 --log_dest_buffer_appending;
139 log_dest_buffer_pos = 0;
147 const char* Log_Timestamp (const char *desc)
149 static char timestamp [128];
151 const struct tm *crt_tm;
152 char timestring [64];
154 // Build the time stamp (ex: "Wed Jun 30 21:49:08 1993");
156 crt_tm = localtime (&crt_time);
157 strftime (timestring, sizeof (timestring), "%a %b %d %H:%M:%S %Y", crt_tm);
160 dpsnprintf (timestamp, sizeof (timestamp), "====== %s (%s) ======\n", desc, timestring);
162 dpsnprintf (timestamp, sizeof (timestamp), "====== %s ======\n", timestring);
175 if (logfile != NULL || log_file.string[0] == '\0')
178 logfile = FS_Open (log_file.string, "ab", false, false);
181 strlcpy (crt_log_file, log_file.string, sizeof (crt_log_file));
182 FS_Print (logfile, Log_Timestamp ("Log started"));
192 void Log_Close (void)
197 FS_Print (logfile, Log_Timestamp ("Log stopped"));
198 FS_Print (logfile, "\n");
202 crt_log_file[0] = '\0';
211 void Log_Start (void)
217 // Dump the contents of the log queue into the log file and free it
218 if (logqueue != NULL)
220 unsigned char *temp = logqueue;
225 FS_Write (logfile, temp, logq_ind);
226 if(*log_dest_udp.string)
228 for(pos = 0; pos < logq_ind; )
230 if(log_dest_buffer_pos == 0)
231 Log_DestBuffer_Init();
232 n = min(sizeof(log_dest_buffer) - log_dest_buffer_pos - 1, logq_ind - pos);
233 memcpy(log_dest_buffer + log_dest_buffer_pos, temp + pos, n);
234 log_dest_buffer_pos += n;
235 Log_DestBuffer_Flush();
252 void Log_ConPrint (const char *msg)
254 static qboolean inprogress = false;
256 // don't allow feedback loops with memory error reports
261 // Until the host is completely initialized, we maintain a log queue
262 // to store the messages, since the log can't be started before
263 if (logqueue != NULL)
265 size_t remain = logq_size - logq_ind;
266 size_t len = strlen (msg);
268 // If we need to enlarge the log queue
271 size_t factor = ((logq_ind + len) / logq_size) + 1;
272 unsigned char* newqueue;
275 newqueue = (unsigned char *)Mem_Alloc (tempmempool, logq_size);
276 memcpy (newqueue, logqueue, logq_ind);
279 remain = logq_size - logq_ind;
281 memcpy (&logqueue[logq_ind], msg, len);
288 // Check if log_file has changed
289 if (strcmp (crt_log_file, log_file.string) != 0)
295 // If a log file is available
297 FS_Print (logfile, msg);
308 void Log_Printf (const char *logfilename, const char *fmt, ...)
312 file = FS_Open (logfilename, "ab", true, false);
317 va_start (argptr, fmt);
318 FS_VPrintf (file, fmt, argptr);
327 ==============================================================================
331 ==============================================================================
339 void Con_ToggleConsole_f (void)
341 // toggle the 'user wants console' bit
342 key_consoleactive ^= KEY_CONSOLEACTIVE_USER;
343 memset (con_times, 0, sizeof(con_times));
351 void Con_Clear_f (void)
354 memset (con_text, ' ', CON_TEXTSIZE);
363 void Con_ClearNotify (void)
367 for (i=0 ; i<MAX_NOTIFYLINES ; i++)
377 void Con_MessageMode_f (void)
379 key_dest = key_message;
389 void Con_MessageMode2_f (void)
391 key_dest = key_message;
400 If the line width has changed, reformat the buffer.
403 void Con_CheckResize (void)
405 int i, j, width, oldwidth, oldtotallines, numlines, numchars;
407 char tbuf[CON_TEXTSIZE];
409 f = bound(1, con_textsize.value, 128);
410 if(f != con_textsize.value)
411 Cvar_SetValueQuick(&con_textsize, f);
412 width = (int)floor(vid_conwidth.value / con_textsize.value);
413 width = bound(1, width, CON_TEXTSIZE/4);
415 if (width == con_linewidth)
418 oldwidth = con_linewidth;
419 con_linewidth = width;
420 oldtotallines = con_totallines;
421 con_totallines = CON_TEXTSIZE / con_linewidth;
422 numlines = oldtotallines;
424 if (con_totallines < numlines)
425 numlines = con_totallines;
429 if (con_linewidth < numchars)
430 numchars = con_linewidth;
432 memcpy (tbuf, con_text, CON_TEXTSIZE);
433 memset (con_text, ' ', CON_TEXTSIZE);
435 for (i=0 ; i<numlines ; i++)
437 for (j=0 ; j<numchars ; j++)
439 con_text[(con_totallines - 1 - i) * con_linewidth + j] =
440 tbuf[((con_current - i + oldtotallines) %
441 oldtotallines) * oldwidth + j];
448 con_current = con_totallines - 1;
451 //[515]: the simplest command ever
452 //LordHavoc: not so simple after I made it print usage...
453 static void Con_Maps_f (void)
457 Con_Printf("usage: maps [mapnameprefix]\n");
460 else if (Cmd_Argc() == 2)
461 GetMapList(Cmd_Argv(1), NULL, 0);
463 GetMapList("", NULL, 0);
466 void Con_ConDump_f (void)
469 qboolean allblankssofar;
472 char temp[MAX_INPUTLINE+2];
475 Con_Printf("usage: condump <filename>\n");
478 file = FS_Open(Cmd_Argv(1), "wb", false, false);
481 Con_Printf("condump: unable to write file \"%s\"\n", Cmd_Argv(1));
484 // iterate over the entire console history buffer line by line
485 allblankssofar = true;
486 for (i = 0;i < con_totallines;i++)
488 text = con_text + ((con_current + 1 + i) % con_totallines)*con_linewidth;
489 // count the used characters on this line
490 for (l = min(con_linewidth, (int)sizeof(temp));l > 0 && text[l-1] == ' ';l--);
491 // if not a blank line, begin output
493 allblankssofar = false;
494 // output the current line to the file
498 memcpy(temp, text, l);
501 FS_Print(file, temp);
514 memset (con_text, ' ', CON_TEXTSIZE);
516 con_totallines = CON_TEXTSIZE / con_linewidth;
518 // Allocate a log queue, this will be freed after configs are parsed
519 logq_size = MAX_INPUTLINE;
520 logqueue = (unsigned char *)Mem_Alloc (tempmempool, logq_size);
523 Cvar_RegisterVariable (&sys_colortranslation);
524 Cvar_RegisterVariable (&sys_specialcharactertranslation);
526 Cvar_RegisterVariable (&log_file);
527 Cvar_RegisterVariable (&log_dest_udp);
529 // support for the classic Quake option
530 // COMMANDLINEOPTION: Console: -condebug logs console messages to qconsole.log, see also log_file
531 if (COM_CheckParm ("-condebug") != 0)
532 Cvar_SetQuick (&log_file, "qconsole.log");
534 // register our cvars
535 Cvar_RegisterVariable (&con_notifytime);
536 Cvar_RegisterVariable (&con_notify);
537 Cvar_RegisterVariable (&con_textsize);
539 // register our commands
540 Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f, "opens or closes the console");
541 Cmd_AddCommand ("messagemode", Con_MessageMode_f, "input a chat message to say to everyone");
542 Cmd_AddCommand ("messagemode2", Con_MessageMode2_f, "input a chat message to say to only your team");
543 Cmd_AddCommand ("clear", Con_Clear_f, "clear console history");
544 Cmd_AddCommand ("maps", Con_Maps_f, "list information about available maps");
545 Cmd_AddCommand ("condump", Con_ConDump_f, "output console history to a file (see also log_file)");
547 con_initialized = true;
548 Con_Print("Console initialized.\n");
557 void Con_Linefeed (void)
564 memset (&con_text[(con_current%con_totallines)*con_linewidth], ' ', con_linewidth);
571 Handles cursor positioning, line wrapping, etc
572 All console printing must go through this in order to be displayed
573 If no console is visible, the notify window will pop up.
576 void Con_PrintToHistory(const char *txt, int mask)
584 for (l=0 ; l< con_linewidth ; l++)
589 if (l != con_linewidth && (con_x + l > con_linewidth) )
604 // mark time for transparent overlay
605 if (con_current >= 0)
607 if (con_notify.integer < 0)
608 Cvar_SetValueQuick(&con_notify, 0);
609 if (con_notify.integer > MAX_NOTIFYLINES)
610 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
611 if (con_notify.integer > 0)
612 con_times[con_current % con_notify.integer] = cl.time;
627 default: // display character and advance
628 y = con_current % con_totallines;
629 con_text[y*con_linewidth+con_x] = c | mask;
631 if (con_x >= con_linewidth)
639 /* The translation table between the graphical font and plain ASCII --KB */
640 static char qfont_table[256] = {
641 '\0', '#', '#', '#', '#', '.', '#', '#',
642 '#', 9, 10, '#', ' ', 13, '.', '.',
643 '[', ']', '0', '1', '2', '3', '4', '5',
644 '6', '7', '8', '9', '.', '<', '=', '>',
645 ' ', '!', '"', '#', '$', '%', '&', '\'',
646 '(', ')', '*', '+', ',', '-', '.', '/',
647 '0', '1', '2', '3', '4', '5', '6', '7',
648 '8', '9', ':', ';', '<', '=', '>', '?',
649 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
650 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
651 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
652 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
653 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
654 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
655 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
656 'x', 'y', 'z', '{', '|', '}', '~', '<',
658 '<', '=', '>', '#', '#', '.', '#', '#',
659 '#', '#', ' ', '#', ' ', '>', '.', '.',
660 '[', ']', '0', '1', '2', '3', '4', '5',
661 '6', '7', '8', '9', '.', '<', '=', '>',
662 ' ', '!', '"', '#', '$', '%', '&', '\'',
663 '(', ')', '*', '+', ',', '-', '.', '/',
664 '0', '1', '2', '3', '4', '5', '6', '7',
665 '8', '9', ':', ';', '<', '=', '>', '?',
666 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
667 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
668 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
669 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
670 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
671 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
672 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
673 'x', 'y', 'z', '{', '|', '}', '~', '<'
680 Adds a character to the rcon buffer
683 void Con_Rcon_AddChar(char c)
685 if(log_dest_buffer_appending)
687 ++log_dest_buffer_appending;
689 // if this print is in response to an rcon command, add the character
690 // to the rcon redirect buffer
692 if (rcon_redirect && rcon_redirect_bufferpos < (int)sizeof(rcon_redirect_buffer) - 1)
693 rcon_redirect_buffer[rcon_redirect_bufferpos++] = c;
694 else if(*log_dest_udp.string) // don't duplicate rcon command responses here, these are sent another way
696 if(log_dest_buffer_pos == 0)
697 Log_DestBuffer_Init();
698 log_dest_buffer[log_dest_buffer_pos++] = c;
699 if(log_dest_buffer_pos >= sizeof(log_dest_buffer) - 1) // minus one, to allow for terminating zero
700 Log_DestBuffer_Flush();
703 log_dest_buffer_pos = 0;
705 --log_dest_buffer_appending;
712 Prints to all appropriate console targets, and adds timestamps
715 extern cvar_t timestamps;
716 extern cvar_t timeformat;
717 extern qboolean sys_nostdout;
718 void Con_Print(const char *msg)
721 static int index = 0;
722 static char line[MAX_INPUTLINE];
726 Con_Rcon_AddChar(*msg);
727 // if this is the beginning of a new line, print timestamp
730 const char *timestamp = timestamps.integer ? Sys_TimeString(timeformat.string) : "";
732 // FIXME: 1. perhaps we should use a terminal system 2. use a constant instead of 7!
733 line[index++] = STRING_COLOR_TAG;
734 // assert( STRING_COLOR_DEFAULT < 10 )
735 line[index++] = STRING_COLOR_DEFAULT + '0';
736 // special color codes for chat messages must always come first
737 // for Con_PrintToHistory to work properly
738 if (*msg == 1 || *msg == 2)
743 if (msg[1] == '(' && cl.foundtalk2wav)
744 S_LocalSound ("sound/misc/talk2.wav");
746 S_LocalSound ("sound/misc/talk.wav");
748 line[index++] = STRING_COLOR_TAG;
751 Con_Rcon_AddChar(*msg);
754 for (;*timestamp;index++, timestamp++)
755 if (index < (int)sizeof(line) - 2)
756 line[index] = *timestamp;
758 // append the character
759 line[index++] = *msg;
760 // if this is a newline character, we have a complete line to print
761 if (*msg == '\n' || index >= (int)sizeof(line) / 2)
763 // terminate the line
767 // send to scrollable buffer
768 if (con_initialized && cls.state != ca_dedicated)
769 Con_PrintToHistory(line, mask);
770 // send to terminal or dedicated server window
774 if(sys_specialcharactertranslation.integer)
776 for (p = (unsigned char *) line;*p; p++)
777 *p = qfont_table[*p];
780 if(sys_colortranslation.integer == 1) // ANSI
782 static char printline[MAX_INPUTLINE * 4 + 3];
783 // 2 can become 7 bytes, rounding that up to 8, and 3 bytes are added at the end
784 // a newline can transform into four bytes, but then prevents the three extra bytes from appearing
788 for(in = line, out = printline; *in; ++in)
792 case STRING_COLOR_TAG:
795 case STRING_COLOR_TAG:
797 *out++ = STRING_COLOR_TAG;
803 if(lastcolor == 0) break; else lastcolor = 0;
804 *out++ = 0x1B; *out++ = '['; *out++ = 'm';
809 if(lastcolor == 1) break; else lastcolor = 1;
810 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '1'; *out++ = 'm';
815 if(lastcolor == 2) break; else lastcolor = 2;
816 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '2'; *out++ = 'm';
821 if(lastcolor == 3) break; else lastcolor = 3;
822 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '3'; *out++ = 'm';
827 if(lastcolor == 4) break; else lastcolor = 4;
828 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '4'; *out++ = 'm';
833 if(lastcolor == 5) break; else lastcolor = 5;
834 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '6'; *out++ = 'm';
839 if(lastcolor == 6) break; else lastcolor = 6;
840 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '5'; *out++ = 'm';
847 if(lastcolor == 8) break; else lastcolor = 8;
848 *out++ = 0x1B; *out++ = '['; *out++ = '0'; *out++ = ';'; *out++ = '1'; *out++ = 'm';
851 *out++ = STRING_COLOR_TAG;
858 *out++ = 0x1B; *out++ = '['; *out++ = 'm';
875 Sys_PrintToTerminal(printline);
877 else if(sys_colortranslation.integer == 2) // Quake
879 Sys_PrintToTerminal(line);
883 static char printline[MAX_INPUTLINE]; // it can only get shorter here
886 for(in = line, out = printline; *in; ++in)
890 case STRING_COLOR_TAG:
893 case STRING_COLOR_TAG:
895 *out++ = STRING_COLOR_TAG;
910 *out++ = STRING_COLOR_TAG;
920 Sys_PrintToTerminal(printline);
923 // empty the line buffer
934 Prints to all appropriate console targets
937 void Con_Printf(const char *fmt, ...)
940 char msg[MAX_INPUTLINE];
942 va_start(argptr,fmt);
943 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
953 A Con_Print that only shows up if the "developer" cvar is set
956 void Con_DPrint(const char *msg)
958 if (!developer.integer)
959 return; // don't confuse non-developers with techie stuff...
967 A Con_Printf that only shows up if the "developer" cvar is set
970 void Con_DPrintf(const char *fmt, ...)
973 char msg[MAX_INPUTLINE];
975 if (!developer.integer)
976 return; // don't confuse non-developers with techie stuff...
978 va_start(argptr,fmt);
979 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
987 ==============================================================================
991 ==============================================================================
998 The input line scrolls horizontally if typing goes beyond the right edge
1000 Modified by EvilTypeGuy eviltypeguy@qeradiant.com
1003 void Con_DrawInput (void)
1007 char editlinecopy[MAX_INPUTLINE+1], *text;
1009 if (!key_consoleactive)
1010 return; // don't draw anything
1012 strlcpy(editlinecopy, key_lines[edit_line], sizeof(editlinecopy));
1013 text = editlinecopy;
1015 // Advanced Console Editing by Radix radix@planetquake.com
1016 // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
1017 // use strlen of edit_line instead of key_linepos to allow editing
1018 // of early characters w/o erasing
1020 y = (int)strlen(text);
1022 // fill out remainder with spaces
1023 for (i = y; i < (int)sizeof(editlinecopy)-1; i++)
1026 // add the cursor frame
1027 if ((int)(realtime*con_cursorspeed) & 1) // cursor is visible
1028 text[key_linepos] = 11 + 130 * key_insert; // either solid or triangle facing right
1030 // text[key_linepos + 1] = 0;
1032 // prestep if horizontally scrolling
1033 if (key_linepos >= con_linewidth)
1034 text += 1 + key_linepos - con_linewidth;
1037 DrawQ_String(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, false );
1040 // key_lines[edit_line][key_linepos] = 0;
1048 Draws the last few lines of output transparently over the game top
1051 void Con_DrawNotify (void)
1057 char temptext[MAX_INPUTLINE];
1058 int colorindex = -1; //-1 for default
1060 if (con_notify.integer < 0)
1061 Cvar_SetValueQuick(&con_notify, 0);
1062 if (con_notify.integer > MAX_NOTIFYLINES)
1063 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
1064 if (gamemode == GAME_TRANSFUSION)
1068 // 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
1070 for (i= stop-con_notify.integer+1 ; i<=stop ; i++)
1075 time = con_times[i % con_notify.integer];
1078 time = cl.time - time;
1079 if (time > con_notifytime.value)
1081 text = con_text + (i % con_totallines)*con_linewidth;
1083 if (gamemode == GAME_NEXUIZ) {
1088 // count up to the last non-whitespace, and ignore color codes
1089 for (j = 0;j < con_linewidth && text[j];j++)
1091 if (text[j] == STRING_COLOR_TAG && (text[j+1] >= '0' && text[j+1] <= '9'))
1101 // center the line using the calculated width
1102 x = (vid_conwidth.integer - finalchars * con_textsize.value) * 0.5;
1106 DrawQ_String( x, v, text, con_linewidth, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, &colorindex, false );
1108 v += con_textsize.value;
1112 if (key_dest == key_message)
1114 int colorindex = -1;
1118 // LordHavoc: speedup, and other improvements
1120 sprintf(temptext, "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
1122 sprintf(temptext, "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
1123 while ((int)strlen(temptext) >= con_linewidth)
1125 DrawQ_String( 0, v, temptext, con_linewidth, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, &colorindex, false );
1126 strlcpy(temptext, &temptext[con_linewidth], sizeof(temptext));
1127 v += con_textsize.value;
1129 if (strlen(temptext) > 0)
1131 DrawQ_String( 0, v, temptext, 0, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, &colorindex, false );
1132 v += con_textsize.value;
1141 Draws the console with the solid background
1142 The typing input line at the bottom should only be drawn if typing is allowed
1145 void Con_DrawConsole (int lines)
1147 int i, rows, j, stop;
1150 int colorindex = -1;
1155 // draw the background
1156 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);
1157 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, NULL, true);
1160 con_vislines = lines;
1162 rows = (int)ceil((lines/con_textsize.value)-2); // rows of text to draw
1163 y = lines - (rows+2)*con_textsize.value; // may start slightly negative
1165 // 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
1167 for (i = stop - rows + 1;i <= stop;i++, y += con_textsize.value)
1169 j = max(i - con_backscroll, 0);
1170 text = con_text + (j % con_totallines)*con_linewidth;
1172 DrawQ_String( 0, y, text, con_linewidth, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, &colorindex, false );
1175 // draw the input prompt, user text, and cursor if desired
1183 Prints not only map filename, but also
1184 its format (q1/q2/q3/hl) and even its message
1186 //[515]: here is an ugly hack.. two gotos... oh my... *but it works*
1187 //LordHavoc: rewrote bsp type detection, added mcbsp support and rewrote message extraction to do proper worldspawn parsing
1188 //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
1189 //LordHavoc: FIXME: man this GetMapList is STILL ugly code even after my cleanups...
1190 qboolean GetMapList (const char *s, char *completedname, int completednamebufferlength)
1194 int i, k, max, p, o, min;
1197 unsigned char buf[1024];
1199 sprintf(message, "maps/%s*.bsp", s);
1200 t = FS_Search(message, 1, true);
1203 if (t->numfilenames > 1)
1204 Con_Printf("^1 %i maps found :\n", t->numfilenames);
1205 len = (unsigned char *)Z_Malloc(t->numfilenames);
1207 for(max=i=0;i<t->numfilenames;i++)
1209 k = (int)strlen(t->filenames[i]);
1219 for(i=0;i<t->numfilenames;i++)
1221 int lumpofs = 0, lumplen = 0;
1222 char *entities = NULL;
1223 const char *data = NULL;
1225 char entfilename[MAX_QPATH];
1226 strlcpy(message, "^1**ERROR**^7", sizeof(message));
1228 f = FS_Open(t->filenames[i], "rb", true, false);
1231 memset(buf, 0, 1024);
1232 FS_Read(f, buf, 1024);
1233 if (!memcmp(buf, "IBSP", 4))
1235 p = LittleLong(((int *)buf)[1]);
1236 if (p == Q3BSPVERSION)
1238 q3dheader_t *header = (q3dheader_t *)buf;
1239 lumpofs = LittleLong(header->lumps[Q3LUMP_ENTITIES].fileofs);
1240 lumplen = LittleLong(header->lumps[Q3LUMP_ENTITIES].filelen);
1242 else if (p == Q2BSPVERSION)
1244 q2dheader_t *header = (q2dheader_t *)buf;
1245 lumpofs = LittleLong(header->lumps[Q2LUMP_ENTITIES].fileofs);
1246 lumplen = LittleLong(header->lumps[Q2LUMP_ENTITIES].filelen);
1249 else if (!memcmp(buf, "MCBSPpad", 8))
1251 p = LittleLong(((int *)buf)[2]);
1252 if (p == MCBSPVERSION)
1254 int numhulls = LittleLong(((int *)buf)[3]);
1255 lumpofs = LittleLong(((int *)buf)[3 + numhulls + LUMP_ENTITIES*2+0]);
1256 lumplen = LittleLong(((int *)buf)[3 + numhulls + LUMP_ENTITIES*2+1]);
1259 else if((p = LittleLong(((int *)buf)[0])) == BSPVERSION || p == 30)
1261 dheader_t *header = (dheader_t *)buf;
1262 lumpofs = LittleLong(header->lumps[LUMP_ENTITIES].fileofs);
1263 lumplen = LittleLong(header->lumps[LUMP_ENTITIES].filelen);
1267 strlcpy(entfilename, t->filenames[i], sizeof(entfilename));
1268 memcpy(entfilename + strlen(entfilename) - 4, ".ent", 5);
1269 entities = (char *)FS_LoadFile(entfilename, tempmempool, true, NULL);
1270 if (!entities && lumplen >= 10)
1272 FS_Seek(f, lumpofs, SEEK_SET);
1273 entities = (char *)Z_Malloc(lumplen + 1);
1274 FS_Read(f, entities, lumplen);
1278 // if there are entities to parse, a missing message key just
1279 // means there is no title, so clear the message string now
1285 if (!COM_ParseToken_Simple(&data, false))
1287 if (com_token[0] == '{')
1289 if (com_token[0] == '}')
1291 // skip leading whitespace
1292 for (k = 0;com_token[k] && com_token[k] <= ' ';k++);
1293 for (l = 0;l < (int)sizeof(keyname) - 1 && com_token[k+l] && com_token[k+l] > ' ';l++)
1294 keyname[l] = com_token[k+l];
1296 if (!COM_ParseToken_Simple(&data, false))
1298 if (developer.integer >= 100)
1299 Con_Printf("key: %s %s\n", keyname, com_token);
1300 if (!strcmp(keyname, "message"))
1302 // get the message contents
1303 strlcpy(message, com_token, sizeof(message));
1313 *(t->filenames[i]+len[i]+5) = 0;
1316 case Q3BSPVERSION: strlcpy((char *)buf, "Q3", sizeof(buf));break;
1317 case Q2BSPVERSION: strlcpy((char *)buf, "Q2", sizeof(buf));break;
1318 case BSPVERSION: strlcpy((char *)buf, "Q1", sizeof(buf));break;
1319 case MCBSPVERSION: strlcpy((char *)buf, "MC", sizeof(buf));break;
1320 case 30: strlcpy((char *)buf, "HL", sizeof(buf));break;
1321 default: strlcpy((char *)buf, "??", sizeof(buf));break;
1323 Con_Printf("%16s (%s) %s\n", t->filenames[i]+5, buf, message);
1328 k = *(t->filenames[0]+5+p);
1331 for(i=1;i<t->numfilenames;i++)
1332 if(*(t->filenames[i]+5+p) != k)
1336 if(p > o && completedname && completednamebufferlength > 0)
1338 memset(completedname, 0, completednamebufferlength);
1339 memcpy(completedname, (t->filenames[0]+5), min(p, completednamebufferlength - 1));
1349 New function for tab-completion system
1350 Added by EvilTypeGuy
1351 MEGA Thanks to Taniwha
1354 void Con_DisplayList(const char **list)
1356 int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
1357 const char **walk = list;
1360 len = (int)strlen(*walk);
1368 len = (int)strlen(*list);
1369 if (pos + maxlen >= width) {
1375 for (i = 0; i < (maxlen - len); i++)
1387 Con_CompleteCommandLine
1389 New function for tab-completion system
1390 Added by EvilTypeGuy
1391 Thanks to Fett erich@heintz.com
1393 Enhanced to tab-complete map names by [515]
1396 void Con_CompleteCommandLine (void)
1398 const char *cmd = "";
1400 const char **list[3] = {0, 0, 0};
1402 int c, v, a, i, cmd_len, pos, k;
1404 //find what we want to complete
1408 k = key_lines[edit_line][pos];
1409 if(k == '\"' || k == ';' || k == ' ' || k == '\'')
1414 s = key_lines[edit_line] + pos;
1415 strlcpy(s2, key_lines[edit_line] + key_linepos, sizeof(s2)); //save chars after cursor
1416 key_lines[edit_line][key_linepos] = 0; //hide them
1419 for(k=pos-1;k>2;k--)
1420 if(key_lines[edit_line][k] != ' ')
1422 if(key_lines[edit_line][k] == '\"' || key_lines[edit_line][k] == ';' || key_lines[edit_line][k] == '\'')
1424 if ((pos+k > 2 && !strncmp(key_lines[edit_line]+k-2, "map", 3))
1425 || (pos+k > 10 && !strncmp(key_lines[edit_line]+k-10, "changelevel", 11)))
1428 if (GetMapList(s, t, sizeof(t)))
1430 // first move the cursor
1431 key_linepos += (int)strlen(t) - (int)strlen(s);
1433 // and now do the actual work
1435 strlcat(key_lines[edit_line], t, MAX_INPUTLINE);
1436 strlcat(key_lines[edit_line], s2, MAX_INPUTLINE); //add back chars after cursor
1438 // and fix the cursor
1439 if(key_linepos > (int) strlen(key_lines[edit_line]))
1440 key_linepos = (int) strlen(key_lines[edit_line]);
1446 // Count number of possible matches and print them
1447 c = Cmd_CompleteCountPossible(s);
1450 Con_Printf("\n%i possible command%s\n", c, (c > 1) ? "s: " : ":");
1451 Cmd_CompleteCommandPrint(s);
1453 v = Cvar_CompleteCountPossible(s);
1456 Con_Printf("\n%i possible variable%s\n", v, (v > 1) ? "s: " : ":");
1457 Cvar_CompleteCvarPrint(s);
1459 a = Cmd_CompleteAliasCountPossible(s);
1462 Con_Printf("\n%i possible aliases%s\n", a, (a > 1) ? "s: " : ":");
1463 Cmd_CompleteAliasPrint(s);
1466 if (!(c + v + a)) // No possible matches
1469 strlcpy(&key_lines[edit_line][key_linepos], s2, sizeof(key_lines[edit_line]) - key_linepos);
1474 cmd = *(list[0] = Cmd_CompleteBuildList(s));
1476 cmd = *(list[1] = Cvar_CompleteBuildList(s));
1478 cmd = *(list[2] = Cmd_CompleteAliasBuildList(s));
1480 for (cmd_len = (int)strlen(s);;cmd_len++)
1483 for (i = 0; i < 3; i++)
1485 for (l = list[i];*l;l++)
1486 if ((*l)[cmd_len] != cmd[cmd_len])
1488 // all possible matches share this character, so we continue...
1491 // if all matches ended at the same position, stop
1492 // (this means there is only one match)
1498 // prevent a buffer overrun by limiting cmd_len according to remaining space
1499 cmd_len = min(cmd_len, (int)sizeof(key_lines[edit_line]) - 1 - pos);
1503 memcpy(&key_lines[edit_line][key_linepos], cmd, cmd_len);
1504 key_linepos += cmd_len;
1505 // if there is only one match, add a space after it
1506 if (c + v + a == 1 && key_linepos < (int)sizeof(key_lines[edit_line]) - 1)
1507 key_lines[edit_line][key_linepos++] = ' ';
1510 // use strlcat to avoid a buffer overrun
1511 key_lines[edit_line][key_linepos] = 0;
1512 strlcat(key_lines[edit_line], s2, sizeof(key_lines[edit_line]));
1514 // free the command, cvar, and alias lists
1515 for (i = 0; i < 3; i++)
1517 Mem_Free((void *)list[i]);