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
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",
53 cvar_t sys_colortranslation = {0, "sys_colortranslation", "1",
55 "terminal console color translation (supported values: 0 = strip color codes, 1 = translate to ANSI codes, 2 = no translation)"};
57 #define MAX_NOTIFYLINES 32
58 // cl.time time the line was generated for transparent notify lines
59 float con_times[MAX_NOTIFYLINES];
63 qboolean con_initialized;
65 // used for server replies to rcon command
66 qboolean rcon_redirect = false;
67 int rcon_redirect_bufferpos = 0;
68 char rcon_redirect_buffer[1400];
72 ==============================================================================
76 ==============================================================================
79 cvar_t log_file = {0, "log_file","", "filename to log messages to"};
80 char crt_log_file [MAX_OSPATH] = "";
81 qfile_t* logfile = NULL;
83 unsigned char* logqueue = NULL;
87 void Log_ConPrint (const char *msg);
94 const char* Log_Timestamp (const char *desc)
96 static char timestamp [128];
98 const struct tm *crt_tm;
101 // Build the time stamp (ex: "Wed Jun 30 21:49:08 1993");
103 crt_tm = localtime (&crt_time);
104 strftime (timestring, sizeof (timestring), "%a %b %d %H:%M:%S %Y", crt_tm);
107 dpsnprintf (timestamp, sizeof (timestamp), "====== %s (%s) ======\n", desc, timestring);
109 dpsnprintf (timestamp, sizeof (timestamp), "====== %s ======\n", timestring);
122 if (logfile != NULL || log_file.string[0] == '\0')
125 logfile = FS_Open (log_file.string, "ab", false, false);
128 strlcpy (crt_log_file, log_file.string, sizeof (crt_log_file));
129 FS_Print (logfile, Log_Timestamp ("Log started"));
139 void Log_Close (void)
144 FS_Print (logfile, Log_Timestamp ("Log stopped"));
145 FS_Print (logfile, "\n");
149 crt_log_file[0] = '\0';
158 void Log_Start (void)
162 // Dump the contents of the log queue into the log file and free it
163 if (logqueue != NULL)
165 unsigned char *temp = logqueue;
167 if (logfile != NULL && logq_ind != 0)
168 FS_Write (logfile, temp, logq_ind);
181 void Log_ConPrint (const char *msg)
183 static qboolean inprogress = false;
185 // don't allow feedback loops with memory error reports
190 // Until the host is completely initialized, we maintain a log queue
191 // to store the messages, since the log can't be started before
192 if (logqueue != NULL)
194 size_t remain = logq_size - logq_ind;
195 size_t len = strlen (msg);
197 // If we need to enlarge the log queue
200 size_t factor = ((logq_ind + len) / logq_size) + 1;
201 unsigned char* newqueue;
204 newqueue = (unsigned char *)Mem_Alloc (tempmempool, logq_size);
205 memcpy (newqueue, logqueue, logq_ind);
208 remain = logq_size - logq_ind;
210 memcpy (&logqueue[logq_ind], msg, len);
217 // Check if log_file has changed
218 if (strcmp (crt_log_file, log_file.string) != 0)
224 // If a log file is available
226 FS_Print (logfile, msg);
236 void Log_Printf (const char *logfilename, const char *fmt, ...)
240 file = FS_Open (logfilename, "ab", true, false);
245 va_start (argptr, fmt);
246 FS_VPrintf (file, fmt, argptr);
255 ==============================================================================
259 ==============================================================================
267 void Con_ToggleConsole_f (void)
269 // toggle the 'user wants console' bit
270 key_consoleactive ^= KEY_CONSOLEACTIVE_USER;
271 memset (con_times, 0, sizeof(con_times));
279 void Con_Clear_f (void)
282 memset (con_text, ' ', CON_TEXTSIZE);
291 void Con_ClearNotify (void)
295 for (i=0 ; i<MAX_NOTIFYLINES ; i++)
305 void Con_MessageMode_f (void)
307 key_dest = key_message;
317 void Con_MessageMode2_f (void)
319 key_dest = key_message;
328 If the line width has changed, reformat the buffer.
331 void Con_CheckResize (void)
333 int i, j, width, oldwidth, oldtotallines, numlines, numchars;
335 char tbuf[CON_TEXTSIZE];
337 f = bound(1, con_textsize.value, 128);
338 if(f != con_textsize.value)
339 Cvar_SetValueQuick(&con_textsize, f);
340 width = (int)floor(vid_conwidth.value / con_textsize.value);
341 width = bound(1, width, CON_TEXTSIZE/4);
343 if (width == con_linewidth)
346 oldwidth = con_linewidth;
347 con_linewidth = width;
348 oldtotallines = con_totallines;
349 con_totallines = CON_TEXTSIZE / con_linewidth;
350 numlines = oldtotallines;
352 if (con_totallines < numlines)
353 numlines = con_totallines;
357 if (con_linewidth < numchars)
358 numchars = con_linewidth;
360 memcpy (tbuf, con_text, CON_TEXTSIZE);
361 memset (con_text, ' ', CON_TEXTSIZE);
363 for (i=0 ; i<numlines ; i++)
365 for (j=0 ; j<numchars ; j++)
367 con_text[(con_totallines - 1 - i) * con_linewidth + j] =
368 tbuf[((con_current - i + oldtotallines) %
369 oldtotallines) * oldwidth + j];
376 con_current = con_totallines - 1;
379 //[515]: the simplest command ever
380 //LordHavoc: not so simple after I made it print usage...
381 static void Con_Maps_f (void)
385 Con_Printf("usage: maps [mapnameprefix]\n");
388 else if (Cmd_Argc() == 2)
389 GetMapList(Cmd_Argv(1), NULL, 0);
391 GetMapList("", NULL, 0);
401 memset (con_text, ' ', CON_TEXTSIZE);
403 con_totallines = CON_TEXTSIZE / con_linewidth;
405 // Allocate a log queue, this will be freed after configs are parsed
406 logq_size = MAX_INPUTLINE;
407 logqueue = (unsigned char *)Mem_Alloc (tempmempool, logq_size);
410 Cvar_RegisterVariable (&sys_colortranslation);
411 Cvar_RegisterVariable (&sys_specialcharactertranslation);
413 Cvar_RegisterVariable (&log_file);
415 // support for the classic Quake option
416 // COMMANDLINEOPTION: Console: -condebug logs console messages to qconsole.log, see also log_file
417 if (COM_CheckParm ("-condebug") != 0)
418 Cvar_SetQuick (&log_file, "qconsole.log");
420 // register our cvars
421 Cvar_RegisterVariable (&con_notifytime);
422 Cvar_RegisterVariable (&con_notify);
423 Cvar_RegisterVariable (&con_textsize);
425 // register our commands
426 Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f, "opens or closes the console");
427 Cmd_AddCommand ("messagemode", Con_MessageMode_f, "input a chat message to say to everyone");
428 Cmd_AddCommand ("messagemode2", Con_MessageMode2_f, "input a chat message to say to only your team");
429 Cmd_AddCommand ("clear", Con_Clear_f, "clear console history");
430 Cmd_AddCommand ("maps", Con_Maps_f, "list information about available maps"); // By [515]
432 con_initialized = true;
433 Con_Print("Console initialized.\n");
442 void Con_Linefeed (void)
449 memset (&con_text[(con_current%con_totallines)*con_linewidth], ' ', con_linewidth);
456 Handles cursor positioning, line wrapping, etc
457 All console printing must go through this in order to be displayed
458 If no console is visible, the notify window will pop up.
461 void Con_PrintToHistory(const char *txt, int mask)
469 for (l=0 ; l< con_linewidth ; l++)
474 if (l != con_linewidth && (con_x + l > con_linewidth) )
489 // mark time for transparent overlay
490 if (con_current >= 0)
492 if (con_notify.integer < 0)
493 Cvar_SetValueQuick(&con_notify, 0);
494 if (con_notify.integer > MAX_NOTIFYLINES)
495 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
496 if (con_notify.integer > 0)
497 con_times[con_current % con_notify.integer] = cl.time;
512 default: // display character and advance
513 y = con_current % con_totallines;
514 con_text[y*con_linewidth+con_x] = c | mask;
516 if (con_x >= con_linewidth)
524 /* The translation table between the graphical font and plain ASCII --KB */
525 static char qfont_table[256] = {
526 '\0', '#', '#', '#', '#', '.', '#', '#',
527 '#', 9, 10, '#', ' ', 13, '.', '.',
528 '[', ']', '0', '1', '2', '3', '4', '5',
529 '6', '7', '8', '9', '.', '<', '=', '>',
530 ' ', '!', '"', '#', '$', '%', '&', '\'',
531 '(', ')', '*', '+', ',', '-', '.', '/',
532 '0', '1', '2', '3', '4', '5', '6', '7',
533 '8', '9', ':', ';', '<', '=', '>', '?',
534 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
535 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
536 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
537 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
538 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
539 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
540 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
541 'x', 'y', 'z', '{', '|', '}', '~', '<',
543 '<', '=', '>', '#', '#', '.', '#', '#',
544 '#', '#', ' ', '#', ' ', '>', '.', '.',
545 '[', ']', '0', '1', '2', '3', '4', '5',
546 '6', '7', '8', '9', '.', '<', '=', '>',
547 ' ', '!', '"', '#', '$', '%', '&', '\'',
548 '(', ')', '*', '+', ',', '-', '.', '/',
549 '0', '1', '2', '3', '4', '5', '6', '7',
550 '8', '9', ':', ';', '<', '=', '>', '?',
551 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
552 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
553 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
554 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
555 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
556 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
557 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
558 'x', 'y', 'z', '{', '|', '}', '~', '<'
565 Prints to all appropriate console targets, and adds timestamps
568 extern cvar_t timestamps;
569 extern cvar_t timeformat;
570 extern qboolean sys_nostdout;
571 void Con_Print(const char *msg)
574 static int index = 0;
575 static char line[MAX_INPUTLINE];
579 // if this print is in response to an rcon command, add the character
580 // to the rcon redirect buffer
581 if (rcon_redirect && rcon_redirect_bufferpos < (int)sizeof(rcon_redirect_buffer) - 1)
582 rcon_redirect_buffer[rcon_redirect_bufferpos++] = *msg;
583 // if this is the beginning of a new line, print timestamp
586 const char *timestamp = timestamps.integer ? Sys_TimeString(timeformat.string) : "";
588 // FIXME: 1. perhaps we should use a terminal system 2. use a constant instead of 7!
589 line[index++] = STRING_COLOR_TAG;
590 // assert( STRING_COLOR_DEFAULT < 10 )
591 line[index++] = STRING_COLOR_DEFAULT + '0';
592 // special color codes for chat messages must always come first
593 // for Con_PrintToHistory to work properly
594 if (*msg == 1 || *msg == 2)
598 S_LocalSound ("sound/misc/talk.wav");
599 line[index++] = STRING_COLOR_TAG;
604 for (;*timestamp;index++, timestamp++)
605 if (index < (int)sizeof(line) - 2)
606 line[index] = *timestamp;
608 // append the character
609 line[index++] = *msg;
610 // if this is a newline character, we have a complete line to print
611 if (*msg == '\n' || index >= (int)sizeof(line) / 2)
613 // terminate the line
617 // send to scrollable buffer
618 if (con_initialized && cls.state != ca_dedicated)
619 Con_PrintToHistory(line, mask);
620 // send to terminal or dedicated server window
624 if(sys_specialcharactertranslation.integer)
626 for (p = (unsigned char *) line;*p; p++)
627 *p = qfont_table[*p];
630 if(sys_colortranslation.integer == 1) // ANSI
632 static char printline[MAX_INPUTLINE * 4 + 3];
633 // 2 can become 7 bytes, rounding that up to 8, and 3 bytes are added at the end
634 // a newline can transform into four bytes, but then prevents the three extra bytes from appearing
638 for(in = line, out = printline; *in; ++in)
642 case STRING_COLOR_TAG:
645 case STRING_COLOR_TAG:
647 *out++ = STRING_COLOR_TAG;
653 if(lastcolor == 0) break; else lastcolor = 0;
654 *out++ = 0x1B; *out++ = '['; *out++ = 'm';
659 if(lastcolor == 1) break; else lastcolor = 1;
660 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '1'; *out++ = 'm';
665 if(lastcolor == 2) break; else lastcolor = 2;
666 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '2'; *out++ = 'm';
671 if(lastcolor == 3) break; else lastcolor = 3;
672 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '3'; *out++ = 'm';
677 if(lastcolor == 4) break; else lastcolor = 4;
678 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '4'; *out++ = 'm';
683 if(lastcolor == 5) break; else lastcolor = 5;
684 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '6'; *out++ = 'm';
689 if(lastcolor == 6) break; else lastcolor = 6;
690 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '5'; *out++ = 'm';
697 if(lastcolor == 8) break; else lastcolor = 8;
698 *out++ = 0x1B; *out++ = '['; *out++ = '0'; *out++ = ';'; *out++ = '1'; *out++ = 'm';
701 *out++ = STRING_COLOR_TAG;
708 *out++ = 0x1B; *out++ = '['; *out++ = 'm';
725 Sys_PrintToTerminal(printline);
727 else if(sys_colortranslation.integer == 2) // Quake
729 Sys_PrintToTerminal(line);
733 static char printline[MAX_INPUTLINE]; // it can only get shorter here
736 for(in = line, out = printline; *in; ++in)
740 case STRING_COLOR_TAG:
743 case STRING_COLOR_TAG:
745 *out++ = STRING_COLOR_TAG;
760 *out++ = STRING_COLOR_TAG;
770 Sys_PrintToTerminal(printline);
773 // empty the line buffer
784 Prints to all appropriate console targets
787 void Con_Printf(const char *fmt, ...)
790 char msg[MAX_INPUTLINE];
792 va_start(argptr,fmt);
793 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
803 A Con_Print that only shows up if the "developer" cvar is set
806 void Con_DPrint(const char *msg)
808 if (!developer.integer)
809 return; // don't confuse non-developers with techie stuff...
817 A Con_Printf that only shows up if the "developer" cvar is set
820 void Con_DPrintf(const char *fmt, ...)
823 char msg[MAX_INPUTLINE];
825 if (!developer.integer)
826 return; // don't confuse non-developers with techie stuff...
828 va_start(argptr,fmt);
829 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
837 ==============================================================================
841 ==============================================================================
848 The input line scrolls horizontally if typing goes beyond the right edge
850 Modified by EvilTypeGuy eviltypeguy@qeradiant.com
853 void Con_DrawInput (void)
857 char editlinecopy[MAX_INPUTLINE+1], *text;
859 if (!key_consoleactive)
860 return; // don't draw anything
862 strlcpy(editlinecopy, key_lines[edit_line], sizeof(editlinecopy));
865 // Advanced Console Editing by Radix radix@planetquake.com
866 // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
867 // use strlen of edit_line instead of key_linepos to allow editing
868 // of early characters w/o erasing
870 y = (int)strlen(text);
872 // fill out remainder with spaces
873 for (i = y; i < (int)sizeof(editlinecopy)-1; i++)
876 // add the cursor frame
877 if ((int)(realtime*con_cursorspeed) & 1) // cursor is visible
878 text[key_linepos] = 11 + 130 * key_insert; // either solid or triangle facing right
880 // text[key_linepos + 1] = 0;
882 // prestep if horizontally scrolling
883 if (key_linepos >= con_linewidth)
884 text += 1 + key_linepos - con_linewidth;
887 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 );
890 // key_lines[edit_line][key_linepos] = 0;
898 Draws the last few lines of output transparently over the game top
901 void Con_DrawNotify (void)
907 char temptext[MAX_INPUTLINE];
908 int colorindex = -1; //-1 for default
910 if (con_notify.integer < 0)
911 Cvar_SetValueQuick(&con_notify, 0);
912 if (con_notify.integer > MAX_NOTIFYLINES)
913 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
914 if (gamemode == GAME_TRANSFUSION)
918 // 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
920 for (i= stop-con_notify.integer+1 ; i<=stop ; i++)
925 time = con_times[i % con_notify.integer];
928 time = cl.time - time;
929 if (time > con_notifytime.value)
931 text = con_text + (i % con_totallines)*con_linewidth;
933 if (gamemode == GAME_NEXUIZ) {
938 // count up to the last non-whitespace, and ignore color codes
939 for (j = 0;j < con_linewidth && text[j];j++)
941 if (text[j] == STRING_COLOR_TAG && (text[j+1] >= '0' && text[j+1] <= '9'))
951 // center the line using the calculated width
952 x = (vid_conwidth.integer - finalchars * con_textsize.value) * 0.5;
956 DrawQ_ColoredString( x, v, text, con_linewidth, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
958 v += con_textsize.value;
962 if (key_dest == key_message)
968 // LordHavoc: speedup, and other improvements
970 sprintf(temptext, "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
972 sprintf(temptext, "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
973 while ((int)strlen(temptext) >= con_linewidth)
975 DrawQ_ColoredString( 0, v, temptext, con_linewidth, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
976 strlcpy(temptext, &temptext[con_linewidth], sizeof(temptext));
977 v += con_textsize.value;
979 if (strlen(temptext) > 0)
981 DrawQ_ColoredString( 0, v, temptext, 0, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
982 v += con_textsize.value;
991 Draws the console with the solid background
992 The typing input line at the bottom should only be drawn if typing is allowed
995 void Con_DrawConsole (int lines)
997 int i, rows, j, stop;
1000 int colorindex = -1;
1005 // draw the background
1006 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);
1007 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);
1010 con_vislines = lines;
1012 rows = (int)ceil((lines/con_textsize.value)-2); // rows of text to draw
1013 y = lines - (rows+2)*con_textsize.value; // may start slightly negative
1015 // 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
1017 for (i = stop - rows + 1;i <= stop;i++, y += con_textsize.value)
1019 j = max(i - con_backscroll, 0);
1020 text = con_text + (j % con_totallines)*con_linewidth;
1022 DrawQ_ColoredString( 0, y, text, con_linewidth, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
1025 // draw the input prompt, user text, and cursor if desired
1033 Prints not only map filename, but also
1034 its format (q1/q2/q3/hl) and even its message
1036 //[515]: here is an ugly hack.. two gotos... oh my... *but it works*
1037 //LordHavoc: rewrote bsp type detection, added mcbsp support and rewrote message extraction to do proper worldspawn parsing
1038 //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
1039 //LordHavoc: FIXME: man this GetMapList is STILL ugly code even after my cleanups...
1040 qboolean GetMapList (const char *s, char *completedname, int completednamebufferlength)
1044 int i, k, max, p, o, min;
1047 unsigned char buf[1024];
1049 sprintf(message, "maps/%s*.bsp", s);
1050 t = FS_Search(message, 1, true);
1053 if (t->numfilenames > 1)
1054 Con_Printf("^1 %i maps found :\n", t->numfilenames);
1055 len = (unsigned char *)Z_Malloc(t->numfilenames);
1057 for(max=i=0;i<t->numfilenames;i++)
1059 k = (int)strlen(t->filenames[i]);
1069 for(i=0;i<t->numfilenames;i++)
1071 int lumpofs = 0, lumplen = 0;
1072 char *entities = NULL;
1073 const char *data = NULL;
1075 char entfilename[MAX_QPATH];
1076 strlcpy(message, "^1**ERROR**^7", sizeof(message));
1078 f = FS_Open(t->filenames[i], "rb", true, false);
1081 memset(buf, 0, 1024);
1082 FS_Read(f, buf, 1024);
1083 if (!memcmp(buf, "IBSP", 4))
1085 p = LittleLong(((int *)buf)[1]);
1086 if (p == Q3BSPVERSION)
1088 q3dheader_t *header = (q3dheader_t *)buf;
1089 lumpofs = LittleLong(header->lumps[Q3LUMP_ENTITIES].fileofs);
1090 lumplen = LittleLong(header->lumps[Q3LUMP_ENTITIES].filelen);
1092 else if (p == Q2BSPVERSION)
1094 q2dheader_t *header = (q2dheader_t *)buf;
1095 lumpofs = LittleLong(header->lumps[Q2LUMP_ENTITIES].fileofs);
1096 lumplen = LittleLong(header->lumps[Q2LUMP_ENTITIES].filelen);
1099 else if (!memcmp(buf, "MCBSPpad", 8))
1101 p = LittleLong(((int *)buf)[2]);
1102 if (p == MCBSPVERSION)
1104 int numhulls = LittleLong(((int *)buf)[3]);
1105 lumpofs = LittleLong(((int *)buf)[3 + numhulls + LUMP_ENTITIES*2+0]);
1106 lumplen = LittleLong(((int *)buf)[3 + numhulls + LUMP_ENTITIES*2+1]);
1109 else if((p = LittleLong(((int *)buf)[0])) == BSPVERSION || p == 30)
1111 dheader_t *header = (dheader_t *)buf;
1112 lumpofs = LittleLong(header->lumps[LUMP_ENTITIES].fileofs);
1113 lumplen = LittleLong(header->lumps[LUMP_ENTITIES].filelen);
1117 strlcpy(entfilename, t->filenames[i], sizeof(entfilename));
1118 memcpy(entfilename + strlen(entfilename) - 4, ".ent", 5);
1119 entities = (char *)FS_LoadFile(entfilename, tempmempool, true, NULL);
1120 if (!entities && lumplen >= 10)
1122 FS_Seek(f, lumpofs, SEEK_SET);
1123 entities = (char *)Z_Malloc(lumplen + 1);
1124 FS_Read(f, entities, lumplen);
1128 // if there are entities to parse, a missing message key just
1129 // means there is no title, so clear the message string now
1135 if (!COM_ParseTokenConsole(&data))
1137 if (com_token[0] == '{')
1139 if (com_token[0] == '}')
1141 // skip leading whitespace
1142 for (k = 0;com_token[k] && com_token[k] <= ' ';k++);
1143 for (l = 0;l < (int)sizeof(keyname) - 1 && com_token[k+l] && com_token[k+l] > ' ';l++)
1144 keyname[l] = com_token[k+l];
1146 if (!COM_ParseTokenConsole(&data))
1148 if (developer.integer >= 100)
1149 Con_Printf("key: %s %s\n", keyname, com_token);
1150 if (!strcmp(keyname, "message"))
1152 // get the message contents
1153 strlcpy(message, com_token, sizeof(message));
1163 *(t->filenames[i]+len[i]+5) = 0;
1166 case Q3BSPVERSION: strlcpy((char *)buf, "Q3", sizeof(buf));break;
1167 case Q2BSPVERSION: strlcpy((char *)buf, "Q2", sizeof(buf));break;
1168 case BSPVERSION: strlcpy((char *)buf, "Q1", sizeof(buf));break;
1169 case MCBSPVERSION: strlcpy((char *)buf, "MC", sizeof(buf));break;
1170 case 30: strlcpy((char *)buf, "HL", sizeof(buf));break;
1171 default: strlcpy((char *)buf, "??", sizeof(buf));break;
1173 Con_Printf("%16s (%s) %s\n", t->filenames[i]+5, buf, message);
1178 k = *(t->filenames[0]+5+p);
1181 for(i=1;i<t->numfilenames;i++)
1182 if(*(t->filenames[i]+5+p) != k)
1188 memset(completedname, 0, completednamebufferlength);
1189 memcpy(completedname, (t->filenames[0]+5), min(p, completednamebufferlength - 1));
1199 New function for tab-completion system
1200 Added by EvilTypeGuy
1201 MEGA Thanks to Taniwha
1204 void Con_DisplayList(const char **list)
1206 int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
1207 const char **walk = list;
1210 len = (int)strlen(*walk);
1218 len = (int)strlen(*list);
1219 if (pos + maxlen >= width) {
1225 for (i = 0; i < (maxlen - len); i++)
1237 Con_CompleteCommandLine
1239 New function for tab-completion system
1240 Added by EvilTypeGuy
1241 Thanks to Fett erich@heintz.com
1243 Enhanced to tab-complete map names by [515]
1246 void Con_CompleteCommandLine (void)
1248 const char *cmd = "";
1250 const char **list[3] = {0, 0, 0};
1252 int c, v, a, i, cmd_len, pos, k;
1254 //find what we want to complete
1258 k = key_lines[edit_line][pos];
1259 if(k == '\"' || k == ';' || k == ' ' || k == '\'')
1264 s = key_lines[edit_line] + pos;
1265 strlcpy(s2, key_lines[edit_line] + key_linepos, sizeof(s2)); //save chars after cursor
1266 key_lines[edit_line][key_linepos] = 0; //hide them
1269 for(k=pos-1;k>2;k--)
1270 if(key_lines[edit_line][k] != ' ')
1272 if(key_lines[edit_line][k] == '\"' || key_lines[edit_line][k] == ';' || key_lines[edit_line][k] == '\'')
1274 if ((pos+k > 2 && !strncmp(key_lines[edit_line]+k-2, "map", 3))
1275 || (pos+k > 10 && !strncmp(key_lines[edit_line]+k-10, "changelevel", 11)))
1278 if (GetMapList(s, t, sizeof(t)))
1280 // first move the cursor
1281 key_linepos += (int)strlen(t) - (int)strlen(s);
1283 // and now do the actual work
1285 strlcat(key_lines[edit_line], t, MAX_INPUTLINE);
1286 strlcat(key_lines[edit_line], s2, MAX_INPUTLINE); //add back chars after cursor
1288 // and fix the cursor
1289 if(key_linepos > (int) strlen(key_lines[edit_line]))
1290 key_linepos = (int) strlen(key_lines[edit_line]);
1296 // Count number of possible matches and print them
1297 c = Cmd_CompleteCountPossible(s);
1300 Con_Printf("\n%i possible command%s\n", c, (c > 1) ? "s: " : ":");
1301 Cmd_CompleteCommandPrint(s);
1303 v = Cvar_CompleteCountPossible(s);
1306 Con_Printf("\n%i possible variable%s\n", v, (v > 1) ? "s: " : ":");
1307 Cvar_CompleteCvarPrint(s);
1309 a = Cmd_CompleteAliasCountPossible(s);
1312 Con_Printf("\n%i possible aliases%s\n", a, (a > 1) ? "s: " : ":");
1313 Cmd_CompleteAliasPrint(s);
1316 if (!(c + v + a)) // No possible matches
1319 strlcpy(&key_lines[edit_line][key_linepos], s2, sizeof(key_lines[edit_line]) - key_linepos);
1324 cmd = *(list[0] = Cmd_CompleteBuildList(s));
1326 cmd = *(list[1] = Cvar_CompleteBuildList(s));
1328 cmd = *(list[2] = Cmd_CompleteAliasBuildList(s));
1330 for (cmd_len = (int)strlen(s);;cmd_len++)
1333 for (i = 0; i < 3; i++)
1335 for (l = list[i];*l;l++)
1336 if ((*l)[cmd_len] != cmd[cmd_len])
1338 // all possible matches share this character, so we continue...
1341 // if all matches ended at the same position, stop
1342 // (this means there is only one match)
1348 // prevent a buffer overrun by limiting cmd_len according to remaining space
1349 cmd_len = min(cmd_len, (int)sizeof(key_lines[edit_line]) - 1 - pos);
1353 memcpy(&key_lines[edit_line][key_linepos], cmd, cmd_len);
1354 key_linepos += cmd_len;
1355 // if there is only one match, add a space after it
1356 if (c + v + a == 1 && key_linepos < (int)sizeof(key_lines[edit_line]) - 1)
1357 key_lines[edit_line][key_linepos++] = ' ';
1360 // use strlcat to avoid a buffer overrun
1361 key_lines[edit_line][key_linepos] = 0;
1362 strlcat(key_lines[edit_line], s2, sizeof(key_lines[edit_line]));
1364 // free the command, cvar, and alias lists
1365 for (i = 0; i < 3; i++)
1367 Mem_Free((void *)list[i]);