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)
640 if (msg[1] == '(' && cl.foundtalk2wav)
641 S_LocalSound ("sound/misc/talk2.wav");
643 S_LocalSound ("sound/misc/talk.wav");
645 line[index++] = STRING_COLOR_TAG;
650 for (;*timestamp;index++, timestamp++)
651 if (index < (int)sizeof(line) - 2)
652 line[index] = *timestamp;
654 // append the character
655 line[index++] = *msg;
656 // if this is a newline character, we have a complete line to print
657 if (*msg == '\n' || index >= (int)sizeof(line) / 2)
659 // terminate the line
663 // send to scrollable buffer
664 if (con_initialized && cls.state != ca_dedicated)
665 Con_PrintToHistory(line, mask);
666 // send to terminal or dedicated server window
670 if(sys_specialcharactertranslation.integer)
672 for (p = (unsigned char *) line;*p; p++)
673 *p = qfont_table[*p];
676 if(sys_colortranslation.integer == 1) // ANSI
678 static char printline[MAX_INPUTLINE * 4 + 3];
679 // 2 can become 7 bytes, rounding that up to 8, and 3 bytes are added at the end
680 // a newline can transform into four bytes, but then prevents the three extra bytes from appearing
684 for(in = line, out = printline; *in; ++in)
688 case STRING_COLOR_TAG:
691 case STRING_COLOR_TAG:
693 *out++ = STRING_COLOR_TAG;
699 if(lastcolor == 0) break; else lastcolor = 0;
700 *out++ = 0x1B; *out++ = '['; *out++ = 'm';
705 if(lastcolor == 1) break; else lastcolor = 1;
706 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '1'; *out++ = 'm';
711 if(lastcolor == 2) break; else lastcolor = 2;
712 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '2'; *out++ = 'm';
717 if(lastcolor == 3) break; else lastcolor = 3;
718 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '3'; *out++ = 'm';
723 if(lastcolor == 4) break; else lastcolor = 4;
724 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '4'; *out++ = 'm';
729 if(lastcolor == 5) break; else lastcolor = 5;
730 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '6'; *out++ = 'm';
735 if(lastcolor == 6) break; else lastcolor = 6;
736 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '5'; *out++ = 'm';
743 if(lastcolor == 8) break; else lastcolor = 8;
744 *out++ = 0x1B; *out++ = '['; *out++ = '0'; *out++ = ';'; *out++ = '1'; *out++ = 'm';
747 *out++ = STRING_COLOR_TAG;
754 *out++ = 0x1B; *out++ = '['; *out++ = 'm';
771 Sys_PrintToTerminal(printline);
773 else if(sys_colortranslation.integer == 2) // Quake
775 Sys_PrintToTerminal(line);
779 static char printline[MAX_INPUTLINE]; // it can only get shorter here
782 for(in = line, out = printline; *in; ++in)
786 case STRING_COLOR_TAG:
789 case STRING_COLOR_TAG:
791 *out++ = STRING_COLOR_TAG;
806 *out++ = STRING_COLOR_TAG;
816 Sys_PrintToTerminal(printline);
819 // empty the line buffer
830 Prints to all appropriate console targets
833 void Con_Printf(const char *fmt, ...)
836 char msg[MAX_INPUTLINE];
838 va_start(argptr,fmt);
839 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
849 A Con_Print that only shows up if the "developer" cvar is set
852 void Con_DPrint(const char *msg)
854 if (!developer.integer)
855 return; // don't confuse non-developers with techie stuff...
863 A Con_Printf that only shows up if the "developer" cvar is set
866 void Con_DPrintf(const char *fmt, ...)
869 char msg[MAX_INPUTLINE];
871 if (!developer.integer)
872 return; // don't confuse non-developers with techie stuff...
874 va_start(argptr,fmt);
875 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
883 ==============================================================================
887 ==============================================================================
894 The input line scrolls horizontally if typing goes beyond the right edge
896 Modified by EvilTypeGuy eviltypeguy@qeradiant.com
899 void Con_DrawInput (void)
903 char editlinecopy[MAX_INPUTLINE+1], *text;
905 if (!key_consoleactive)
906 return; // don't draw anything
908 strlcpy(editlinecopy, key_lines[edit_line], sizeof(editlinecopy));
911 // Advanced Console Editing by Radix radix@planetquake.com
912 // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
913 // use strlen of edit_line instead of key_linepos to allow editing
914 // of early characters w/o erasing
916 y = (int)strlen(text);
918 // fill out remainder with spaces
919 for (i = y; i < (int)sizeof(editlinecopy)-1; i++)
922 // add the cursor frame
923 if ((int)(realtime*con_cursorspeed) & 1) // cursor is visible
924 text[key_linepos] = 11 + 130 * key_insert; // either solid or triangle facing right
926 // text[key_linepos + 1] = 0;
928 // prestep if horizontally scrolling
929 if (key_linepos >= con_linewidth)
930 text += 1 + key_linepos - con_linewidth;
933 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 );
936 // key_lines[edit_line][key_linepos] = 0;
944 Draws the last few lines of output transparently over the game top
947 void Con_DrawNotify (void)
953 char temptext[MAX_INPUTLINE];
954 int colorindex = -1; //-1 for default
956 if (con_notify.integer < 0)
957 Cvar_SetValueQuick(&con_notify, 0);
958 if (con_notify.integer > MAX_NOTIFYLINES)
959 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
960 if (gamemode == GAME_TRANSFUSION)
964 // 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
966 for (i= stop-con_notify.integer+1 ; i<=stop ; i++)
971 time = con_times[i % con_notify.integer];
974 time = cl.time - time;
975 if (time > con_notifytime.value)
977 text = con_text + (i % con_totallines)*con_linewidth;
979 if (gamemode == GAME_NEXUIZ) {
984 // count up to the last non-whitespace, and ignore color codes
985 for (j = 0;j < con_linewidth && text[j];j++)
987 if (text[j] == STRING_COLOR_TAG && (text[j+1] >= '0' && text[j+1] <= '9'))
997 // center the line using the calculated width
998 x = (vid_conwidth.integer - finalchars * con_textsize.value) * 0.5;
1002 DrawQ_String( x, v, text, con_linewidth, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, &colorindex, false );
1004 v += con_textsize.value;
1008 if (key_dest == key_message)
1010 int colorindex = -1;
1014 // LordHavoc: speedup, and other improvements
1016 sprintf(temptext, "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
1018 sprintf(temptext, "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
1019 while ((int)strlen(temptext) >= con_linewidth)
1021 DrawQ_String( 0, v, temptext, con_linewidth, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, &colorindex, false );
1022 strlcpy(temptext, &temptext[con_linewidth], sizeof(temptext));
1023 v += con_textsize.value;
1025 if (strlen(temptext) > 0)
1027 DrawQ_String( 0, v, temptext, 0, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, &colorindex, false );
1028 v += con_textsize.value;
1037 Draws the console with the solid background
1038 The typing input line at the bottom should only be drawn if typing is allowed
1041 void Con_DrawConsole (int lines)
1043 int i, rows, j, stop;
1046 int colorindex = -1;
1051 // draw the background
1052 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);
1053 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);
1056 con_vislines = lines;
1058 rows = (int)ceil((lines/con_textsize.value)-2); // rows of text to draw
1059 y = lines - (rows+2)*con_textsize.value; // may start slightly negative
1061 // 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
1063 for (i = stop - rows + 1;i <= stop;i++, y += con_textsize.value)
1065 j = max(i - con_backscroll, 0);
1066 text = con_text + (j % con_totallines)*con_linewidth;
1068 DrawQ_String( 0, y, text, con_linewidth, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, &colorindex, false );
1071 // draw the input prompt, user text, and cursor if desired
1079 Prints not only map filename, but also
1080 its format (q1/q2/q3/hl) and even its message
1082 //[515]: here is an ugly hack.. two gotos... oh my... *but it works*
1083 //LordHavoc: rewrote bsp type detection, added mcbsp support and rewrote message extraction to do proper worldspawn parsing
1084 //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
1085 //LordHavoc: FIXME: man this GetMapList is STILL ugly code even after my cleanups...
1086 qboolean GetMapList (const char *s, char *completedname, int completednamebufferlength)
1090 int i, k, max, p, o, min;
1093 unsigned char buf[1024];
1095 sprintf(message, "maps/%s*.bsp", s);
1096 t = FS_Search(message, 1, true);
1099 if (t->numfilenames > 1)
1100 Con_Printf("^1 %i maps found :\n", t->numfilenames);
1101 len = (unsigned char *)Z_Malloc(t->numfilenames);
1103 for(max=i=0;i<t->numfilenames;i++)
1105 k = (int)strlen(t->filenames[i]);
1115 for(i=0;i<t->numfilenames;i++)
1117 int lumpofs = 0, lumplen = 0;
1118 char *entities = NULL;
1119 const char *data = NULL;
1121 char entfilename[MAX_QPATH];
1122 strlcpy(message, "^1**ERROR**^7", sizeof(message));
1124 f = FS_Open(t->filenames[i], "rb", true, false);
1127 memset(buf, 0, 1024);
1128 FS_Read(f, buf, 1024);
1129 if (!memcmp(buf, "IBSP", 4))
1131 p = LittleLong(((int *)buf)[1]);
1132 if (p == Q3BSPVERSION)
1134 q3dheader_t *header = (q3dheader_t *)buf;
1135 lumpofs = LittleLong(header->lumps[Q3LUMP_ENTITIES].fileofs);
1136 lumplen = LittleLong(header->lumps[Q3LUMP_ENTITIES].filelen);
1138 else if (p == Q2BSPVERSION)
1140 q2dheader_t *header = (q2dheader_t *)buf;
1141 lumpofs = LittleLong(header->lumps[Q2LUMP_ENTITIES].fileofs);
1142 lumplen = LittleLong(header->lumps[Q2LUMP_ENTITIES].filelen);
1145 else if (!memcmp(buf, "MCBSPpad", 8))
1147 p = LittleLong(((int *)buf)[2]);
1148 if (p == MCBSPVERSION)
1150 int numhulls = LittleLong(((int *)buf)[3]);
1151 lumpofs = LittleLong(((int *)buf)[3 + numhulls + LUMP_ENTITIES*2+0]);
1152 lumplen = LittleLong(((int *)buf)[3 + numhulls + LUMP_ENTITIES*2+1]);
1155 else if((p = LittleLong(((int *)buf)[0])) == BSPVERSION || p == 30)
1157 dheader_t *header = (dheader_t *)buf;
1158 lumpofs = LittleLong(header->lumps[LUMP_ENTITIES].fileofs);
1159 lumplen = LittleLong(header->lumps[LUMP_ENTITIES].filelen);
1163 strlcpy(entfilename, t->filenames[i], sizeof(entfilename));
1164 memcpy(entfilename + strlen(entfilename) - 4, ".ent", 5);
1165 entities = (char *)FS_LoadFile(entfilename, tempmempool, true, NULL);
1166 if (!entities && lumplen >= 10)
1168 FS_Seek(f, lumpofs, SEEK_SET);
1169 entities = (char *)Z_Malloc(lumplen + 1);
1170 FS_Read(f, entities, lumplen);
1174 // if there are entities to parse, a missing message key just
1175 // means there is no title, so clear the message string now
1181 if (!COM_ParseToken_Simple(&data, false))
1183 if (com_token[0] == '{')
1185 if (com_token[0] == '}')
1187 // skip leading whitespace
1188 for (k = 0;com_token[k] && com_token[k] <= ' ';k++);
1189 for (l = 0;l < (int)sizeof(keyname) - 1 && com_token[k+l] && com_token[k+l] > ' ';l++)
1190 keyname[l] = com_token[k+l];
1192 if (!COM_ParseToken_Simple(&data, false))
1194 if (developer.integer >= 100)
1195 Con_Printf("key: %s %s\n", keyname, com_token);
1196 if (!strcmp(keyname, "message"))
1198 // get the message contents
1199 strlcpy(message, com_token, sizeof(message));
1209 *(t->filenames[i]+len[i]+5) = 0;
1212 case Q3BSPVERSION: strlcpy((char *)buf, "Q3", sizeof(buf));break;
1213 case Q2BSPVERSION: strlcpy((char *)buf, "Q2", sizeof(buf));break;
1214 case BSPVERSION: strlcpy((char *)buf, "Q1", sizeof(buf));break;
1215 case MCBSPVERSION: strlcpy((char *)buf, "MC", sizeof(buf));break;
1216 case 30: strlcpy((char *)buf, "HL", sizeof(buf));break;
1217 default: strlcpy((char *)buf, "??", sizeof(buf));break;
1219 Con_Printf("%16s (%s) %s\n", t->filenames[i]+5, buf, message);
1224 k = *(t->filenames[0]+5+p);
1227 for(i=1;i<t->numfilenames;i++)
1228 if(*(t->filenames[i]+5+p) != k)
1232 if(p > o && completedname && completednamebufferlength > 0)
1234 memset(completedname, 0, completednamebufferlength);
1235 memcpy(completedname, (t->filenames[0]+5), min(p, completednamebufferlength - 1));
1245 New function for tab-completion system
1246 Added by EvilTypeGuy
1247 MEGA Thanks to Taniwha
1250 void Con_DisplayList(const char **list)
1252 int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
1253 const char **walk = list;
1256 len = (int)strlen(*walk);
1264 len = (int)strlen(*list);
1265 if (pos + maxlen >= width) {
1271 for (i = 0; i < (maxlen - len); i++)
1283 Con_CompleteCommandLine
1285 New function for tab-completion system
1286 Added by EvilTypeGuy
1287 Thanks to Fett erich@heintz.com
1289 Enhanced to tab-complete map names by [515]
1292 void Con_CompleteCommandLine (void)
1294 const char *cmd = "";
1296 const char **list[3] = {0, 0, 0};
1298 int c, v, a, i, cmd_len, pos, k;
1300 //find what we want to complete
1304 k = key_lines[edit_line][pos];
1305 if(k == '\"' || k == ';' || k == ' ' || k == '\'')
1310 s = key_lines[edit_line] + pos;
1311 strlcpy(s2, key_lines[edit_line] + key_linepos, sizeof(s2)); //save chars after cursor
1312 key_lines[edit_line][key_linepos] = 0; //hide them
1315 for(k=pos-1;k>2;k--)
1316 if(key_lines[edit_line][k] != ' ')
1318 if(key_lines[edit_line][k] == '\"' || key_lines[edit_line][k] == ';' || key_lines[edit_line][k] == '\'')
1320 if ((pos+k > 2 && !strncmp(key_lines[edit_line]+k-2, "map", 3))
1321 || (pos+k > 10 && !strncmp(key_lines[edit_line]+k-10, "changelevel", 11)))
1324 if (GetMapList(s, t, sizeof(t)))
1326 // first move the cursor
1327 key_linepos += (int)strlen(t) - (int)strlen(s);
1329 // and now do the actual work
1331 strlcat(key_lines[edit_line], t, MAX_INPUTLINE);
1332 strlcat(key_lines[edit_line], s2, MAX_INPUTLINE); //add back chars after cursor
1334 // and fix the cursor
1335 if(key_linepos > (int) strlen(key_lines[edit_line]))
1336 key_linepos = (int) strlen(key_lines[edit_line]);
1342 // Count number of possible matches and print them
1343 c = Cmd_CompleteCountPossible(s);
1346 Con_Printf("\n%i possible command%s\n", c, (c > 1) ? "s: " : ":");
1347 Cmd_CompleteCommandPrint(s);
1349 v = Cvar_CompleteCountPossible(s);
1352 Con_Printf("\n%i possible variable%s\n", v, (v > 1) ? "s: " : ":");
1353 Cvar_CompleteCvarPrint(s);
1355 a = Cmd_CompleteAliasCountPossible(s);
1358 Con_Printf("\n%i possible aliases%s\n", a, (a > 1) ? "s: " : ":");
1359 Cmd_CompleteAliasPrint(s);
1362 if (!(c + v + a)) // No possible matches
1365 strlcpy(&key_lines[edit_line][key_linepos], s2, sizeof(key_lines[edit_line]) - key_linepos);
1370 cmd = *(list[0] = Cmd_CompleteBuildList(s));
1372 cmd = *(list[1] = Cvar_CompleteBuildList(s));
1374 cmd = *(list[2] = Cmd_CompleteAliasBuildList(s));
1376 for (cmd_len = (int)strlen(s);;cmd_len++)
1379 for (i = 0; i < 3; i++)
1381 for (l = list[i];*l;l++)
1382 if ((*l)[cmd_len] != cmd[cmd_len])
1384 // all possible matches share this character, so we continue...
1387 // if all matches ended at the same position, stop
1388 // (this means there is only one match)
1394 // prevent a buffer overrun by limiting cmd_len according to remaining space
1395 cmd_len = min(cmd_len, (int)sizeof(key_lines[edit_line]) - 1 - pos);
1399 memcpy(&key_lines[edit_line][key_linepos], cmd, cmd_len);
1400 key_linepos += cmd_len;
1401 // if there is only one match, add a space after it
1402 if (c + v + a == 1 && key_linepos < (int)sizeof(key_lines[edit_line]) - 1)
1403 key_lines[edit_line][key_linepos++] = ' ';
1406 // use strlcat to avoid a buffer overrun
1407 key_lines[edit_line][key_linepos] = 0;
1408 strlcat(key_lines[edit_line], s2, sizeof(key_lines[edit_line]));
1410 // free the command, cvar, and alias lists
1411 for (i = 0; i < 3; i++)
1413 Mem_Free((void *)list[i]);