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[1500]; // UDP packet
81 size_t log_dest_buffer_pos;
82 char crt_log_file [MAX_OSPATH] = "";
83 qfile_t* logfile = NULL;
85 unsigned char* logqueue = NULL;
89 void Log_ConPrint (const char *msg);
96 static void Log_DestBuffer_Init()
98 memcpy(log_dest_buffer, "\377\377\377\377n", 5); // QW rcon print
99 log_dest_buffer_pos = 5;
107 void Log_DestBuffer_Flush()
109 lhnetaddress_t log_dest_addr;
110 lhnetsocket_t *log_dest_socket;
111 const char *s = log_dest_udp.string;
112 qboolean have_opened_temp_sockets = false;
113 if(s) if(log_dest_buffer_pos > 5)
115 log_dest_buffer[log_dest_buffer_pos++] = 0;
117 if(!NetConn_HaveServerPorts() && !NetConn_HaveClientPorts()) // then temporarily open one
119 have_opened_temp_sockets = true;
120 NetConn_OpenServerPorts(true);
123 while(COM_ParseToken_Console(&s))
124 if(LHNETADDRESS_FromString(&log_dest_addr, com_token, 26000))
126 log_dest_socket = NetConn_ChooseClientSocketForAddress(&log_dest_addr);
128 log_dest_socket = NetConn_ChooseServerSocketForAddress(&log_dest_addr);
130 NetConn_WriteString(log_dest_socket, log_dest_buffer, &log_dest_addr);
133 if(have_opened_temp_sockets)
134 NetConn_CloseServerPorts();
136 log_dest_buffer_pos = 0;
144 const char* Log_Timestamp (const char *desc)
146 static char timestamp [128];
148 const struct tm *crt_tm;
149 char timestring [64];
151 // Build the time stamp (ex: "Wed Jun 30 21:49:08 1993");
153 crt_tm = localtime (&crt_time);
154 strftime (timestring, sizeof (timestring), "%a %b %d %H:%M:%S %Y", crt_tm);
157 dpsnprintf (timestamp, sizeof (timestamp), "====== %s (%s) ======\n", desc, timestring);
159 dpsnprintf (timestamp, sizeof (timestamp), "====== %s ======\n", timestring);
172 if (logfile != NULL || log_file.string[0] == '\0')
175 logfile = FS_Open (log_file.string, "ab", false, false);
178 strlcpy (crt_log_file, log_file.string, sizeof (crt_log_file));
179 FS_Print (logfile, Log_Timestamp ("Log started"));
189 void Log_Close (void)
194 FS_Print (logfile, Log_Timestamp ("Log stopped"));
195 FS_Print (logfile, "\n");
199 crt_log_file[0] = '\0';
208 void Log_Start (void)
214 // Dump the contents of the log queue into the log file and free it
215 if (logqueue != NULL)
217 unsigned char *temp = logqueue;
222 FS_Write (logfile, temp, logq_ind);
223 if(*log_dest_udp.string)
225 for(pos = 0; pos < logq_ind; )
227 if(log_dest_buffer_pos == 0)
228 Log_DestBuffer_Init();
229 n = min(sizeof(log_dest_buffer) - log_dest_buffer_pos - 1, logq_ind - pos);
230 memcpy(log_dest_buffer + log_dest_buffer_pos, temp + pos, n);
231 log_dest_buffer_pos += n;
232 Log_DestBuffer_Flush();
249 void Log_ConPrint (const char *msg)
251 static qboolean inprogress = false;
253 // don't allow feedback loops with memory error reports
258 // Until the host is completely initialized, we maintain a log queue
259 // to store the messages, since the log can't be started before
260 if (logqueue != NULL)
262 size_t remain = logq_size - logq_ind;
263 size_t len = strlen (msg);
265 // If we need to enlarge the log queue
268 size_t factor = ((logq_ind + len) / logq_size) + 1;
269 unsigned char* newqueue;
272 newqueue = (unsigned char *)Mem_Alloc (tempmempool, logq_size);
273 memcpy (newqueue, logqueue, logq_ind);
276 remain = logq_size - logq_ind;
278 memcpy (&logqueue[logq_ind], msg, len);
285 // Check if log_file has changed
286 if (strcmp (crt_log_file, log_file.string) != 0)
292 // If a log file is available
294 FS_Print (logfile, msg);
305 void Log_Printf (const char *logfilename, const char *fmt, ...)
309 file = FS_Open (logfilename, "ab", true, false);
314 va_start (argptr, fmt);
315 FS_VPrintf (file, fmt, argptr);
324 ==============================================================================
328 ==============================================================================
336 void Con_ToggleConsole_f (void)
338 // toggle the 'user wants console' bit
339 key_consoleactive ^= KEY_CONSOLEACTIVE_USER;
340 memset (con_times, 0, sizeof(con_times));
348 void Con_Clear_f (void)
351 memset (con_text, ' ', CON_TEXTSIZE);
360 void Con_ClearNotify (void)
364 for (i=0 ; i<MAX_NOTIFYLINES ; i++)
374 void Con_MessageMode_f (void)
376 key_dest = key_message;
386 void Con_MessageMode2_f (void)
388 key_dest = key_message;
397 If the line width has changed, reformat the buffer.
400 void Con_CheckResize (void)
402 int i, j, width, oldwidth, oldtotallines, numlines, numchars;
404 char tbuf[CON_TEXTSIZE];
406 f = bound(1, con_textsize.value, 128);
407 if(f != con_textsize.value)
408 Cvar_SetValueQuick(&con_textsize, f);
409 width = (int)floor(vid_conwidth.value / con_textsize.value);
410 width = bound(1, width, CON_TEXTSIZE/4);
412 if (width == con_linewidth)
415 oldwidth = con_linewidth;
416 con_linewidth = width;
417 oldtotallines = con_totallines;
418 con_totallines = CON_TEXTSIZE / con_linewidth;
419 numlines = oldtotallines;
421 if (con_totallines < numlines)
422 numlines = con_totallines;
426 if (con_linewidth < numchars)
427 numchars = con_linewidth;
429 memcpy (tbuf, con_text, CON_TEXTSIZE);
430 memset (con_text, ' ', CON_TEXTSIZE);
432 for (i=0 ; i<numlines ; i++)
434 for (j=0 ; j<numchars ; j++)
436 con_text[(con_totallines - 1 - i) * con_linewidth + j] =
437 tbuf[((con_current - i + oldtotallines) %
438 oldtotallines) * oldwidth + j];
445 con_current = con_totallines - 1;
448 //[515]: the simplest command ever
449 //LordHavoc: not so simple after I made it print usage...
450 static void Con_Maps_f (void)
454 Con_Printf("usage: maps [mapnameprefix]\n");
457 else if (Cmd_Argc() == 2)
458 GetMapList(Cmd_Argv(1), NULL, 0);
460 GetMapList("", NULL, 0);
463 void Con_ConDump_f (void)
466 qboolean allblankssofar;
469 char temp[MAX_INPUTLINE+2];
472 Con_Printf("usage: condump <filename>\n");
475 file = FS_Open(Cmd_Argv(1), "wb", false, false);
478 Con_Printf("condump: unable to write file \"%s\"\n", Cmd_Argv(1));
481 // iterate over the entire console history buffer line by line
482 allblankssofar = true;
483 for (i = 0;i < con_totallines;i++)
485 text = con_text + ((con_current + 1 + i) % con_totallines)*con_linewidth;
486 // count the used characters on this line
487 for (l = min(con_linewidth, (int)sizeof(temp));l > 0 && text[l-1] == ' ';l--);
488 // if not a blank line, begin output
490 allblankssofar = false;
491 // output the current line to the file
495 memcpy(temp, text, l);
498 FS_Print(file, temp);
511 memset (con_text, ' ', CON_TEXTSIZE);
513 con_totallines = CON_TEXTSIZE / con_linewidth;
515 // Allocate a log queue, this will be freed after configs are parsed
516 logq_size = MAX_INPUTLINE;
517 logqueue = (unsigned char *)Mem_Alloc (tempmempool, logq_size);
520 Cvar_RegisterVariable (&sys_colortranslation);
521 Cvar_RegisterVariable (&sys_specialcharactertranslation);
523 Cvar_RegisterVariable (&log_file);
524 Cvar_RegisterVariable (&log_dest_udp);
526 // support for the classic Quake option
527 // COMMANDLINEOPTION: Console: -condebug logs console messages to qconsole.log, see also log_file
528 if (COM_CheckParm ("-condebug") != 0)
529 Cvar_SetQuick (&log_file, "qconsole.log");
531 // register our cvars
532 Cvar_RegisterVariable (&con_notifytime);
533 Cvar_RegisterVariable (&con_notify);
534 Cvar_RegisterVariable (&con_textsize);
536 // register our commands
537 Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f, "opens or closes the console");
538 Cmd_AddCommand ("messagemode", Con_MessageMode_f, "input a chat message to say to everyone");
539 Cmd_AddCommand ("messagemode2", Con_MessageMode2_f, "input a chat message to say to only your team");
540 Cmd_AddCommand ("clear", Con_Clear_f, "clear console history");
541 Cmd_AddCommand ("maps", Con_Maps_f, "list information about available maps");
542 Cmd_AddCommand ("condump", Con_ConDump_f, "output console history to a file (see also log_file)");
544 con_initialized = true;
545 Con_Print("Console initialized.\n");
554 void Con_Linefeed (void)
561 memset (&con_text[(con_current%con_totallines)*con_linewidth], ' ', con_linewidth);
568 Handles cursor positioning, line wrapping, etc
569 All console printing must go through this in order to be displayed
570 If no console is visible, the notify window will pop up.
573 void Con_PrintToHistory(const char *txt, int mask)
581 for (l=0 ; l< con_linewidth ; l++)
586 if (l != con_linewidth && (con_x + l > con_linewidth) )
601 // mark time for transparent overlay
602 if (con_current >= 0)
604 if (con_notify.integer < 0)
605 Cvar_SetValueQuick(&con_notify, 0);
606 if (con_notify.integer > MAX_NOTIFYLINES)
607 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
608 if (con_notify.integer > 0)
609 con_times[con_current % con_notify.integer] = cl.time;
624 default: // display character and advance
625 y = con_current % con_totallines;
626 con_text[y*con_linewidth+con_x] = c | mask;
628 if (con_x >= con_linewidth)
636 /* The translation table between the graphical font and plain ASCII --KB */
637 static char qfont_table[256] = {
638 '\0', '#', '#', '#', '#', '.', '#', '#',
639 '#', 9, 10, '#', ' ', 13, '.', '.',
640 '[', ']', '0', '1', '2', '3', '4', '5',
641 '6', '7', '8', '9', '.', '<', '=', '>',
642 ' ', '!', '"', '#', '$', '%', '&', '\'',
643 '(', ')', '*', '+', ',', '-', '.', '/',
644 '0', '1', '2', '3', '4', '5', '6', '7',
645 '8', '9', ':', ';', '<', '=', '>', '?',
646 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
647 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
648 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
649 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
650 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
651 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
652 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
653 'x', 'y', 'z', '{', '|', '}', '~', '<',
655 '<', '=', '>', '#', '#', '.', '#', '#',
656 '#', '#', ' ', '#', ' ', '>', '.', '.',
657 '[', ']', '0', '1', '2', '3', '4', '5',
658 '6', '7', '8', '9', '.', '<', '=', '>',
659 ' ', '!', '"', '#', '$', '%', '&', '\'',
660 '(', ')', '*', '+', ',', '-', '.', '/',
661 '0', '1', '2', '3', '4', '5', '6', '7',
662 '8', '9', ':', ';', '<', '=', '>', '?',
663 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
664 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
665 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
666 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
667 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
668 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
669 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
670 'x', 'y', 'z', '{', '|', '}', '~', '<'
677 Adds a character to the rcon buffer
680 void Con_Rcon_AddChar(char c)
682 // if this print is in response to an rcon command, add the character
683 // to the rcon redirect buffer
684 if (rcon_redirect && rcon_redirect_bufferpos < (int)sizeof(rcon_redirect_buffer) - 1)
685 rcon_redirect_buffer[rcon_redirect_bufferpos++] = c;
686 else if(*log_dest_udp.string) // don't duplicate rcon command responses here, these are sent another way
688 if(log_dest_buffer_pos == 0)
689 Log_DestBuffer_Init();
690 log_dest_buffer[log_dest_buffer_pos++] = c;
691 if(log_dest_buffer_pos >= sizeof(log_dest_buffer) - 1) // minus one, to allow for terminating zero
692 Log_DestBuffer_Flush();
695 log_dest_buffer_pos = 0;
702 Prints to all appropriate console targets, and adds timestamps
705 extern cvar_t timestamps;
706 extern cvar_t timeformat;
707 extern qboolean sys_nostdout;
708 void Con_Print(const char *msg)
711 static int index = 0;
712 static char line[MAX_INPUTLINE];
716 Con_Rcon_AddChar(*msg);
717 // if this is the beginning of a new line, print timestamp
720 const char *timestamp = timestamps.integer ? Sys_TimeString(timeformat.string) : "";
722 // FIXME: 1. perhaps we should use a terminal system 2. use a constant instead of 7!
723 line[index++] = STRING_COLOR_TAG;
724 // assert( STRING_COLOR_DEFAULT < 10 )
725 line[index++] = STRING_COLOR_DEFAULT + '0';
726 // special color codes for chat messages must always come first
727 // for Con_PrintToHistory to work properly
728 if (*msg == 1 || *msg == 2)
733 if (msg[1] == '(' && cl.foundtalk2wav)
734 S_LocalSound ("sound/misc/talk2.wav");
736 S_LocalSound ("sound/misc/talk.wav");
738 line[index++] = STRING_COLOR_TAG;
741 Con_Rcon_AddChar(*msg);
744 for (;*timestamp;index++, timestamp++)
745 if (index < (int)sizeof(line) - 2)
746 line[index] = *timestamp;
748 // append the character
749 line[index++] = *msg;
750 // if this is a newline character, we have a complete line to print
751 if (*msg == '\n' || index >= (int)sizeof(line) / 2)
753 // terminate the line
757 // send to scrollable buffer
758 if (con_initialized && cls.state != ca_dedicated)
759 Con_PrintToHistory(line, mask);
760 // send to terminal or dedicated server window
764 if(sys_specialcharactertranslation.integer)
766 for (p = (unsigned char *) line;*p; p++)
767 *p = qfont_table[*p];
770 if(sys_colortranslation.integer == 1) // ANSI
772 static char printline[MAX_INPUTLINE * 4 + 3];
773 // 2 can become 7 bytes, rounding that up to 8, and 3 bytes are added at the end
774 // a newline can transform into four bytes, but then prevents the three extra bytes from appearing
778 for(in = line, out = printline; *in; ++in)
782 case STRING_COLOR_TAG:
785 case STRING_COLOR_TAG:
787 *out++ = STRING_COLOR_TAG;
793 if(lastcolor == 0) break; else lastcolor = 0;
794 *out++ = 0x1B; *out++ = '['; *out++ = 'm';
799 if(lastcolor == 1) break; else lastcolor = 1;
800 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '1'; *out++ = 'm';
805 if(lastcolor == 2) break; else lastcolor = 2;
806 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '2'; *out++ = 'm';
811 if(lastcolor == 3) break; else lastcolor = 3;
812 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '3'; *out++ = 'm';
817 if(lastcolor == 4) break; else lastcolor = 4;
818 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '4'; *out++ = 'm';
823 if(lastcolor == 5) break; else lastcolor = 5;
824 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '6'; *out++ = 'm';
829 if(lastcolor == 6) break; else lastcolor = 6;
830 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '5'; *out++ = 'm';
837 if(lastcolor == 8) break; else lastcolor = 8;
838 *out++ = 0x1B; *out++ = '['; *out++ = '0'; *out++ = ';'; *out++ = '1'; *out++ = 'm';
841 *out++ = STRING_COLOR_TAG;
848 *out++ = 0x1B; *out++ = '['; *out++ = 'm';
865 Sys_PrintToTerminal(printline);
867 else if(sys_colortranslation.integer == 2) // Quake
869 Sys_PrintToTerminal(line);
873 static char printline[MAX_INPUTLINE]; // it can only get shorter here
876 for(in = line, out = printline; *in; ++in)
880 case STRING_COLOR_TAG:
883 case STRING_COLOR_TAG:
885 *out++ = STRING_COLOR_TAG;
900 *out++ = STRING_COLOR_TAG;
910 Sys_PrintToTerminal(printline);
913 // empty the line buffer
924 Prints to all appropriate console targets
927 void Con_Printf(const char *fmt, ...)
930 char msg[MAX_INPUTLINE];
932 va_start(argptr,fmt);
933 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
943 A Con_Print that only shows up if the "developer" cvar is set
946 void Con_DPrint(const char *msg)
948 if (!developer.integer)
949 return; // don't confuse non-developers with techie stuff...
957 A Con_Printf that only shows up if the "developer" cvar is set
960 void Con_DPrintf(const char *fmt, ...)
963 char msg[MAX_INPUTLINE];
965 if (!developer.integer)
966 return; // don't confuse non-developers with techie stuff...
968 va_start(argptr,fmt);
969 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
977 ==============================================================================
981 ==============================================================================
988 The input line scrolls horizontally if typing goes beyond the right edge
990 Modified by EvilTypeGuy eviltypeguy@qeradiant.com
993 void Con_DrawInput (void)
997 char editlinecopy[MAX_INPUTLINE+1], *text;
999 if (!key_consoleactive)
1000 return; // don't draw anything
1002 strlcpy(editlinecopy, key_lines[edit_line], sizeof(editlinecopy));
1003 text = editlinecopy;
1005 // Advanced Console Editing by Radix radix@planetquake.com
1006 // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
1007 // use strlen of edit_line instead of key_linepos to allow editing
1008 // of early characters w/o erasing
1010 y = (int)strlen(text);
1012 // fill out remainder with spaces
1013 for (i = y; i < (int)sizeof(editlinecopy)-1; i++)
1016 // add the cursor frame
1017 if ((int)(realtime*con_cursorspeed) & 1) // cursor is visible
1018 text[key_linepos] = 11 + 130 * key_insert; // either solid or triangle facing right
1020 // text[key_linepos + 1] = 0;
1022 // prestep if horizontally scrolling
1023 if (key_linepos >= con_linewidth)
1024 text += 1 + key_linepos - con_linewidth;
1027 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 );
1030 // key_lines[edit_line][key_linepos] = 0;
1038 Draws the last few lines of output transparently over the game top
1041 void Con_DrawNotify (void)
1047 char temptext[MAX_INPUTLINE];
1048 int colorindex = -1; //-1 for default
1050 if (con_notify.integer < 0)
1051 Cvar_SetValueQuick(&con_notify, 0);
1052 if (con_notify.integer > MAX_NOTIFYLINES)
1053 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
1054 if (gamemode == GAME_TRANSFUSION)
1058 // 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
1060 for (i= stop-con_notify.integer+1 ; i<=stop ; i++)
1065 time = con_times[i % con_notify.integer];
1068 time = cl.time - time;
1069 if (time > con_notifytime.value)
1071 text = con_text + (i % con_totallines)*con_linewidth;
1073 if (gamemode == GAME_NEXUIZ) {
1078 // count up to the last non-whitespace, and ignore color codes
1079 for (j = 0;j < con_linewidth && text[j];j++)
1081 if (text[j] == STRING_COLOR_TAG && (text[j+1] >= '0' && text[j+1] <= '9'))
1091 // center the line using the calculated width
1092 x = (vid_conwidth.integer - finalchars * con_textsize.value) * 0.5;
1096 DrawQ_String( x, v, text, con_linewidth, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, &colorindex, false );
1098 v += con_textsize.value;
1102 if (key_dest == key_message)
1104 int colorindex = -1;
1108 // LordHavoc: speedup, and other improvements
1110 sprintf(temptext, "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
1112 sprintf(temptext, "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
1113 while ((int)strlen(temptext) >= con_linewidth)
1115 DrawQ_String( 0, v, temptext, con_linewidth, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, &colorindex, false );
1116 strlcpy(temptext, &temptext[con_linewidth], sizeof(temptext));
1117 v += con_textsize.value;
1119 if (strlen(temptext) > 0)
1121 DrawQ_String( 0, v, temptext, 0, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, &colorindex, false );
1122 v += con_textsize.value;
1131 Draws the console with the solid background
1132 The typing input line at the bottom should only be drawn if typing is allowed
1135 void Con_DrawConsole (int lines)
1137 int i, rows, j, stop;
1140 int colorindex = -1;
1145 // draw the background
1146 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);
1147 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);
1150 con_vislines = lines;
1152 rows = (int)ceil((lines/con_textsize.value)-2); // rows of text to draw
1153 y = lines - (rows+2)*con_textsize.value; // may start slightly negative
1155 // 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
1157 for (i = stop - rows + 1;i <= stop;i++, y += con_textsize.value)
1159 j = max(i - con_backscroll, 0);
1160 text = con_text + (j % con_totallines)*con_linewidth;
1162 DrawQ_String( 0, y, text, con_linewidth, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, &colorindex, false );
1165 // draw the input prompt, user text, and cursor if desired
1173 Prints not only map filename, but also
1174 its format (q1/q2/q3/hl) and even its message
1176 //[515]: here is an ugly hack.. two gotos... oh my... *but it works*
1177 //LordHavoc: rewrote bsp type detection, added mcbsp support and rewrote message extraction to do proper worldspawn parsing
1178 //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
1179 //LordHavoc: FIXME: man this GetMapList is STILL ugly code even after my cleanups...
1180 qboolean GetMapList (const char *s, char *completedname, int completednamebufferlength)
1184 int i, k, max, p, o, min;
1187 unsigned char buf[1024];
1189 sprintf(message, "maps/%s*.bsp", s);
1190 t = FS_Search(message, 1, true);
1193 if (t->numfilenames > 1)
1194 Con_Printf("^1 %i maps found :\n", t->numfilenames);
1195 len = (unsigned char *)Z_Malloc(t->numfilenames);
1197 for(max=i=0;i<t->numfilenames;i++)
1199 k = (int)strlen(t->filenames[i]);
1209 for(i=0;i<t->numfilenames;i++)
1211 int lumpofs = 0, lumplen = 0;
1212 char *entities = NULL;
1213 const char *data = NULL;
1215 char entfilename[MAX_QPATH];
1216 strlcpy(message, "^1**ERROR**^7", sizeof(message));
1218 f = FS_Open(t->filenames[i], "rb", true, false);
1221 memset(buf, 0, 1024);
1222 FS_Read(f, buf, 1024);
1223 if (!memcmp(buf, "IBSP", 4))
1225 p = LittleLong(((int *)buf)[1]);
1226 if (p == Q3BSPVERSION)
1228 q3dheader_t *header = (q3dheader_t *)buf;
1229 lumpofs = LittleLong(header->lumps[Q3LUMP_ENTITIES].fileofs);
1230 lumplen = LittleLong(header->lumps[Q3LUMP_ENTITIES].filelen);
1232 else if (p == Q2BSPVERSION)
1234 q2dheader_t *header = (q2dheader_t *)buf;
1235 lumpofs = LittleLong(header->lumps[Q2LUMP_ENTITIES].fileofs);
1236 lumplen = LittleLong(header->lumps[Q2LUMP_ENTITIES].filelen);
1239 else if (!memcmp(buf, "MCBSPpad", 8))
1241 p = LittleLong(((int *)buf)[2]);
1242 if (p == MCBSPVERSION)
1244 int numhulls = LittleLong(((int *)buf)[3]);
1245 lumpofs = LittleLong(((int *)buf)[3 + numhulls + LUMP_ENTITIES*2+0]);
1246 lumplen = LittleLong(((int *)buf)[3 + numhulls + LUMP_ENTITIES*2+1]);
1249 else if((p = LittleLong(((int *)buf)[0])) == BSPVERSION || p == 30)
1251 dheader_t *header = (dheader_t *)buf;
1252 lumpofs = LittleLong(header->lumps[LUMP_ENTITIES].fileofs);
1253 lumplen = LittleLong(header->lumps[LUMP_ENTITIES].filelen);
1257 strlcpy(entfilename, t->filenames[i], sizeof(entfilename));
1258 memcpy(entfilename + strlen(entfilename) - 4, ".ent", 5);
1259 entities = (char *)FS_LoadFile(entfilename, tempmempool, true, NULL);
1260 if (!entities && lumplen >= 10)
1262 FS_Seek(f, lumpofs, SEEK_SET);
1263 entities = (char *)Z_Malloc(lumplen + 1);
1264 FS_Read(f, entities, lumplen);
1268 // if there are entities to parse, a missing message key just
1269 // means there is no title, so clear the message string now
1275 if (!COM_ParseToken_Simple(&data, false))
1277 if (com_token[0] == '{')
1279 if (com_token[0] == '}')
1281 // skip leading whitespace
1282 for (k = 0;com_token[k] && com_token[k] <= ' ';k++);
1283 for (l = 0;l < (int)sizeof(keyname) - 1 && com_token[k+l] && com_token[k+l] > ' ';l++)
1284 keyname[l] = com_token[k+l];
1286 if (!COM_ParseToken_Simple(&data, false))
1288 if (developer.integer >= 100)
1289 Con_Printf("key: %s %s\n", keyname, com_token);
1290 if (!strcmp(keyname, "message"))
1292 // get the message contents
1293 strlcpy(message, com_token, sizeof(message));
1303 *(t->filenames[i]+len[i]+5) = 0;
1306 case Q3BSPVERSION: strlcpy((char *)buf, "Q3", sizeof(buf));break;
1307 case Q2BSPVERSION: strlcpy((char *)buf, "Q2", sizeof(buf));break;
1308 case BSPVERSION: strlcpy((char *)buf, "Q1", sizeof(buf));break;
1309 case MCBSPVERSION: strlcpy((char *)buf, "MC", sizeof(buf));break;
1310 case 30: strlcpy((char *)buf, "HL", sizeof(buf));break;
1311 default: strlcpy((char *)buf, "??", sizeof(buf));break;
1313 Con_Printf("%16s (%s) %s\n", t->filenames[i]+5, buf, message);
1318 k = *(t->filenames[0]+5+p);
1321 for(i=1;i<t->numfilenames;i++)
1322 if(*(t->filenames[i]+5+p) != k)
1326 if(p > o && completedname && completednamebufferlength > 0)
1328 memset(completedname, 0, completednamebufferlength);
1329 memcpy(completedname, (t->filenames[0]+5), min(p, completednamebufferlength - 1));
1339 New function for tab-completion system
1340 Added by EvilTypeGuy
1341 MEGA Thanks to Taniwha
1344 void Con_DisplayList(const char **list)
1346 int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
1347 const char **walk = list;
1350 len = (int)strlen(*walk);
1358 len = (int)strlen(*list);
1359 if (pos + maxlen >= width) {
1365 for (i = 0; i < (maxlen - len); i++)
1377 Con_CompleteCommandLine
1379 New function for tab-completion system
1380 Added by EvilTypeGuy
1381 Thanks to Fett erich@heintz.com
1383 Enhanced to tab-complete map names by [515]
1386 void Con_CompleteCommandLine (void)
1388 const char *cmd = "";
1390 const char **list[3] = {0, 0, 0};
1392 int c, v, a, i, cmd_len, pos, k;
1394 //find what we want to complete
1398 k = key_lines[edit_line][pos];
1399 if(k == '\"' || k == ';' || k == ' ' || k == '\'')
1404 s = key_lines[edit_line] + pos;
1405 strlcpy(s2, key_lines[edit_line] + key_linepos, sizeof(s2)); //save chars after cursor
1406 key_lines[edit_line][key_linepos] = 0; //hide them
1409 for(k=pos-1;k>2;k--)
1410 if(key_lines[edit_line][k] != ' ')
1412 if(key_lines[edit_line][k] == '\"' || key_lines[edit_line][k] == ';' || key_lines[edit_line][k] == '\'')
1414 if ((pos+k > 2 && !strncmp(key_lines[edit_line]+k-2, "map", 3))
1415 || (pos+k > 10 && !strncmp(key_lines[edit_line]+k-10, "changelevel", 11)))
1418 if (GetMapList(s, t, sizeof(t)))
1420 // first move the cursor
1421 key_linepos += (int)strlen(t) - (int)strlen(s);
1423 // and now do the actual work
1425 strlcat(key_lines[edit_line], t, MAX_INPUTLINE);
1426 strlcat(key_lines[edit_line], s2, MAX_INPUTLINE); //add back chars after cursor
1428 // and fix the cursor
1429 if(key_linepos > (int) strlen(key_lines[edit_line]))
1430 key_linepos = (int) strlen(key_lines[edit_line]);
1436 // Count number of possible matches and print them
1437 c = Cmd_CompleteCountPossible(s);
1440 Con_Printf("\n%i possible command%s\n", c, (c > 1) ? "s: " : ":");
1441 Cmd_CompleteCommandPrint(s);
1443 v = Cvar_CompleteCountPossible(s);
1446 Con_Printf("\n%i possible variable%s\n", v, (v > 1) ? "s: " : ":");
1447 Cvar_CompleteCvarPrint(s);
1449 a = Cmd_CompleteAliasCountPossible(s);
1452 Con_Printf("\n%i possible aliases%s\n", a, (a > 1) ? "s: " : ":");
1453 Cmd_CompleteAliasPrint(s);
1456 if (!(c + v + a)) // No possible matches
1459 strlcpy(&key_lines[edit_line][key_linepos], s2, sizeof(key_lines[edit_line]) - key_linepos);
1464 cmd = *(list[0] = Cmd_CompleteBuildList(s));
1466 cmd = *(list[1] = Cvar_CompleteBuildList(s));
1468 cmd = *(list[2] = Cmd_CompleteAliasBuildList(s));
1470 for (cmd_len = (int)strlen(s);;cmd_len++)
1473 for (i = 0; i < 3; i++)
1475 for (l = list[i];*l;l++)
1476 if ((*l)[cmd_len] != cmd[cmd_len])
1478 // all possible matches share this character, so we continue...
1481 // if all matches ended at the same position, stop
1482 // (this means there is only one match)
1488 // prevent a buffer overrun by limiting cmd_len according to remaining space
1489 cmd_len = min(cmd_len, (int)sizeof(key_lines[edit_line]) - 1 - pos);
1493 memcpy(&key_lines[edit_line][key_linepos], cmd, cmd_len);
1494 key_linepos += cmd_len;
1495 // if there is only one match, add a space after it
1496 if (c + v + a == 1 && key_linepos < (int)sizeof(key_lines[edit_line]) - 1)
1497 key_lines[edit_line][key_linepos++] = ' ';
1500 // use strlcat to avoid a buffer overrun
1501 key_lines[edit_line][key_linepos] = 0;
1502 strlcat(key_lines[edit_line], s2, sizeof(key_lines[edit_line]));
1504 // free the command, cvar, and alias lists
1505 for (i = 0; i < 3; i++)
1507 Mem_Free((void *)list[i]);