]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - console.c
convert lightmaps to sRGB for nice sRGB support
[xonotic/darkplaces.git] / console.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
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.
8
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.
12
13 See the GNU General Public License for more details.
14
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.
18
19 */
20 // console.c
21
22 #if !defined(WIN32) || defined(__MINGW32__)
23 # include <unistd.h>
24 #endif
25 #include <time.h>
26
27 #include "quakedef.h"
28 #include "thread.h"
29
30 // for u8_encodech
31 #include "ft2.h"
32
33 float con_cursorspeed = 4;
34
35 // lines up from bottom to display
36 int con_backscroll;
37
38 conbuffer_t con;
39 void *con_mutex = NULL;
40
41 #define CON_LINES(i) CONBUFFER_LINES(&con, i)
42 #define CON_LINES_LAST CONBUFFER_LINES_LAST(&con)
43 #define CON_LINES_COUNT CONBUFFER_LINES_COUNT(&con)
44
45 cvar_t con_notifytime = {CVAR_SAVE, "con_notifytime","3", "how long notify lines last, in seconds"};
46 cvar_t con_notify = {CVAR_SAVE, "con_notify","4", "how many notify lines to show"};
47 cvar_t con_notifyalign = {CVAR_SAVE, "con_notifyalign", "", "how to align notify lines: 0 = left, 0.5 = center, 1 = right, empty string = game default)"};
48
49 cvar_t con_chattime = {CVAR_SAVE, "con_chattime","30", "how long chat lines last, in seconds"};
50 cvar_t con_chat = {CVAR_SAVE, "con_chat","0", "how many chat lines to show in a dedicated chat area"};
51 cvar_t con_chatpos = {CVAR_SAVE, "con_chatpos","0", "where to put chat (negative: lines from bottom of screen, positive: lines below notify, 0: at top)"};
52 cvar_t con_chatrect = {CVAR_SAVE, "con_chatrect","0", "use con_chatrect_x and _y to position con_notify and con_chat freely instead of con_chatpos"};
53 cvar_t con_chatrect_x = {CVAR_SAVE, "con_chatrect_x","", "where to put chat, relative x coordinate of left edge on screen (use con_chatwidth for width)"};
54 cvar_t con_chatrect_y = {CVAR_SAVE, "con_chatrect_y","", "where to put chat, relative y coordinate of top edge on screen (use con_chat for line count)"};
55 cvar_t con_chatwidth = {CVAR_SAVE, "con_chatwidth","1.0", "relative chat window width"};
56 cvar_t con_textsize = {CVAR_SAVE, "con_textsize","8", "console text size in virtual 2D pixels"};
57 cvar_t con_notifysize = {CVAR_SAVE, "con_notifysize","8", "notify text size in virtual 2D pixels"};
58 cvar_t con_chatsize = {CVAR_SAVE, "con_chatsize","8", "chat text size in virtual 2D pixels (if con_chat is enabled)"};
59 cvar_t con_chatsound = {CVAR_SAVE, "con_chatsound","1", "enables chat sound to play on message"};
60
61
62 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)"};
63 #ifdef WIN32
64 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)"};
65 #else
66 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)"};
67 #endif
68
69
70 cvar_t con_nickcompletion = {CVAR_SAVE, "con_nickcompletion", "1", "tab-complete nicks in console and message input"};
71 cvar_t con_nickcompletion_flags = {CVAR_SAVE, "con_nickcompletion_flags", "11", "Bitfield: "
72                                    "0: add nothing after completion. "
73                                    "1: add the last color after completion. "
74                                    "2: add a quote when starting a quote instead of the color. "
75                                    "4: will replace 1, will force color, even after a quote. "
76                                    "8: ignore non-alphanumerics. "
77                                    "16: ignore spaces. "};
78 #define NICKS_ADD_COLOR 1
79 #define NICKS_ADD_QUOTE 2
80 #define NICKS_FORCE_COLOR 4
81 #define NICKS_ALPHANUMERICS_ONLY 8
82 #define NICKS_NO_SPACES 16
83
84 cvar_t con_completion_playdemo = {CVAR_SAVE, "con_completion_playdemo", "*.dem", "completion pattern for the playdemo command"};
85 cvar_t con_completion_timedemo = {CVAR_SAVE, "con_completion_timedemo", "*.dem", "completion pattern for the timedemo command"};
86 cvar_t con_completion_exec = {CVAR_SAVE, "con_completion_exec", "*.cfg", "completion pattern for the exec command"};
87
88 int con_linewidth;
89 int con_vislines;
90
91 qboolean con_initialized;
92
93 // used for server replies to rcon command
94 lhnetsocket_t *rcon_redirect_sock = NULL;
95 lhnetaddress_t *rcon_redirect_dest = NULL;
96 int rcon_redirect_bufferpos = 0;
97 char rcon_redirect_buffer[1400];
98 qboolean rcon_redirect_proquakeprotocol = false;
99
100 // generic functions for console buffers
101
102 void ConBuffer_Init(conbuffer_t *buf, int textsize, int maxlines, mempool_t *mempool)
103 {
104         buf->active = true;
105         buf->textsize = textsize;
106         buf->text = (char *) Mem_Alloc(mempool, textsize);
107         buf->maxlines = maxlines;
108         buf->lines = (con_lineinfo_t *) Mem_Alloc(mempool, maxlines * sizeof(*buf->lines));
109         buf->lines_first = 0;
110         buf->lines_count = 0;
111 }
112
113 /*
114 ================
115 ConBuffer_Clear
116 ================
117 */
118 void ConBuffer_Clear (conbuffer_t *buf)
119 {
120         buf->lines_count = 0;
121 }
122
123 /*
124 ================
125 ConBuffer_Shutdown
126 ================
127 */
128 void ConBuffer_Shutdown(conbuffer_t *buf)
129 {
130         buf->active = false;
131         if (buf->text)
132                 Mem_Free(buf->text);
133         if (buf->lines)
134                 Mem_Free(buf->lines);
135         buf->text = NULL;
136         buf->lines = NULL;
137 }
138
139 /*
140 ================
141 ConBuffer_FixTimes
142
143 Notifies the console code about the current time
144 (and shifts back times of other entries when the time
145 went backwards)
146 ================
147 */
148 void ConBuffer_FixTimes(conbuffer_t *buf)
149 {
150         int i;
151         if(buf->lines_count >= 1)
152         {
153                 double diff = cl.time - CONBUFFER_LINES_LAST(buf).addtime;
154                 if(diff < 0)
155                 {
156                         for(i = 0; i < buf->lines_count; ++i)
157                                 CONBUFFER_LINES(buf, i).addtime += diff;
158                 }
159         }
160 }
161
162 /*
163 ================
164 ConBuffer_DeleteLine
165
166 Deletes the first line from the console history.
167 ================
168 */
169 void ConBuffer_DeleteLine(conbuffer_t *buf)
170 {
171         if(buf->lines_count == 0)
172                 return;
173         --buf->lines_count;
174         buf->lines_first = (buf->lines_first + 1) % buf->maxlines;
175 }
176
177 /*
178 ================
179 ConBuffer_DeleteLastLine
180
181 Deletes the last line from the console history.
182 ================
183 */
184 void ConBuffer_DeleteLastLine(conbuffer_t *buf)
185 {
186         if(buf->lines_count == 0)
187                 return;
188         --buf->lines_count;
189 }
190
191 /*
192 ================
193 ConBuffer_BytesLeft
194
195 Checks if there is space for a line of the given length, and if yes, returns a
196 pointer to the start of such a space, and NULL otherwise.
197 ================
198 */
199 static char *ConBuffer_BytesLeft(conbuffer_t *buf, int len)
200 {
201         if(len > buf->textsize)
202                 return NULL;
203         if(buf->lines_count == 0)
204                 return buf->text;
205         else
206         {
207                 char *firstline_start = buf->lines[buf->lines_first].start;
208                 char *lastline_onepastend = CONBUFFER_LINES_LAST(buf).start + CONBUFFER_LINES_LAST(buf).len;
209                 // the buffer is cyclic, so we first have two cases...
210                 if(firstline_start < lastline_onepastend) // buffer is contiguous
211                 {
212                         // put at end?
213                         if(len <= buf->text + buf->textsize - lastline_onepastend)
214                                 return lastline_onepastend;
215                         // put at beginning?
216                         else if(len <= firstline_start - buf->text)
217                                 return buf->text;
218                         else
219                                 return NULL;
220                 }
221                 else // buffer has a contiguous hole
222                 {
223                         if(len <= firstline_start - lastline_onepastend)
224                                 return lastline_onepastend;
225                         else
226                                 return NULL;
227                 }
228         }
229 }
230
231 /*
232 ================
233 ConBuffer_AddLine
234
235 Appends a given string as a new line to the console.
236 ================
237 */
238 void ConBuffer_AddLine(conbuffer_t *buf, const char *line, int len, int mask)
239 {
240         char *putpos;
241         con_lineinfo_t *p;
242
243         // developer_memory 1 during shutdown prints while conbuffer_t is being freed
244         if (!buf->active)
245                 return;
246
247         ConBuffer_FixTimes(buf);
248
249         if(len >= buf->textsize)
250         {
251                 // line too large?
252                 // only display end of line.
253                 line += len - buf->textsize + 1;
254                 len = buf->textsize - 1;
255         }
256         while(!(putpos = ConBuffer_BytesLeft(buf, len + 1)) || buf->lines_count >= buf->maxlines)
257                 ConBuffer_DeleteLine(buf);
258         memcpy(putpos, line, len);
259         putpos[len] = 0;
260         ++buf->lines_count;
261
262         //fprintf(stderr, "Now have %d lines (%d -> %d).\n", buf->lines_count, buf->lines_first, CON_LINES_LAST);
263
264         p = &CONBUFFER_LINES_LAST(buf);
265         p->start = putpos;
266         p->len = len;
267         p->addtime = cl.time;
268         p->mask = mask;
269         p->height = -1; // calculate when needed
270 }
271
272 int ConBuffer_FindPrevLine(conbuffer_t *buf, int mask_must, int mask_mustnot, int start)
273 {
274         int i;
275         if(start == -1)
276                 start = buf->lines_count;
277         for(i = start - 1; i >= 0; --i)
278         {
279                 con_lineinfo_t *l = &CONBUFFER_LINES(buf, i);
280
281                 if((l->mask & mask_must) != mask_must)
282                         continue;
283                 if(l->mask & mask_mustnot)
284                         continue;
285
286                 return i;
287         }
288
289         return -1;
290 }
291
292 int Con_FindNextLine(conbuffer_t *buf, int mask_must, int mask_mustnot, int start)
293 {
294         int i;
295         for(i = start + 1; i < buf->lines_count; ++i)
296         {
297                 con_lineinfo_t *l = &CONBUFFER_LINES(buf, i);
298
299                 if((l->mask & mask_must) != mask_must)
300                         continue;
301                 if(l->mask & mask_mustnot)
302                         continue;
303
304                 return i;
305         }
306
307         return -1;
308 }
309
310 const char *ConBuffer_GetLine(conbuffer_t *buf, int i)
311 {
312         static char copybuf[MAX_INPUTLINE];
313         con_lineinfo_t *l = &CONBUFFER_LINES(buf, i);
314         size_t sz = l->len+1 > sizeof(copybuf) ? sizeof(copybuf) : l->len+1;
315         strlcpy(copybuf, l->start, sz);
316         return copybuf;
317 }
318
319 /*
320 ==============================================================================
321
322 LOGGING
323
324 ==============================================================================
325 */
326
327 /// \name Logging
328 //@{
329 cvar_t log_file = {0, "log_file","", "filename to log messages to"};
330 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"};
331 char log_dest_buffer[1400]; // UDP packet
332 size_t log_dest_buffer_pos;
333 unsigned int log_dest_buffer_appending;
334 char crt_log_file [MAX_OSPATH] = "";
335 qfile_t* logfile = NULL;
336
337 unsigned char* logqueue = NULL;
338 size_t logq_ind = 0;
339 size_t logq_size = 0;
340
341 void Log_ConPrint (const char *msg);
342 //@}
343 /*
344 ====================
345 Log_DestBuffer_Init
346 ====================
347 */
348 static void Log_DestBuffer_Init(void)
349 {
350         memcpy(log_dest_buffer, "\377\377\377\377n", 5); // QW rcon print
351         log_dest_buffer_pos = 5;
352 }
353
354 /*
355 ====================
356 Log_DestBuffer_Flush
357 ====================
358 */
359 void Log_DestBuffer_Flush(void)
360 {
361         lhnetaddress_t log_dest_addr;
362         lhnetsocket_t *log_dest_socket;
363         const char *s = log_dest_udp.string;
364         qboolean have_opened_temp_sockets = false;
365         if(s) if(log_dest_buffer_pos > 5)
366         {
367                 ++log_dest_buffer_appending;
368                 log_dest_buffer[log_dest_buffer_pos++] = 0;
369
370                 if(!NetConn_HaveServerPorts() && !NetConn_HaveClientPorts()) // then temporarily open one
371                 {
372                         have_opened_temp_sockets = true;
373                         NetConn_OpenServerPorts(true);
374                 }
375
376                 while(COM_ParseToken_Console(&s))
377                         if(LHNETADDRESS_FromString(&log_dest_addr, com_token, 26000))
378                         {
379                                 log_dest_socket = NetConn_ChooseClientSocketForAddress(&log_dest_addr);
380                                 if(!log_dest_socket)
381                                         log_dest_socket = NetConn_ChooseServerSocketForAddress(&log_dest_addr);
382                                 if(log_dest_socket)
383                                         NetConn_WriteString(log_dest_socket, log_dest_buffer, &log_dest_addr);
384                         }
385
386                 if(have_opened_temp_sockets)
387                         NetConn_CloseServerPorts();
388                 --log_dest_buffer_appending;
389         }
390         log_dest_buffer_pos = 0;
391 }
392
393 /*
394 ====================
395 Log_Timestamp
396 ====================
397 */
398 const char* Log_Timestamp (const char *desc)
399 {
400         static char timestamp [128];
401         time_t crt_time;
402 #if _MSC_VER >= 1400
403         struct tm crt_tm;
404 #else
405         struct tm *crt_tm;
406 #endif
407         char timestring [64];
408
409         // Build the time stamp (ex: "Wed Jun 30 21:49:08 1993");
410         time (&crt_time);
411 #if _MSC_VER >= 1400
412         localtime_s (&crt_tm, &crt_time);
413         strftime (timestring, sizeof (timestring), "%a %b %d %H:%M:%S %Y", &crt_tm);
414 #else
415         crt_tm = localtime (&crt_time);
416         strftime (timestring, sizeof (timestring), "%a %b %d %H:%M:%S %Y", crt_tm);
417 #endif
418
419         if (desc != NULL)
420                 dpsnprintf (timestamp, sizeof (timestamp), "====== %s (%s) ======\n", desc, timestring);
421         else
422                 dpsnprintf (timestamp, sizeof (timestamp), "====== %s ======\n", timestring);
423
424         return timestamp;
425 }
426
427
428 /*
429 ====================
430 Log_Open
431 ====================
432 */
433 void Log_Open (void)
434 {
435         if (logfile != NULL || log_file.string[0] == '\0')
436                 return;
437
438         logfile = FS_OpenRealFile(log_file.string, "a", false);
439         if (logfile != NULL)
440         {
441                 strlcpy (crt_log_file, log_file.string, sizeof (crt_log_file));
442                 FS_Print (logfile, Log_Timestamp ("Log started"));
443         }
444 }
445
446
447 /*
448 ====================
449 Log_Close
450 ====================
451 */
452 void Log_Close (void)
453 {
454         if (logfile == NULL)
455                 return;
456
457         FS_Print (logfile, Log_Timestamp ("Log stopped"));
458         FS_Print (logfile, "\n");
459         FS_Close (logfile);
460
461         logfile = NULL;
462         crt_log_file[0] = '\0';
463 }
464
465
466 /*
467 ====================
468 Log_Start
469 ====================
470 */
471 void Log_Start (void)
472 {
473         size_t pos;
474         size_t n;
475         Log_Open ();
476
477         // Dump the contents of the log queue into the log file and free it
478         if (logqueue != NULL)
479         {
480                 unsigned char *temp = logqueue;
481                 logqueue = NULL;
482                 if(logq_ind != 0)
483                 {
484                         if (logfile != NULL)
485                                 FS_Write (logfile, temp, logq_ind);
486                         if(*log_dest_udp.string)
487                         {
488                                 for(pos = 0; pos < logq_ind; )
489                                 {
490                                         if(log_dest_buffer_pos == 0)
491                                                 Log_DestBuffer_Init();
492                                         n = min(sizeof(log_dest_buffer) - log_dest_buffer_pos - 1, logq_ind - pos);
493                                         memcpy(log_dest_buffer + log_dest_buffer_pos, temp + pos, n);
494                                         log_dest_buffer_pos += n;
495                                         Log_DestBuffer_Flush();
496                                         pos += n;
497                                 }
498                         }
499                 }
500                 Mem_Free (temp);
501                 logq_ind = 0;
502                 logq_size = 0;
503         }
504 }
505
506
507 /*
508 ================
509 Log_ConPrint
510 ================
511 */
512 void Log_ConPrint (const char *msg)
513 {
514         static qboolean inprogress = false;
515
516         // don't allow feedback loops with memory error reports
517         if (inprogress)
518                 return;
519         inprogress = true;
520
521         // Until the host is completely initialized, we maintain a log queue
522         // to store the messages, since the log can't be started before
523         if (logqueue != NULL)
524         {
525                 size_t remain = logq_size - logq_ind;
526                 size_t len = strlen (msg);
527
528                 // If we need to enlarge the log queue
529                 if (len > remain)
530                 {
531                         size_t factor = ((logq_ind + len) / logq_size) + 1;
532                         unsigned char* newqueue;
533
534                         logq_size *= factor;
535                         newqueue = (unsigned char *)Mem_Alloc (tempmempool, logq_size);
536                         memcpy (newqueue, logqueue, logq_ind);
537                         Mem_Free (logqueue);
538                         logqueue = newqueue;
539                         remain = logq_size - logq_ind;
540                 }
541                 memcpy (&logqueue[logq_ind], msg, len);
542                 logq_ind += len;
543
544                 inprogress = false;
545                 return;
546         }
547
548         // Check if log_file has changed
549         if (strcmp (crt_log_file, log_file.string) != 0)
550         {
551                 Log_Close ();
552                 Log_Open ();
553         }
554
555         // If a log file is available
556         if (logfile != NULL)
557                 FS_Print (logfile, msg);
558
559         inprogress = false;
560 }
561
562
563 /*
564 ================
565 Log_Printf
566 ================
567 */
568 void Log_Printf (const char *logfilename, const char *fmt, ...)
569 {
570         qfile_t *file;
571
572         file = FS_OpenRealFile(logfilename, "a", true);
573         if (file != NULL)
574         {
575                 va_list argptr;
576
577                 va_start (argptr, fmt);
578                 FS_VPrintf (file, fmt, argptr);
579                 va_end (argptr);
580
581                 FS_Close (file);
582         }
583 }
584
585
586 /*
587 ==============================================================================
588
589 CONSOLE
590
591 ==============================================================================
592 */
593
594 /*
595 ================
596 Con_ToggleConsole_f
597 ================
598 */
599 void Con_ToggleConsole_f (void)
600 {
601         if (COM_CheckParm ("-noconsole"))
602                 if (!(key_consoleactive & KEY_CONSOLEACTIVE_USER))
603                         return; // only allow the key bind to turn off console
604
605         // toggle the 'user wants console' bit
606         key_consoleactive ^= KEY_CONSOLEACTIVE_USER;
607         Con_ClearNotify();
608 }
609
610 /*
611 ================
612 Con_ClearNotify
613 ================
614 */
615 void Con_ClearNotify (void)
616 {
617         int i;
618         for(i = 0; i < CON_LINES_COUNT; ++i)
619                 CON_LINES(i).mask |= CON_MASK_HIDENOTIFY;
620 }
621
622
623 /*
624 ================
625 Con_MessageMode_f
626 ================
627 */
628 void Con_MessageMode_f (void)
629 {
630         key_dest = key_message;
631         chat_mode = 0; // "say"
632         if(Cmd_Argc() > 1)
633         {
634                 dpsnprintf(chat_buffer, sizeof(chat_buffer), "%s ", Cmd_Args());
635                 chat_bufferlen = strlen(chat_buffer);
636         }
637 }
638
639
640 /*
641 ================
642 Con_MessageMode2_f
643 ================
644 */
645 void Con_MessageMode2_f (void)
646 {
647         key_dest = key_message;
648         chat_mode = 1; // "say_team"
649         if(Cmd_Argc() > 1)
650         {
651                 dpsnprintf(chat_buffer, sizeof(chat_buffer), "%s ", Cmd_Args());
652                 chat_bufferlen = strlen(chat_buffer);
653         }
654 }
655
656 /*
657 ================
658 Con_CommandMode_f
659 ================
660 */
661 void Con_CommandMode_f (void)
662 {
663         key_dest = key_message;
664         if(Cmd_Argc() > 1)
665         {
666                 dpsnprintf(chat_buffer, sizeof(chat_buffer), "%s ", Cmd_Args());
667                 chat_bufferlen = strlen(chat_buffer);
668         }
669         chat_mode = -1; // command
670 }
671
672 /*
673 ================
674 Con_CheckResize
675 ================
676 */
677 void Con_CheckResize (void)
678 {
679         int i, width;
680         float f;
681
682         f = bound(1, con_textsize.value, 128);
683         if(f != con_textsize.value)
684                 Cvar_SetValueQuick(&con_textsize, f);
685         width = (int)floor(vid_conwidth.value / con_textsize.value);
686         width = bound(1, width, con.textsize/4);
687                 // FIXME uses con in a non abstracted way
688
689         if (width == con_linewidth)
690                 return;
691
692         con_linewidth = width;
693
694         for(i = 0; i < CON_LINES_COUNT; ++i)
695                 CON_LINES(i).height = -1; // recalculate when next needed
696
697         Con_ClearNotify();
698         con_backscroll = 0;
699 }
700
701 //[515]: the simplest command ever
702 //LordHavoc: not so simple after I made it print usage...
703 static void Con_Maps_f (void)
704 {
705         if (Cmd_Argc() > 2)
706         {
707                 Con_Printf("usage: maps [mapnameprefix]\n");
708                 return;
709         }
710         else if (Cmd_Argc() == 2)
711                 GetMapList(Cmd_Argv(1), NULL, 0);
712         else
713                 GetMapList("", NULL, 0);
714 }
715
716 void Con_ConDump_f (void)
717 {
718         int i;
719         qfile_t *file;
720         if (Cmd_Argc() != 2)
721         {
722                 Con_Printf("usage: condump <filename>\n");
723                 return;
724         }
725         file = FS_OpenRealFile(Cmd_Argv(1), "w", false);
726         if (!file)
727         {
728                 Con_Printf("condump: unable to write file \"%s\"\n", Cmd_Argv(1));
729                 return;
730         }
731         if (con_mutex) Thread_LockMutex(con_mutex);
732         for(i = 0; i < CON_LINES_COUNT; ++i)
733         {
734                 FS_Write(file, CON_LINES(i).start, CON_LINES(i).len);
735                 FS_Write(file, "\n", 1);
736         }
737         if (con_mutex) Thread_UnlockMutex(con_mutex);
738         FS_Close(file);
739 }
740
741 void Con_Clear_f (void)
742 {
743         if (con_mutex) Thread_LockMutex(con_mutex);
744         ConBuffer_Clear(&con);
745         if (con_mutex) Thread_UnlockMutex(con_mutex);
746 }
747
748 /*
749 ================
750 Con_Init
751 ================
752 */
753 void Con_Init (void)
754 {
755         con_linewidth = 80;
756         ConBuffer_Init(&con, CON_TEXTSIZE, CON_MAXLINES, zonemempool);
757         if (Thread_HasThreads())
758                 con_mutex = Thread_CreateMutex();
759
760         // Allocate a log queue, this will be freed after configs are parsed
761         logq_size = MAX_INPUTLINE;
762         logqueue = (unsigned char *)Mem_Alloc (tempmempool, logq_size);
763         logq_ind = 0;
764
765         Cvar_RegisterVariable (&sys_colortranslation);
766         Cvar_RegisterVariable (&sys_specialcharactertranslation);
767
768         Cvar_RegisterVariable (&log_file);
769         Cvar_RegisterVariable (&log_dest_udp);
770
771         // support for the classic Quake option
772 // COMMANDLINEOPTION: Console: -condebug logs console messages to qconsole.log, see also log_file
773         if (COM_CheckParm ("-condebug") != 0)
774                 Cvar_SetQuick (&log_file, "qconsole.log");
775
776         // register our cvars
777         Cvar_RegisterVariable (&con_chat);
778         Cvar_RegisterVariable (&con_chatpos);
779         Cvar_RegisterVariable (&con_chatrect_x);
780         Cvar_RegisterVariable (&con_chatrect_y);
781         Cvar_RegisterVariable (&con_chatrect);
782         Cvar_RegisterVariable (&con_chatsize);
783         Cvar_RegisterVariable (&con_chattime);
784         Cvar_RegisterVariable (&con_chatwidth);
785         Cvar_RegisterVariable (&con_notify);
786         Cvar_RegisterVariable (&con_notifyalign);
787         Cvar_RegisterVariable (&con_notifysize);
788         Cvar_RegisterVariable (&con_notifytime);
789         Cvar_RegisterVariable (&con_textsize);
790         Cvar_RegisterVariable (&con_chatsound);
791
792         // --blub
793         Cvar_RegisterVariable (&con_nickcompletion);
794         Cvar_RegisterVariable (&con_nickcompletion_flags);
795
796         Cvar_RegisterVariable (&con_completion_playdemo); // *.dem
797         Cvar_RegisterVariable (&con_completion_timedemo); // *.dem
798         Cvar_RegisterVariable (&con_completion_exec); // *.cfg
799
800         // register our commands
801         Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f, "opens or closes the console");
802         Cmd_AddCommand ("messagemode", Con_MessageMode_f, "input a chat message to say to everyone");
803         Cmd_AddCommand ("messagemode2", Con_MessageMode2_f, "input a chat message to say to only your team");
804         Cmd_AddCommand ("commandmode", Con_CommandMode_f, "input a console command");
805         Cmd_AddCommand ("clear", Con_Clear_f, "clear console history");
806         Cmd_AddCommand ("maps", Con_Maps_f, "list information about available maps");
807         Cmd_AddCommand ("condump", Con_ConDump_f, "output console history to a file (see also log_file)");
808
809         con_initialized = true;
810         Con_DPrint("Console initialized.\n");
811 }
812
813 void Con_Shutdown (void)
814 {
815         if (con_mutex) Thread_LockMutex(con_mutex);
816         ConBuffer_Shutdown(&con);
817         if (con_mutex) Thread_UnlockMutex(con_mutex);
818         if (con_mutex) Thread_DestroyMutex(con_mutex);con_mutex = NULL;
819 }
820
821 /*
822 ================
823 Con_PrintToHistory
824
825 Handles cursor positioning, line wrapping, etc
826 All console printing must go through this in order to be displayed
827 If no console is visible, the notify window will pop up.
828 ================
829 */
830 void Con_PrintToHistory(const char *txt, int mask)
831 {
832         // process:
833         //   \n goes to next line
834         //   \r deletes current line and makes a new one
835
836         static int cr_pending = 0;
837         static char buf[CON_TEXTSIZE];
838         static int bufpos = 0;
839
840         if(!con.text) // FIXME uses a non-abstracted property of con
841                 return;
842
843         if (con_mutex) Thread_LockMutex(con_mutex);
844         for(; *txt; ++txt)
845         {
846                 if(cr_pending)
847                 {
848                         ConBuffer_DeleteLastLine(&con);
849                         cr_pending = 0;
850                 }
851                 switch(*txt)
852                 {
853                         case 0:
854                                 break;
855                         case '\r':
856                                 ConBuffer_AddLine(&con, buf, bufpos, mask);
857                                 bufpos = 0;
858                                 cr_pending = 1;
859                                 break;
860                         case '\n':
861                                 ConBuffer_AddLine(&con, buf, bufpos, mask);
862                                 bufpos = 0;
863                                 break;
864                         default:
865                                 buf[bufpos++] = *txt;
866                                 if(bufpos >= con.textsize - 1) // FIXME uses a non-abstracted property of con
867                                 {
868                                         ConBuffer_AddLine(&con, buf, bufpos, mask);
869                                         bufpos = 0;
870                                 }
871                                 break;
872                 }
873         }
874         if (con_mutex) Thread_UnlockMutex(con_mutex);
875 }
876
877 /*! The translation table between the graphical font and plain ASCII  --KB */
878 static char qfont_table[256] = {
879         '\0', '#',  '#',  '#',  '#',  '.',  '#',  '#',
880         '#',  9,    10,   '#',  ' ',  13,   '.',  '.',
881         '[',  ']',  '0',  '1',  '2',  '3',  '4',  '5',
882         '6',  '7',  '8',  '9',  '.',  '<',  '=',  '>',
883         ' ',  '!',  '"',  '#',  '$',  '%',  '&',  '\'',
884         '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',
885         '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',
886         '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',
887         '@',  'A',  'B',  'C',  'D',  'E',  'F',  'G',
888         'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',
889         'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',
890         'X',  'Y',  'Z',  '[',  '\\', ']',  '^',  '_',
891         '`',  'a',  'b',  'c',  'd',  'e',  'f',  'g',
892         'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
893         'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
894         'x',  'y',  'z',  '{',  '|',  '}',  '~',  '<',
895
896         '<',  '=',  '>',  '#',  '#',  '.',  '#',  '#',
897         '#',  '#',  ' ',  '#',  ' ',  '>',  '.',  '.',
898         '[',  ']',  '0',  '1',  '2',  '3',  '4',  '5',
899         '6',  '7',  '8',  '9',  '.',  '<',  '=',  '>',
900         ' ',  '!',  '"',  '#',  '$',  '%',  '&',  '\'',
901         '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',
902         '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',
903         '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',
904         '@',  'A',  'B',  'C',  'D',  'E',  'F',  'G',
905         'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',
906         'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',
907         'X',  'Y',  'Z',  '[',  '\\', ']',  '^',  '_',
908         '`',  'a',  'b',  'c',  'd',  'e',  'f',  'g',
909         'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
910         'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
911         'x',  'y',  'z',  '{',  '|',  '}',  '~',  '<'
912 };
913
914 void Con_Rcon_Redirect_Init(lhnetsocket_t *sock, lhnetaddress_t *dest, qboolean proquakeprotocol)
915 {
916         rcon_redirect_sock = sock;
917         rcon_redirect_dest = dest;
918         rcon_redirect_proquakeprotocol = proquakeprotocol;
919         if (rcon_redirect_proquakeprotocol)
920         {
921                 // reserve space for the packet header
922                 rcon_redirect_buffer[0] = 0;
923                 rcon_redirect_buffer[1] = 0;
924                 rcon_redirect_buffer[2] = 0;
925                 rcon_redirect_buffer[3] = 0;
926                 // this is a reply to a CCREQ_RCON
927                 rcon_redirect_buffer[4] = (char)CCREP_RCON;
928         }
929         else
930                 memcpy(rcon_redirect_buffer, "\377\377\377\377n", 5); // QW rcon print
931         rcon_redirect_bufferpos = 5;
932 }
933
934 void Con_Rcon_Redirect_Flush(void)
935 {
936         rcon_redirect_buffer[rcon_redirect_bufferpos] = 0;
937         if (rcon_redirect_proquakeprotocol)
938         {
939                 // update the length in the packet header
940                 StoreBigLong((unsigned char *)rcon_redirect_buffer, NETFLAG_CTL | (rcon_redirect_bufferpos & NETFLAG_LENGTH_MASK));
941         }
942         NetConn_Write(rcon_redirect_sock, rcon_redirect_buffer, rcon_redirect_bufferpos, rcon_redirect_dest);
943         memcpy(rcon_redirect_buffer, "\377\377\377\377n", 5); // QW rcon print
944         rcon_redirect_bufferpos = 5;
945         rcon_redirect_proquakeprotocol = false;
946 }
947
948 void Con_Rcon_Redirect_End(void)
949 {
950         Con_Rcon_Redirect_Flush();
951         rcon_redirect_dest = NULL;
952         rcon_redirect_sock = NULL;
953 }
954
955 void Con_Rcon_Redirect_Abort(void)
956 {
957         rcon_redirect_dest = NULL;
958         rcon_redirect_sock = NULL;
959 }
960
961 /*
962 ================
963 Con_Rcon_AddChar
964 ================
965 */
966 /// Adds a character to the rcon buffer.
967 void Con_Rcon_AddChar(int c)
968 {
969         if(log_dest_buffer_appending)
970                 return;
971         ++log_dest_buffer_appending;
972
973         // if this print is in response to an rcon command, add the character
974         // to the rcon redirect buffer
975
976         if (rcon_redirect_dest)
977         {
978                 rcon_redirect_buffer[rcon_redirect_bufferpos++] = c;
979                 if(rcon_redirect_bufferpos >= (int)sizeof(rcon_redirect_buffer) - 1)
980                         Con_Rcon_Redirect_Flush();
981         }
982         else if(*log_dest_udp.string) // don't duplicate rcon command responses here, these are sent another way
983         {
984                 if(log_dest_buffer_pos == 0)
985                         Log_DestBuffer_Init();
986                 log_dest_buffer[log_dest_buffer_pos++] = c;
987                 if(log_dest_buffer_pos >= sizeof(log_dest_buffer) - 1) // minus one, to allow for terminating zero
988                         Log_DestBuffer_Flush();
989         }
990         else
991                 log_dest_buffer_pos = 0;
992
993         --log_dest_buffer_appending;
994 }
995
996 /**
997  * Convert an RGB color to its nearest quake color.
998  * I'll cheat on this a bit by translating the colors to HSV first,
999  * S and V decide if it's black or white, otherwise, H will decide the
1000  * actual color.
1001  * @param _r Red (0-255)
1002  * @param _g Green (0-255)
1003  * @param _b Blue (0-255)
1004  * @return A quake color character.
1005  */
1006 static char Sys_Con_NearestColor(const unsigned char _r, const unsigned char _g, const unsigned char _b)
1007 {
1008         float r = ((float)_r)/255.0;
1009         float g = ((float)_g)/255.0;
1010         float b = ((float)_b)/255.0;
1011         float min = min(r, min(g, b));
1012         float max = max(r, max(g, b));
1013
1014         int h; ///< Hue angle [0,360]
1015         float s; ///< Saturation [0,1]
1016         float v = max; ///< In HSV v == max [0,1]
1017
1018         if(max == min)
1019                 s = 0;
1020         else
1021                 s = 1.0 - (min/max);
1022
1023         // Saturation threshold. We now say 0.2 is the minimum value for a color!
1024         if(s < 0.2)
1025         {
1026                 // If the value is less than half, return a black color code.
1027                 // Otherwise return a white one.
1028                 if(v < 0.5)
1029                         return '0';
1030                 return '7';
1031         }
1032
1033         // Let's get the hue angle to define some colors:
1034         if(max == min)
1035                 h = 0;
1036         else if(max == r)
1037                 h = (int)(60.0 * (g-b)/(max-min))%360;
1038         else if(max == g)
1039                 h = (int)(60.0 * (b-r)/(max-min) + 120);
1040         else // if(max == b) redundant check
1041                 h = (int)(60.0 * (r-g)/(max-min) + 240);
1042
1043         if(h < 36) // *red* to orange
1044                 return '1';
1045         else if(h < 80) // orange over *yellow* to evilish-bright-green
1046                 return '3';
1047         else if(h < 150) // evilish-bright-green over *green* to ugly bright blue
1048                 return '2';
1049         else if(h < 200) // ugly bright blue over *bright blue* to darkish blue
1050                 return '5';
1051         else if(h < 270) // darkish blue over *dark blue* to cool purple
1052                 return '4';
1053         else if(h < 330) // cool purple over *purple* to ugly swiny red
1054                 return '6';
1055         else // ugly red to red closes the circly
1056                 return '1';
1057 }
1058
1059 /*
1060 ================
1061 Con_MaskPrint
1062 ================
1063 */
1064 extern cvar_t timestamps;
1065 extern cvar_t timeformat;
1066 extern qboolean sys_nostdout;
1067 void Con_MaskPrint(int additionalmask, const char *msg)
1068 {
1069         static int mask = 0;
1070         static int index = 0;
1071         static char line[MAX_INPUTLINE];
1072
1073         if (con_mutex)
1074                 Thread_LockMutex(con_mutex);
1075
1076         for (;*msg;msg++)
1077         {
1078                 Con_Rcon_AddChar(*msg);
1079                 // if this is the beginning of a new line, print timestamp
1080                 if (index == 0)
1081                 {
1082                         const char *timestamp = timestamps.integer ? Sys_TimeString(timeformat.string) : "";
1083                         // reset the color
1084                         // FIXME: 1. perhaps we should use a terminal system 2. use a constant instead of 7!
1085                         line[index++] = STRING_COLOR_TAG;
1086                         // assert( STRING_COLOR_DEFAULT < 10 )
1087                         line[index++] = STRING_COLOR_DEFAULT + '0';
1088                         // special color codes for chat messages must always come first
1089                         // for Con_PrintToHistory to work properly
1090                         if (*msg == 1 || *msg == 2)
1091                         {
1092                                 // play talk wav
1093                                 if (*msg == 1)
1094                                 {
1095                                         if (con_chatsound.value)
1096                                         {
1097                                                 if(gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC)
1098                                                 {
1099                                                         if(msg[1] == '\r' && cl.foundtalk2wav)
1100                                                                 S_LocalSound ("sound/misc/talk2.wav");
1101                                                         else
1102                                                                 S_LocalSound ("sound/misc/talk.wav");
1103                                                 }
1104                                                 else
1105                                                 {
1106                                                         if (msg[1] == '(' && cl.foundtalk2wav)
1107                                                                 S_LocalSound ("sound/misc/talk2.wav");
1108                                                         else
1109                                                                 S_LocalSound ("sound/misc/talk.wav");
1110                                                 }
1111                                         }
1112                                         mask = CON_MASK_CHAT;
1113                                 }
1114                                 line[index++] = STRING_COLOR_TAG;
1115                                 line[index++] = '3';
1116                                 msg++;
1117                                 Con_Rcon_AddChar(*msg);
1118                         }
1119                         // store timestamp
1120                         for (;*timestamp;index++, timestamp++)
1121                                 if (index < (int)sizeof(line) - 2)
1122                                         line[index] = *timestamp;
1123                         // add the mask
1124                         mask |= additionalmask;
1125                 }
1126                 // append the character
1127                 line[index++] = *msg;
1128                 // if this is a newline character, we have a complete line to print
1129                 if (*msg == '\n' || index >= (int)sizeof(line) / 2)
1130                 {
1131                         // terminate the line
1132                         line[index] = 0;
1133                         // send to log file
1134                         Log_ConPrint(line);
1135                         // send to scrollable buffer
1136                         if (con_initialized && cls.state != ca_dedicated)
1137                         {
1138                                 Con_PrintToHistory(line, mask);
1139                         }
1140                         // send to terminal or dedicated server window
1141                         if (!sys_nostdout)
1142                         if (developer.integer || !(mask & CON_MASK_DEVELOPER))
1143                         {
1144                                 if(sys_specialcharactertranslation.integer)
1145                                 {
1146                                         char *p;
1147                                         const char *q;
1148                                         p = line;
1149                                         while(*p)
1150                                         {
1151                                                 int ch = u8_getchar(p, &q);
1152                                                 if(ch >= 0xE000 && ch <= 0xE0FF)
1153                                                 {
1154                                                         *p = qfont_table[ch - 0xE000];
1155                                                         if(q > p+1)
1156                                                                 memmove(p+1, q, strlen(q)+1);
1157                                                         p = p + 1;
1158                                                 }
1159                                                 else
1160                                                         p = p + (q - p);
1161                                         }
1162                                 }
1163
1164                                 if(sys_colortranslation.integer == 1) // ANSI
1165                                 {
1166                                         static char printline[MAX_INPUTLINE * 4 + 3];
1167                                                 // 2 can become 7 bytes, rounding that up to 8, and 3 bytes are added at the end
1168                                                 // a newline can transform into four bytes, but then prevents the three extra bytes from appearing
1169                                         int lastcolor = 0;
1170                                         const char *in;
1171                                         char *out;
1172                                         int color;
1173                                         for(in = line, out = printline; *in; ++in)
1174                                         {
1175                                                 switch(*in)
1176                                                 {
1177                                                         case STRING_COLOR_TAG:
1178                                                                 if( in[1] == STRING_COLOR_RGB_TAG_CHAR && isxdigit(in[2]) && isxdigit(in[3]) && isxdigit(in[4]) )
1179                                                                 {
1180                                                                         char r = tolower(in[2]);
1181                                                                         char g = tolower(in[3]);
1182                                                                         char b = tolower(in[4]);
1183                                                                         // it's a hex digit already, so the else part needs no check --blub
1184                                                                         if(isdigit(r)) r -= '0';
1185                                                                         else r -= 87;
1186                                                                         if(isdigit(g)) g -= '0';
1187                                                                         else g -= 87;
1188                                                                         if(isdigit(b)) b -= '0';
1189                                                                         else b -= 87;
1190                                                                         
1191                                                                         color = Sys_Con_NearestColor(r * 17, g * 17, b * 17);
1192                                                                         in += 3; // 3 only, the switch down there does the fourth
1193                                                                 }
1194                                                                 else
1195                                                                         color = in[1];
1196                                                                 
1197                                                                 switch(color)
1198                                                                 {
1199                                                                         case STRING_COLOR_TAG:
1200                                                                                 ++in;
1201                                                                                 *out++ = STRING_COLOR_TAG;
1202                                                                                 break;
1203                                                                         case '0':
1204                                                                         case '7':
1205                                                                                 // normal color
1206                                                                                 ++in;
1207                                                                                 if(lastcolor == 0) break; else lastcolor = 0;
1208                                                                                 *out++ = 0x1B; *out++ = '['; *out++ = 'm';
1209                                                                                 break;
1210                                                                         case '1':
1211                                                                                 // light red
1212                                                                                 ++in;
1213                                                                                 if(lastcolor == 1) break; else lastcolor = 1;
1214                                                                                 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '1'; *out++ = 'm';
1215                                                                                 break;
1216                                                                         case '2':
1217                                                                                 // light green
1218                                                                                 ++in;
1219                                                                                 if(lastcolor == 2) break; else lastcolor = 2;
1220                                                                                 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '2'; *out++ = 'm';
1221                                                                                 break;
1222                                                                         case '3':
1223                                                                                 // yellow
1224                                                                                 ++in;
1225                                                                                 if(lastcolor == 3) break; else lastcolor = 3;
1226                                                                                 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '3'; *out++ = 'm';
1227                                                                                 break;
1228                                                                         case '4':
1229                                                                                 // light blue
1230                                                                                 ++in;
1231                                                                                 if(lastcolor == 4) break; else lastcolor = 4;
1232                                                                                 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '4'; *out++ = 'm';
1233                                                                                 break;
1234                                                                         case '5':
1235                                                                                 // light cyan
1236                                                                                 ++in;
1237                                                                                 if(lastcolor == 5) break; else lastcolor = 5;
1238                                                                                 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '6'; *out++ = 'm';
1239                                                                                 break;
1240                                                                         case '6':
1241                                                                                 // light magenta
1242                                                                                 ++in;
1243                                                                                 if(lastcolor == 6) break; else lastcolor = 6;
1244                                                                                 *out++ = 0x1B; *out++ = '['; *out++ = '1'; *out++ = ';'; *out++ = '3'; *out++ = '5'; *out++ = 'm';
1245                                                                                 break;
1246                                                                         // 7 handled above
1247                                                                         case '8':
1248                                                                         case '9':
1249                                                                                 // bold normal color
1250                                                                                 ++in;
1251                                                                                 if(lastcolor == 8) break; else lastcolor = 8;
1252                                                                                 *out++ = 0x1B; *out++ = '['; *out++ = '0'; *out++ = ';'; *out++ = '1'; *out++ = 'm';
1253                                                                                 break;
1254                                                                         default:
1255                                                                                 *out++ = STRING_COLOR_TAG;
1256                                                                                 break;
1257                                                                 }
1258                                                                 break;
1259                                                         case '\n':
1260                                                                 if(lastcolor != 0)
1261                                                                 {
1262                                                                         *out++ = 0x1B; *out++ = '['; *out++ = 'm';
1263                                                                         lastcolor = 0;
1264                                                                 }
1265                                                                 *out++ = *in;
1266                                                                 break;
1267                                                         default:
1268                                                                 *out++ = *in;
1269                                                                 break;
1270                                                 }
1271                                         }
1272                                         if(lastcolor != 0)
1273                                         {
1274                                                 *out++ = 0x1B;
1275                                                 *out++ = '[';
1276                                                 *out++ = 'm';
1277                                         }
1278                                         *out++ = 0;
1279                                         Sys_PrintToTerminal(printline);
1280                                 }
1281                                 else if(sys_colortranslation.integer == 2) // Quake
1282                                 {
1283                                         Sys_PrintToTerminal(line);
1284                                 }
1285                                 else // strip
1286                                 {
1287                                         static char printline[MAX_INPUTLINE]; // it can only get shorter here
1288                                         const char *in;
1289                                         char *out;
1290                                         for(in = line, out = printline; *in; ++in)
1291                                         {
1292                                                 switch(*in)
1293                                                 {
1294                                                         case STRING_COLOR_TAG:
1295                                                                 switch(in[1])
1296                                                                 {
1297                                                                         case STRING_COLOR_RGB_TAG_CHAR:
1298                                                                                 if ( isxdigit(in[2]) && isxdigit(in[3]) && isxdigit(in[4]) )
1299                                                                                 {
1300                                                                                         in+=4;
1301                                                                                         break;
1302                                                                                 }
1303                                                                                 *out++ = STRING_COLOR_TAG;
1304                                                                                 *out++ = STRING_COLOR_RGB_TAG_CHAR;
1305                                                                                 ++in;
1306                                                                                 break;
1307                                                                         case STRING_COLOR_TAG:
1308                                                                                 ++in;
1309                                                                                 *out++ = STRING_COLOR_TAG;
1310                                                                                 break;
1311                                                                         case '0':
1312                                                                         case '1':
1313                                                                         case '2':
1314                                                                         case '3':
1315                                                                         case '4':
1316                                                                         case '5':
1317                                                                         case '6':
1318                                                                         case '7':
1319                                                                         case '8':
1320                                                                         case '9':
1321                                                                                 ++in;
1322                                                                                 break;
1323                                                                         default:
1324                                                                                 *out++ = STRING_COLOR_TAG;
1325                                                                                 break;
1326                                                                 }
1327                                                                 break;
1328                                                         default:
1329                                                                 *out++ = *in;
1330                                                                 break;
1331                                                 }
1332                                         }
1333                                         *out++ = 0;
1334                                         Sys_PrintToTerminal(printline);
1335                                 }
1336                         }
1337                         // empty the line buffer
1338                         index = 0;
1339                         mask = 0;
1340                 }
1341         }
1342
1343         if (con_mutex)
1344                 Thread_UnlockMutex(con_mutex);
1345 }
1346
1347 /*
1348 ================
1349 Con_MaskPrintf
1350 ================
1351 */
1352 void Con_MaskPrintf(int mask, const char *fmt, ...)
1353 {
1354         va_list argptr;
1355         char msg[MAX_INPUTLINE];
1356
1357         va_start(argptr,fmt);
1358         dpvsnprintf(msg,sizeof(msg),fmt,argptr);
1359         va_end(argptr);
1360
1361         Con_MaskPrint(mask, msg);
1362 }
1363
1364 /*
1365 ================
1366 Con_Print
1367 ================
1368 */
1369 void Con_Print(const char *msg)
1370 {
1371         Con_MaskPrint(CON_MASK_PRINT, msg);
1372 }
1373
1374 /*
1375 ================
1376 Con_Printf
1377 ================
1378 */
1379 void Con_Printf(const char *fmt, ...)
1380 {
1381         va_list argptr;
1382         char msg[MAX_INPUTLINE];
1383
1384         va_start(argptr,fmt);
1385         dpvsnprintf(msg,sizeof(msg),fmt,argptr);
1386         va_end(argptr);
1387
1388         Con_MaskPrint(CON_MASK_PRINT, msg);
1389 }
1390
1391 /*
1392 ================
1393 Con_DPrint
1394 ================
1395 */
1396 void Con_DPrint(const char *msg)
1397 {
1398         if(developer.integer < 0) // at 0, we still add to the buffer but hide
1399                 return;
1400
1401         Con_MaskPrint(CON_MASK_DEVELOPER, msg);
1402 }
1403
1404 /*
1405 ================
1406 Con_DPrintf
1407 ================
1408 */
1409 void Con_DPrintf(const char *fmt, ...)
1410 {
1411         va_list argptr;
1412         char msg[MAX_INPUTLINE];
1413
1414         if(developer.integer < 0) // at 0, we still add to the buffer but hide
1415                 return;
1416
1417         va_start(argptr,fmt);
1418         dpvsnprintf(msg,sizeof(msg),fmt,argptr);
1419         va_end(argptr);
1420
1421         Con_MaskPrint(CON_MASK_DEVELOPER, msg);
1422 }
1423
1424
1425 /*
1426 ==============================================================================
1427
1428 DRAWING
1429
1430 ==============================================================================
1431 */
1432
1433 /*
1434 ================
1435 Con_DrawInput
1436
1437 The input line scrolls horizontally if typing goes beyond the right edge
1438
1439 Modified by EvilTypeGuy eviltypeguy@qeradiant.com
1440 ================
1441 */
1442 extern cvar_t r_font_disable_freetype;
1443 void Con_DrawInput (void)
1444 {
1445         int             y;
1446         int             i;
1447         char editlinecopy[MAX_INPUTLINE+1], *text;
1448         float x, xo;
1449         size_t len_out;
1450         int col_out;
1451
1452         if (!key_consoleactive)
1453                 return;         // don't draw anything
1454
1455         strlcpy(editlinecopy, key_line, sizeof(editlinecopy));
1456         text = editlinecopy;
1457
1458         // Advanced Console Editing by Radix radix@planetquake.com
1459         // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
1460         // use strlen of edit_line instead of key_linepos to allow editing
1461         // of early characters w/o erasing
1462
1463         y = (int)strlen(text);
1464
1465         // append enoug nul-bytes to cover the utf8-versions of the cursor too
1466         for (i = y; i < y + 4 && i < (int)sizeof(editlinecopy); ++i)
1467                 text[i] = 0;
1468
1469         // add the cursor frame
1470         if (r_font_disable_freetype.integer)
1471         {
1472                 // this code is freetype incompatible!
1473                 if ((int)(realtime*con_cursorspeed) & 1)                // cursor is visible
1474                 {
1475                         if (!utf8_enable.integer)
1476                                 text[key_linepos] = 11 + 130 * key_insert;      // either solid or triangle facing right
1477                         else if (y + 3 < (int)sizeof(editlinecopy)-1)
1478                         {
1479                                 int ofs = u8_bytelen(text + key_linepos, 1);
1480                                 size_t len;
1481                                 const char *curbuf;
1482                                 curbuf = u8_encodech(0xE000 + 11 + 130 * key_insert, &len);
1483
1484                                 if (curbuf)
1485                                 {
1486                                         memmove(text + key_linepos + len, text + key_linepos + ofs, sizeof(editlinecopy) - key_linepos - len);
1487                                         memcpy(text + key_linepos, curbuf, len);
1488                                 }
1489                         } else
1490                                 text[key_linepos] = '-' + ('+' - '-') * key_insert;
1491                 }
1492         }
1493
1494 //      text[key_linepos + 1] = 0;
1495
1496         len_out = key_linepos;
1497         col_out = -1;
1498         xo = DrawQ_TextWidth_UntilWidth_TrackColors(text, &len_out, con_textsize.value, con_textsize.value, &col_out, false, FONT_CONSOLE, 1000000000);
1499         x = vid_conwidth.value * 0.95 - xo; // scroll
1500         if(x >= 0)
1501                 x = 0;
1502
1503         // draw it
1504         DrawQ_String(x, con_vislines - con_textsize.value*2, text, y + 3, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, NULL, false, FONT_CONSOLE );
1505
1506         // add a cursor on top of this (when using freetype)
1507         if (!r_font_disable_freetype.integer)
1508         {
1509                 if ((int)(realtime*con_cursorspeed) & 1)                // cursor is visible
1510                 {
1511                         if (!utf8_enable.integer)
1512                         {
1513                                 text[0] = 11 + 130 * key_insert;        // either solid or triangle facing right
1514                                 text[1] = 0;
1515                         }
1516                         else
1517                         {
1518                                 size_t len;
1519                                 const char *curbuf;
1520                                 curbuf = u8_encodech(0xE000 + 11 + 130 * key_insert, &len);
1521                                 memcpy(text, curbuf, len);
1522                                 text[len] = 0;
1523                         }
1524                         DrawQ_String(x + xo, con_vislines - con_textsize.value*2, text, 0, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, &col_out, false, FONT_CONSOLE);
1525                 }
1526         }
1527
1528         // remove cursor
1529 //      key_line[key_linepos] = 0;
1530 }
1531
1532 typedef struct
1533 {
1534         dp_font_t *font;
1535         float alignment; // 0 = left, 0.5 = center, 1 = right
1536         float fontsize;
1537         float x;
1538         float y;
1539         float width;
1540         float ymin, ymax;
1541         const char *continuationString;
1542
1543         // PRIVATE:
1544         int colorindex; // init to -1
1545 }
1546 con_text_info_t;
1547
1548 float Con_WordWidthFunc(void *passthrough, const char *w, size_t *length, float maxWidth)
1549 {
1550         con_text_info_t *ti = (con_text_info_t *) passthrough;
1551         if(w == NULL)
1552         {
1553                 ti->colorindex = -1;
1554                 return ti->fontsize * ti->font->maxwidth;
1555         }
1556         if(maxWidth >= 0)
1557                 return DrawQ_TextWidth_UntilWidth(w, length, ti->fontsize, ti->fontsize, false, ti->font, -maxWidth); // -maxWidth: we want at least one char
1558         else if(maxWidth == -1)
1559                 return DrawQ_TextWidth(w, *length, ti->fontsize, ti->fontsize, false, ti->font);
1560         else
1561         {
1562                 printf("Con_WordWidthFunc: can't get here (maxWidth should never be %f)\n", maxWidth);
1563                 // Note: this is NOT a Con_Printf, as it could print recursively
1564                 return 0;
1565         }
1566 }
1567
1568 int Con_CountLineFunc(void *passthrough, const char *line, size_t length, float width, qboolean isContinuation)
1569 {
1570         (void) passthrough;
1571         (void) line;
1572         (void) length;
1573         (void) width;
1574         (void) isContinuation;
1575         return 1;
1576 }
1577
1578 int Con_DisplayLineFunc(void *passthrough, const char *line, size_t length, float width, qboolean isContinuation)
1579 {
1580         con_text_info_t *ti = (con_text_info_t *) passthrough;
1581
1582         if(ti->y < ti->ymin - 0.001)
1583                 (void) 0;
1584         else if(ti->y > ti->ymax - ti->fontsize + 0.001)
1585                 (void) 0;
1586         else
1587         {
1588                 int x = (int) (ti->x + (ti->width - width) * ti->alignment);
1589                 if(isContinuation && *ti->continuationString)
1590                         x = (int) DrawQ_String(x, ti->y, ti->continuationString, strlen(ti->continuationString), ti->fontsize, ti->fontsize, 1.0, 1.0, 1.0, 1.0, 0, NULL, false, ti->font);
1591                 if(length > 0)
1592                         DrawQ_String(x, ti->y, line, length, ti->fontsize, ti->fontsize, 1.0, 1.0, 1.0, 1.0, 0, &(ti->colorindex), false, ti->font);
1593         }
1594
1595         ti->y += ti->fontsize;
1596         return 1;
1597 }
1598
1599 int Con_DrawNotifyRect(int mask_must, int mask_mustnot, float maxage, float x, float y, float width, float height, float fontsize, float alignment_x, float alignment_y, const char *continuationString)
1600 {
1601         int i;
1602         int lines = 0;
1603         int maxlines = (int) floor(height / fontsize + 0.01f);
1604         int startidx;
1605         int nskip = 0;
1606         int continuationWidth = 0;
1607         size_t l;
1608         double t = cl.time; // saved so it won't change
1609         con_text_info_t ti;
1610
1611         ti.font = (mask_must & CON_MASK_CHAT) ? FONT_CHAT : FONT_NOTIFY;
1612         ti.fontsize = fontsize;
1613         ti.alignment = alignment_x;
1614         ti.width = width;
1615         ti.ymin = y;
1616         ti.ymax = y + height;
1617         ti.continuationString = continuationString;
1618
1619         l = 0;
1620         Con_WordWidthFunc(&ti, NULL, &l, -1);
1621         l = strlen(continuationString);
1622         continuationWidth = (int) Con_WordWidthFunc(&ti, continuationString, &l, -1);
1623
1624         // first find the first line to draw by backwards iterating and word wrapping to find their length...
1625         startidx = CON_LINES_COUNT;
1626         for(i = CON_LINES_COUNT - 1; i >= 0; --i)
1627         {
1628                 con_lineinfo_t *l = &CON_LINES(i);
1629                 int mylines;
1630
1631                 if((l->mask & mask_must) != mask_must)
1632                         continue;
1633                 if(l->mask & mask_mustnot)
1634                         continue;
1635                 if(maxage && (l->addtime < t - maxage))
1636                         continue;
1637
1638                 // WE FOUND ONE!
1639                 // Calculate its actual height...
1640                 mylines = COM_Wordwrap(l->start, l->len, continuationWidth, width, Con_WordWidthFunc, &ti, Con_CountLineFunc, &ti);
1641                 if(lines + mylines >= maxlines)
1642                 {
1643                         nskip = lines + mylines - maxlines;
1644                         lines = maxlines;
1645                         startidx = i;
1646                         break;
1647                 }
1648                 lines += mylines;
1649                 startidx = i;
1650         }
1651
1652         // then center according to the calculated amount of lines...
1653         ti.x = x;
1654         ti.y = y + alignment_y * (height - lines * fontsize) - nskip * fontsize;
1655
1656         // then actually draw
1657         for(i = startidx; i < CON_LINES_COUNT; ++i)
1658         {
1659                 con_lineinfo_t *l = &CON_LINES(i);
1660
1661                 if((l->mask & mask_must) != mask_must)
1662                         continue;
1663                 if(l->mask & mask_mustnot)
1664                         continue;
1665                 if(maxage && (l->addtime < t - maxage))
1666                         continue;
1667
1668                 COM_Wordwrap(l->start, l->len, continuationWidth, width, Con_WordWidthFunc, &ti, Con_DisplayLineFunc, &ti);
1669         }
1670
1671         return lines;
1672 }
1673
1674 /*
1675 ================
1676 Con_DrawNotify
1677
1678 Draws the last few lines of output transparently over the game top
1679 ================
1680 */
1681 void Con_DrawNotify (void)
1682 {
1683         float   x, v, xr;
1684         float chatstart, notifystart, inputsize, height;
1685         float align;
1686         char    temptext[MAX_INPUTLINE];
1687         int numChatlines;
1688         int chatpos;
1689
1690         if (con_mutex) Thread_LockMutex(con_mutex);
1691         ConBuffer_FixTimes(&con);
1692
1693         numChatlines = con_chat.integer;
1694
1695         chatpos = con_chatpos.integer;
1696
1697         if (con_notify.integer < 0)
1698                 Cvar_SetValueQuick(&con_notify, 0);
1699         if (gamemode == GAME_TRANSFUSION)
1700                 v = 8; // vertical offset
1701         else
1702                 v = 0;
1703
1704         // GAME_NEXUIZ: center, otherwise left justify
1705         align = con_notifyalign.value;
1706         if(!*con_notifyalign.string) // empty string, evaluated to 0 above
1707         {
1708                 if(gamemode == GAME_NEXUIZ)
1709                         align = 0.5;
1710         }
1711
1712         if(numChatlines || !con_chatrect.integer)
1713         {
1714                 if(chatpos == 0)
1715                 {
1716                         // first chat, input line, then notify
1717                         chatstart = v;
1718                         notifystart = v + (numChatlines + 1) * con_chatsize.value;
1719                 }
1720                 else if(chatpos > 0)
1721                 {
1722                         // first notify, then (chatpos-1) empty lines, then chat, then input
1723                         notifystart = v;
1724                         chatstart = v + (con_notify.value + (chatpos - 1)) * con_notifysize.value;
1725                 }
1726                 else // if(chatpos < 0)
1727                 {
1728                         // first notify, then much space, then chat, then input, then -chatpos-1 empty lines
1729                         notifystart = v;
1730                         chatstart = vid_conheight.value - (-chatpos-1 + numChatlines + 1) * con_chatsize.value;
1731                 }
1732         }
1733         else
1734         {
1735                 // just notify and input
1736                 notifystart = v;
1737                 chatstart = 0; // shut off gcc warning
1738         }
1739
1740         v = notifystart + con_notifysize.value * Con_DrawNotifyRect(0, CON_MASK_INPUT | CON_MASK_HIDENOTIFY | (numChatlines ? CON_MASK_CHAT : 0) | CON_MASK_DEVELOPER, con_notifytime.value, 0, notifystart, vid_conwidth.value, con_notify.value * con_notifysize.value, con_notifysize.value, align, 0.0, "");
1741
1742         if(con_chatrect.integer)
1743         {
1744                 x = con_chatrect_x.value * vid_conwidth.value;
1745                 v = con_chatrect_y.value * vid_conheight.value;
1746         }
1747         else
1748         {
1749                 x = 0;
1750                 if(numChatlines) // only do this if chat area is enabled, or this would move the input line wrong
1751                         v = chatstart;
1752         }
1753         height = numChatlines * con_chatsize.value;
1754
1755         if(numChatlines)
1756         {
1757                 Con_DrawNotifyRect(CON_MASK_CHAT, CON_MASK_INPUT, con_chattime.value, x, v, vid_conwidth.value * con_chatwidth.value, height, con_chatsize.value, 0.0, 1.0, (utf8_enable.integer ? "^3\xee\x80\x8c\xee\x80\x8c\xee\x80\x8c " : "^3\014\014\014 ")); // 015 is Â·> character in conchars.tga
1758                 v += height;
1759         }
1760         if (key_dest == key_message)
1761         {
1762                 //static char *cursor[2] = { "\xee\x80\x8a", "\xee\x80\x8b" }; // { off, on }
1763                 int colorindex = -1;
1764                 const char *cursor;
1765                 cursor = u8_encodech(0xE00A + ((int)(realtime * con_cursorspeed)&1), NULL);
1766
1767                 // LordHavoc: speedup, and other improvements
1768                 if (chat_mode < 0)
1769                         dpsnprintf(temptext, sizeof(temptext), "]%s%s", chat_buffer, cursor);
1770                 else if(chat_mode)
1771                         dpsnprintf(temptext, sizeof(temptext), "say_team:%s%s", chat_buffer, cursor);
1772                 else
1773                         dpsnprintf(temptext, sizeof(temptext), "say:%s%s", chat_buffer, cursor);
1774
1775                 // FIXME word wrap
1776                 inputsize = (numChatlines ? con_chatsize : con_notifysize).value;
1777                 xr = vid_conwidth.value - DrawQ_TextWidth(temptext, 0, inputsize, inputsize, false, FONT_CHAT);
1778                 x = min(xr, x);
1779                 DrawQ_String(x, v, temptext, 0, inputsize, inputsize, 1.0, 1.0, 1.0, 1.0, 0, &colorindex, false, FONT_CHAT);
1780         }
1781         if (con_mutex) Thread_UnlockMutex(con_mutex);
1782 }
1783
1784 /*
1785 ================
1786 Con_LineHeight
1787
1788 Returns the height of a given console line; calculates it if necessary.
1789 ================
1790 */
1791 int Con_LineHeight(int lineno)
1792 {
1793         con_lineinfo_t *li = &CON_LINES(lineno);
1794         if(li->height == -1)
1795         {
1796                 float width = vid_conwidth.value;
1797                 con_text_info_t ti;
1798                 con_lineinfo_t *li = &CON_LINES(lineno);
1799                 ti.fontsize = con_textsize.value;
1800                 ti.font = FONT_CONSOLE;
1801                 li->height = COM_Wordwrap(li->start, li->len, 0, width, Con_WordWidthFunc, &ti, Con_CountLineFunc, NULL);
1802         }
1803         return li->height;
1804 }
1805
1806 /*
1807 ================
1808 Con_DrawConsoleLine
1809
1810 Draws a line of the console; returns its height in lines.
1811 If alpha is 0, the line is not drawn, but still wrapped and its height
1812 returned.
1813 ================
1814 */
1815 int Con_DrawConsoleLine(int mask_must, int mask_mustnot, float y, int lineno, float ymin, float ymax)
1816 {
1817         float width = vid_conwidth.value;
1818         con_text_info_t ti;
1819         con_lineinfo_t *li = &CON_LINES(lineno);
1820
1821         if((li->mask & mask_must) != mask_must)
1822                 return 0;
1823         if((li->mask & mask_mustnot) != 0)
1824                 return 0;
1825
1826         ti.continuationString = "";
1827         ti.alignment = 0;
1828         ti.fontsize = con_textsize.value;
1829         ti.font = FONT_CONSOLE;
1830         ti.x = 0;
1831         ti.y = y - (Con_LineHeight(lineno) - 1) * ti.fontsize;
1832         ti.ymin = ymin;
1833         ti.ymax = ymax;
1834         ti.width = width;
1835
1836         return COM_Wordwrap(li->start, li->len, 0, width, Con_WordWidthFunc, &ti, Con_DisplayLineFunc, &ti);
1837 }
1838
1839 /*
1840 ================
1841 Con_LastVisibleLine
1842
1843 Calculates the last visible line index and how much to show of it based on
1844 con_backscroll.
1845 ================
1846 */
1847 static void Con_LastVisibleLine(int mask_must, int mask_mustnot, int *last, int *limitlast)
1848 {
1849         int lines_seen = 0;
1850         int i;
1851
1852         if(con_backscroll < 0)
1853                 con_backscroll = 0;
1854
1855         *last = 0;
1856
1857         // now count until we saw con_backscroll actual lines
1858         for(i = CON_LINES_COUNT - 1; i >= 0; --i)
1859         if((CON_LINES(i).mask & mask_must) == mask_must)
1860         if((CON_LINES(i).mask & mask_mustnot) == 0)
1861         {
1862                 int h = Con_LineHeight(i);
1863
1864                 // line is the last visible line?
1865                 *last = i;
1866                 if(lines_seen + h > con_backscroll && lines_seen <= con_backscroll)
1867                 {
1868                         *limitlast = lines_seen + h - con_backscroll;
1869                         return;
1870                 }
1871
1872                 lines_seen += h;
1873         }
1874
1875         // if we get here, no line was on screen - scroll so that one line is
1876         // visible then.
1877         con_backscroll = lines_seen - 1;
1878         *limitlast = 1;
1879 }
1880
1881 /*
1882 ================
1883 Con_DrawConsole
1884
1885 Draws the console with the solid background
1886 The typing input line at the bottom should only be drawn if typing is allowed
1887 ================
1888 */
1889 void Con_DrawConsole (int lines)
1890 {
1891         float alpha, alpha0;
1892         double sx, sy;
1893         int mask_must = 0;
1894         int mask_mustnot = (developer.integer>0) ? 0 : CON_MASK_DEVELOPER;
1895         cachepic_t *conbackpic;
1896
1897         if (lines <= 0)
1898                 return;
1899
1900         if (con_mutex) Thread_LockMutex(con_mutex);
1901
1902         if (con_backscroll < 0)
1903                 con_backscroll = 0;
1904
1905         con_vislines = lines;
1906
1907         r_draw2d_force = true;
1908
1909 // draw the background
1910         alpha0 = cls.signon == SIGNONS ? scr_conalpha.value : 1.0f; // always full alpha when not in game
1911         if((alpha = alpha0 * scr_conalphafactor.value) > 0)
1912         {
1913                 sx = scr_conscroll_x.value;
1914                 sy = scr_conscroll_y.value;
1915                 conbackpic = scr_conbrightness.value >= 0.01f ? Draw_CachePic_Flags("gfx/conback", (sx != 0 || sy != 0) ? CACHEPICFLAG_NOCLAMP : 0) : NULL;
1916                 sx *= realtime; sy *= realtime;
1917                 sx -= floor(sx); sy -= floor(sy);
1918                 if (conbackpic && conbackpic->tex != r_texture_notexture)
1919                         DrawQ_SuperPic(0, lines - vid_conheight.integer, conbackpic, vid_conwidth.integer, vid_conheight.integer,
1920                                         0 + sx, 0 + sy, scr_conbrightness.value, scr_conbrightness.value, scr_conbrightness.value, alpha,
1921                                         1 + sx, 0 + sy, scr_conbrightness.value, scr_conbrightness.value, scr_conbrightness.value, alpha,
1922                                         0 + sx, 1 + sy, scr_conbrightness.value, scr_conbrightness.value, scr_conbrightness.value, alpha,
1923                                         1 + sx, 1 + sy, scr_conbrightness.value, scr_conbrightness.value, scr_conbrightness.value, alpha,
1924                                         0);
1925                 else
1926                         DrawQ_Fill(0, lines - vid_conheight.integer, vid_conwidth.integer, vid_conheight.integer, 0.0f, 0.0f, 0.0f, alpha, 0);
1927         }
1928         if((alpha = alpha0 * scr_conalpha2factor.value) > 0)
1929         {
1930                 sx = scr_conscroll2_x.value;
1931                 sy = scr_conscroll2_y.value;
1932                 conbackpic = Draw_CachePic_Flags("gfx/conback2", (sx != 0 || sy != 0) ? CACHEPICFLAG_NOCLAMP : 0);
1933                 sx *= realtime; sy *= realtime;
1934                 sx -= floor(sx); sy -= floor(sy);
1935                 if(conbackpic && conbackpic->tex != r_texture_notexture)
1936                         DrawQ_SuperPic(0, lines - vid_conheight.integer, conbackpic, vid_conwidth.integer, vid_conheight.integer,
1937                                         0 + sx, 0 + sy, scr_conbrightness.value, scr_conbrightness.value, scr_conbrightness.value, alpha,
1938                                         1 + sx, 0 + sy, scr_conbrightness.value, scr_conbrightness.value, scr_conbrightness.value, alpha,
1939                                         0 + sx, 1 + sy, scr_conbrightness.value, scr_conbrightness.value, scr_conbrightness.value, alpha,
1940                                         1 + sx, 1 + sy, scr_conbrightness.value, scr_conbrightness.value, scr_conbrightness.value, alpha,
1941                                         0);
1942         }
1943         if((alpha = alpha0 * scr_conalpha3factor.value) > 0)
1944         {
1945                 sx = scr_conscroll3_x.value;
1946                 sy = scr_conscroll3_y.value;
1947                 conbackpic = Draw_CachePic_Flags("gfx/conback3", (sx != 0 || sy != 0) ? CACHEPICFLAG_NOCLAMP : 0);
1948                 sx *= realtime; sy *= realtime;
1949                 sx -= floor(sx); sy -= floor(sy);
1950                 if(conbackpic && conbackpic->tex != r_texture_notexture)
1951                         DrawQ_SuperPic(0, lines - vid_conheight.integer, conbackpic, vid_conwidth.integer, vid_conheight.integer,
1952                                         0 + sx, 0 + sy, scr_conbrightness.value, scr_conbrightness.value, scr_conbrightness.value, alpha,
1953                                         1 + sx, 0 + sy, scr_conbrightness.value, scr_conbrightness.value, scr_conbrightness.value, alpha,
1954                                         0 + sx, 1 + sy, scr_conbrightness.value, scr_conbrightness.value, scr_conbrightness.value, alpha,
1955                                         1 + sx, 1 + sy, scr_conbrightness.value, scr_conbrightness.value, scr_conbrightness.value, alpha,
1956                                         0);
1957         }
1958         DrawQ_String(vid_conwidth.integer - DrawQ_TextWidth(engineversion, 0, con_textsize.value, con_textsize.value, false, FONT_CONSOLE), lines - con_textsize.value, engineversion, 0, con_textsize.value, con_textsize.value, 1, 0, 0, 1, 0, NULL, true, FONT_CONSOLE);
1959
1960 // draw the text
1961 #if 0
1962         {
1963                 int i;
1964                 int count = CON_LINES_COUNT;
1965                 float ymax = con_vislines - 2 * con_textsize.value;
1966                 float y = ymax + con_textsize.value * con_backscroll;
1967                 for (i = 0;i < count && y >= 0;i++)
1968                         y -= Con_DrawConsoleLine(mask_must, mask_mustnot, y - con_textsize.value, CON_LINES_COUNT - 1 - i, 0, ymax) * con_textsize.value;
1969                 // fix any excessive scrollback for the next frame
1970                 if (i >= count && y >= 0)
1971                 {
1972                         con_backscroll -= (int)(y / con_textsize.value);
1973                         if (con_backscroll < 0)
1974                                 con_backscroll = 0;
1975                 }
1976         }
1977 #else
1978         if(CON_LINES_COUNT > 0)
1979         {
1980                 int i, last, limitlast;
1981                 float y;
1982                 float ymax = con_vislines - 2 * con_textsize.value;
1983                 Con_LastVisibleLine(mask_must, mask_mustnot, &last, &limitlast);
1984                 //Con_LastVisibleLine(mask_must, mask_mustnot, &last, &limitlast);
1985                 y = ymax - con_textsize.value;
1986
1987                 if(limitlast)
1988                         y += (CON_LINES(last).height - limitlast) * con_textsize.value;
1989                 i = last;
1990
1991                 for(;;)
1992                 {
1993                         y -= Con_DrawConsoleLine(mask_must, mask_mustnot, y, i, 0, ymax) * con_textsize.value;
1994                         if(i == 0)
1995                                 break; // top of console buffer
1996                         if(y < 0)
1997                                 break; // top of console window
1998                         limitlast = 0;
1999                         --i;
2000                 }
2001         }
2002 #endif
2003
2004 // draw the input prompt, user text, and cursor if desired
2005         Con_DrawInput ();
2006
2007         r_draw2d_force = false;
2008         if (con_mutex) Thread_UnlockMutex(con_mutex);
2009 }
2010
2011 /*
2012 GetMapList
2013
2014 Made by [515]
2015 Prints not only map filename, but also
2016 its format (q1/q2/q3/hl) and even its message
2017 */
2018 //[515]: here is an ugly hack.. two gotos... oh my... *but it works*
2019 //LordHavoc: rewrote bsp type detection, rewrote message extraction to do proper worldspawn parsing
2020 //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
2021 //LordHavoc: FIXME: man this GetMapList is STILL ugly code even after my cleanups...
2022 qboolean GetMapList (const char *s, char *completedname, int completednamebufferlength)
2023 {
2024         fssearch_t      *t;
2025         char            message[1024];
2026         int                     i, k, max, p, o, min;
2027         unsigned char *len;
2028         qfile_t         *f;
2029         unsigned char buf[1024];
2030
2031         dpsnprintf(message, sizeof(message), "maps/%s*.bsp", s);
2032         t = FS_Search(message, 1, true);
2033         if(!t)
2034                 return false;
2035         if (t->numfilenames > 1)
2036                 Con_Printf("^1 %i maps found :\n", t->numfilenames);
2037         len = (unsigned char *)Z_Malloc(t->numfilenames);
2038         min = 666;
2039         for(max=i=0;i<t->numfilenames;i++)
2040         {
2041                 k = (int)strlen(t->filenames[i]);
2042                 k -= 9;
2043                 if(max < k)
2044                         max = k;
2045                 else
2046                 if(min > k)
2047                         min = k;
2048                 len[i] = k;
2049         }
2050         o = (int)strlen(s);
2051         for(i=0;i<t->numfilenames;i++)
2052         {
2053                 int lumpofs = 0, lumplen = 0;
2054                 char *entities = NULL;
2055                 const char *data = NULL;
2056                 char keyname[64];
2057                 char entfilename[MAX_QPATH];
2058                 strlcpy(message, "^1**ERROR**^7", sizeof(message));
2059                 p = 0;
2060                 f = FS_OpenVirtualFile(t->filenames[i], true);
2061                 if(f)
2062                 {
2063                         memset(buf, 0, 1024);
2064                         FS_Read(f, buf, 1024);
2065                         if (!memcmp(buf, "IBSP", 4))
2066                         {
2067                                 p = LittleLong(((int *)buf)[1]);
2068                                 if (p == Q3BSPVERSION)
2069                                 {
2070                                         q3dheader_t *header = (q3dheader_t *)buf;
2071                                         lumpofs = LittleLong(header->lumps[Q3LUMP_ENTITIES].fileofs);
2072                                         lumplen = LittleLong(header->lumps[Q3LUMP_ENTITIES].filelen);
2073                                 }
2074                                 else if (p == Q2BSPVERSION)
2075                                 {
2076                                         q2dheader_t *header = (q2dheader_t *)buf;
2077                                         lumpofs = LittleLong(header->lumps[Q2LUMP_ENTITIES].fileofs);
2078                                         lumplen = LittleLong(header->lumps[Q2LUMP_ENTITIES].filelen);
2079                                 }
2080                         }
2081                         else if((p = BuffLittleLong(buf)) == BSPVERSION || p == 30)
2082                         {
2083                                 dheader_t *header = (dheader_t *)buf;
2084                                 lumpofs = LittleLong(header->lumps[LUMP_ENTITIES].fileofs);
2085                                 lumplen = LittleLong(header->lumps[LUMP_ENTITIES].filelen);
2086                         }
2087                         else
2088                                 p = 0;
2089                         strlcpy(entfilename, t->filenames[i], sizeof(entfilename));
2090                         memcpy(entfilename + strlen(entfilename) - 4, ".ent", 5);
2091                         entities = (char *)FS_LoadFile(entfilename, tempmempool, true, NULL);
2092                         if (!entities && lumplen >= 10)
2093                         {
2094                                 FS_Seek(f, lumpofs, SEEK_SET);
2095                                 entities = (char *)Z_Malloc(lumplen + 1);
2096                                 FS_Read(f, entities, lumplen);
2097                         }
2098                         if (entities)
2099                         {
2100                                 // if there are entities to parse, a missing message key just
2101                                 // means there is no title, so clear the message string now
2102                                 message[0] = 0;
2103                                 data = entities;
2104                                 for (;;)
2105                                 {
2106                                         int l;
2107                                         if (!COM_ParseToken_Simple(&data, false, false))
2108                                                 break;
2109                                         if (com_token[0] == '{')
2110                                                 continue;
2111                                         if (com_token[0] == '}')
2112                                                 break;
2113                                         // skip leading whitespace
2114                                         for (k = 0;com_token[k] && ISWHITESPACE(com_token[k]);k++);
2115                                         for (l = 0;l < (int)sizeof(keyname) - 1 && com_token[k+l] && !ISWHITESPACE(com_token[k+l]);l++)
2116                                                 keyname[l] = com_token[k+l];
2117                                         keyname[l] = 0;
2118                                         if (!COM_ParseToken_Simple(&data, false, false))
2119                                                 break;
2120                                         if (developer_extra.integer)
2121                                                 Con_DPrintf("key: %s %s\n", keyname, com_token);
2122                                         if (!strcmp(keyname, "message"))
2123                                         {
2124                                                 // get the message contents
2125                                                 strlcpy(message, com_token, sizeof(message));
2126                                                 break;
2127                                         }
2128                                 }
2129                         }
2130                 }
2131                 if (entities)
2132                         Z_Free(entities);
2133                 if(f)
2134                         FS_Close(f);
2135                 *(t->filenames[i]+len[i]+5) = 0;
2136                 switch(p)
2137                 {
2138                 case Q3BSPVERSION:      strlcpy((char *)buf, "Q3", sizeof(buf));break;
2139                 case Q2BSPVERSION:      strlcpy((char *)buf, "Q2", sizeof(buf));break;
2140                 case BSPVERSION:        strlcpy((char *)buf, "Q1", sizeof(buf));break;
2141                 case 30:                        strlcpy((char *)buf, "HL", sizeof(buf));break;
2142                 default:                        strlcpy((char *)buf, "??", sizeof(buf));break;
2143                 }
2144                 Con_Printf("%16s (%s) %s\n", t->filenames[i]+5, buf, message);
2145         }
2146         Con_Print("\n");
2147         for(p=o;p<min;p++)
2148         {
2149                 k = *(t->filenames[0]+5+p);
2150                 if(k == 0)
2151                         goto endcomplete;
2152                 for(i=1;i<t->numfilenames;i++)
2153                         if(*(t->filenames[i]+5+p) != k)
2154                                 goto endcomplete;
2155         }
2156 endcomplete:
2157         if(p > o && completedname && completednamebufferlength > 0)
2158         {
2159                 memset(completedname, 0, completednamebufferlength);
2160                 memcpy(completedname, (t->filenames[0]+5), min(p, completednamebufferlength - 1));
2161         }
2162         Z_Free(len);
2163         FS_FreeSearch(t);
2164         return p > o;
2165 }
2166
2167 /*
2168         Con_DisplayList
2169
2170         New function for tab-completion system
2171         Added by EvilTypeGuy
2172         MEGA Thanks to Taniwha
2173
2174 */
2175 void Con_DisplayList(const char **list)
2176 {
2177         int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
2178         const char **walk = list;
2179
2180         while (*walk) {
2181                 len = (int)strlen(*walk);
2182                 if (len > maxlen)
2183                         maxlen = len;
2184                 walk++;
2185         }
2186         maxlen += 1;
2187
2188         while (*list) {
2189                 len = (int)strlen(*list);
2190                 if (pos + maxlen >= width) {
2191                         Con_Print("\n");
2192                         pos = 0;
2193                 }
2194
2195                 Con_Print(*list);
2196                 for (i = 0; i < (maxlen - len); i++)
2197                         Con_Print(" ");
2198
2199                 pos += maxlen;
2200                 list++;
2201         }
2202
2203         if (pos)
2204                 Con_Print("\n\n");
2205 }
2206
2207 /*
2208         SanitizeString strips color tags from the string in
2209         and writes the result on string out
2210 */
2211 void SanitizeString(char *in, char *out)
2212 {
2213         while(*in)
2214         {
2215                 if(*in == STRING_COLOR_TAG)
2216                 {
2217                         ++in;
2218                         if(!*in)
2219                         {
2220                                 out[0] = STRING_COLOR_TAG;
2221                                 out[1] = 0;
2222                                 return;
2223                         }
2224                         else if (*in >= '0' && *in <= '9') // ^[0-9] found
2225                         {
2226                                 ++in;
2227                                 if(!*in)
2228                                 {
2229                                         *out = 0;
2230                                         return;
2231                                 } else if (*in == STRING_COLOR_TAG) // ^[0-9]^ found, don't print ^[0-9]
2232                                         continue;
2233                         }
2234                         else if (*in == STRING_COLOR_RGB_TAG_CHAR) // ^x found
2235                         {
2236                                 if ( isxdigit(in[1]) && isxdigit(in[2]) && isxdigit(in[3]) )
2237                                 {
2238                                         in+=4;
2239                                         if (!*in)
2240                                         {
2241                                                 *out = 0;
2242                                                 return;
2243                                         } else if (*in == STRING_COLOR_TAG) // ^xrgb^ found, don't print ^xrgb
2244                                                 continue;
2245                                 }
2246                                 else in--;
2247                         }
2248                         else if (*in != STRING_COLOR_TAG)
2249                                 --in;
2250                 }
2251                 *out = qfont_table[*(unsigned char*)in];
2252                 ++in;
2253                 ++out;
2254         }
2255         *out = 0;
2256 }
2257
2258 // Now it becomes TRICKY :D --blub
2259 static char Nicks_list[MAX_SCOREBOARD][MAX_SCOREBOARDNAME];     // contains the nicks with colors and all that
2260 static char Nicks_sanlist[MAX_SCOREBOARD][MAX_SCOREBOARDNAME];  // sanitized list for completion when there are other possible matches.
2261 // means: when somebody uses a cvar's name as his name, we won't ever get his colors in there...
2262 static int Nicks_offset[MAX_SCOREBOARD]; // when nicks use a space, we need this to move the completion list string starts to avoid invalid memcpys
2263 static int Nicks_matchpos;
2264
2265 // co against <<:BLASTER:>> is true!?
2266 int Nicks_strncasecmp_nospaces(char *a, char *b, unsigned int a_len)
2267 {
2268         while(a_len)
2269         {
2270                 if(tolower(*a) == tolower(*b))
2271                 {
2272                         if(*a == 0)
2273                                 return 0;
2274                         --a_len;
2275                         ++a;
2276                         ++b;
2277                         continue;
2278                 }
2279                 if(!*a)
2280                         return -1;
2281                 if(!*b)
2282                         return 1;
2283                 if(*a == ' ')
2284                         return (*a < *b) ? -1 : 1;
2285                 if(*b == ' ')
2286                         ++b;
2287                 else
2288                         return (*a < *b) ? -1 : 1;
2289         }
2290         return 0;
2291 }
2292 int Nicks_strncasecmp(char *a, char *b, unsigned int a_len)
2293 {
2294         char space_char;
2295         if(!(con_nickcompletion_flags.integer & NICKS_ALPHANUMERICS_ONLY))
2296         {
2297                 if(con_nickcompletion_flags.integer & NICKS_NO_SPACES)
2298                         return Nicks_strncasecmp_nospaces(a, b, a_len);
2299                 return strncasecmp(a, b, a_len);
2300         }
2301
2302         space_char = (con_nickcompletion_flags.integer & NICKS_NO_SPACES) ? 'a' : ' ';
2303
2304         // ignore non alphanumerics of B
2305         // if A contains a non-alphanumeric, B must contain it as well though!
2306         while(a_len)
2307         {
2308                 qboolean alnum_a, alnum_b;
2309
2310                 if(tolower(*a) == tolower(*b))
2311                 {
2312                         if(*a == 0) // end of both strings, they're equal
2313                                 return 0;
2314                         --a_len;
2315                         ++a;
2316                         ++b;
2317                         continue;
2318                 }
2319                 // not equal, end of one string?
2320                 if(!*a)
2321                         return -1;
2322                 if(!*b)
2323                         return 1;
2324                 // ignore non alphanumerics
2325                 alnum_a = ( (*a >= 'a' && *a <= 'z') || (*a >= 'A' && *a <= 'Z') || (*a >= '0' && *a <= '9') || *a == space_char);
2326                 alnum_b = ( (*b >= 'a' && *b <= 'z') || (*b >= 'A' && *b <= 'Z') || (*b >= '0' && *b <= '9') || *b == space_char);
2327                 if(!alnum_a) // b must contain this
2328                         return (*a < *b) ? -1 : 1;
2329                 if(!alnum_b)
2330                         ++b;
2331                 // otherwise, both are alnum, they're just not equal, return the appropriate number
2332                 else
2333                         return (*a < *b) ? -1 : 1;
2334         }
2335         return 0;
2336 }
2337
2338
2339 /* Nicks_CompleteCountPossible
2340
2341    Count the number of possible nicks to complete
2342  */
2343 int Nicks_CompleteCountPossible(char *line, int pos, char *s, qboolean isCon)
2344 {
2345         char name[128];
2346         int i, p;
2347         int match;
2348         int spos;
2349         int count = 0;
2350
2351         if(!con_nickcompletion.integer)
2352                 return 0;
2353
2354         // changed that to 1
2355         if(!line[0])// || !line[1]) // we want at least... 2 written characters
2356                 return 0;
2357
2358         for(i = 0; i < cl.maxclients; ++i)
2359         {
2360                 p = i;
2361                 if(!cl.scores[p].name[0])
2362                         continue;
2363
2364                 SanitizeString(cl.scores[p].name, name);
2365                 //Con_Printf(" ^2Sanitized: ^7%s -> %s", cl.scores[p].name, name);
2366
2367                 if(!name[0])
2368                         continue;
2369
2370                 match = -1;
2371                 spos = pos - 1; // no need for a minimum of characters :)
2372
2373                 while(spos >= 0)
2374                 {
2375                         if(spos > 0 && line[spos-1] != ' ' && line[spos-1] != ';' && line[spos-1] != '\"' && line[spos-1] != '\'')
2376                         {
2377                                 if(!(isCon && line[spos-1] == ']' && spos == 1) && // console start
2378                                    !(spos > 1 && line[spos-1] >= '0' && line[spos-1] <= '9' && line[spos-2] == STRING_COLOR_TAG)) // color start
2379                                 {
2380                                         --spos;
2381                                         continue;
2382                                 }
2383                         }
2384                         if(isCon && spos == 0)
2385                                 break;
2386                         if(Nicks_strncasecmp(line+spos, name, pos-spos) == 0)
2387                                 match = spos;
2388                         --spos;
2389                 }
2390                 if(match < 0)
2391                         continue;
2392                 //Con_Printf("Possible match: %s|%s\n", cl.scores[p].name, name);
2393                 strlcpy(Nicks_list[count], cl.scores[p].name, sizeof(Nicks_list[count]));
2394
2395                 // the sanitized list
2396                 strlcpy(Nicks_sanlist[count], name, sizeof(Nicks_sanlist[count]));
2397                 if(!count)
2398                 {
2399                         Nicks_matchpos = match;
2400                 }
2401
2402                 Nicks_offset[count] = s - (&line[match]);
2403                 //Con_Printf("offset for %s: %i\n", name, Nicks_offset[count]);
2404
2405                 ++count;
2406         }
2407         return count;
2408 }
2409
2410 void Cmd_CompleteNicksPrint(int count)
2411 {
2412         int i;
2413         for(i = 0; i < count; ++i)
2414                 Con_Printf("%s\n", Nicks_list[i]);
2415 }
2416
2417 void Nicks_CutMatchesNormal(int count)
2418 {
2419         // cut match 0 down to the longest possible completion
2420         int i;
2421         unsigned int c, l;
2422         c = strlen(Nicks_sanlist[0]) - 1;
2423         for(i = 1; i < count; ++i)
2424         {
2425                 l = strlen(Nicks_sanlist[i]) - 1;
2426                 if(l < c)
2427                         c = l;
2428
2429                 for(l = 0; l <= c; ++l)
2430                         if(tolower(Nicks_sanlist[0][l]) != tolower(Nicks_sanlist[i][l]))
2431                         {
2432                                 c = l-1;
2433                                 break;
2434                         }
2435         }
2436         Nicks_sanlist[0][c+1] = 0;
2437         //Con_Printf("List0: %s\n", Nicks_sanlist[0]);
2438 }
2439
2440 unsigned int Nicks_strcleanlen(const char *s)
2441 {
2442         unsigned int l = 0;
2443         while(*s)
2444         {
2445                 if( (*s >= 'a' && *s <= 'z') ||
2446                     (*s >= 'A' && *s <= 'Z') ||
2447                     (*s >= '0' && *s <= '9') ||
2448                     *s == ' ')
2449                         ++l;
2450                 ++s;
2451         }
2452         return l;
2453 }
2454
2455 void Nicks_CutMatchesAlphaNumeric(int count)
2456 {
2457         // cut match 0 down to the longest possible completion
2458         int i;
2459         unsigned int c, l;
2460         char tempstr[sizeof(Nicks_sanlist[0])];
2461         char *a, *b;
2462         char space_char = (con_nickcompletion_flags.integer & NICKS_NO_SPACES) ? 'a' : ' '; // yes this is correct, we want NO spaces when no spaces
2463
2464         c = strlen(Nicks_sanlist[0]);
2465         for(i = 0, l = 0; i < (int)c; ++i)
2466         {
2467                 if( (Nicks_sanlist[0][i] >= 'a' && Nicks_sanlist[0][i] <= 'z') ||
2468                     (Nicks_sanlist[0][i] >= 'A' && Nicks_sanlist[0][i] <= 'Z') ||
2469                     (Nicks_sanlist[0][i] >= '0' && Nicks_sanlist[0][i] <= '9') || Nicks_sanlist[0][i] == space_char) // this is what's COPIED
2470                 {
2471                         tempstr[l++] = Nicks_sanlist[0][i];
2472                 }
2473         }
2474         tempstr[l] = 0;
2475
2476         for(i = 1; i < count; ++i)
2477         {
2478                 a = tempstr;
2479                 b = Nicks_sanlist[i];
2480                 while(1)
2481                 {
2482                         if(!*a)
2483                                 break;
2484                         if(!*b)
2485                         {
2486                                 *a = 0;
2487                                 break;
2488                         }
2489                         if(tolower(*a) == tolower(*b))
2490                         {
2491                                 ++a;
2492                                 ++b;
2493                                 continue;
2494                         }
2495                         if( (*b >= 'a' && *b <= 'z') || (*b >= 'A' && *b <= 'Z') || (*b >= '0' && *b <= '9') || *b == space_char)
2496                         {
2497                                 // b is alnum, so cut
2498                                 *a = 0;
2499                                 break;
2500                         }
2501                         ++b;
2502                 }
2503         }
2504         // Just so you know, if cutmatchesnormal doesn't kill the first entry, then even the non-alnums fit
2505         Nicks_CutMatchesNormal(count);
2506         //if(!Nicks_sanlist[0][0])
2507         if(Nicks_strcleanlen(Nicks_sanlist[0]) < strlen(tempstr))
2508         {
2509                 // if the clean sanitized one is longer than the current one, use it, it has crap chars which definitely are in there
2510                 strlcpy(Nicks_sanlist[0], tempstr, sizeof(tempstr));
2511         }
2512 }
2513
2514 void Nicks_CutMatchesNoSpaces(int count)
2515 {
2516         // cut match 0 down to the longest possible completion
2517         int i;
2518         unsigned int c, l;
2519         char tempstr[sizeof(Nicks_sanlist[0])];
2520         char *a, *b;
2521
2522         c = strlen(Nicks_sanlist[0]);
2523         for(i = 0, l = 0; i < (int)c; ++i)
2524         {
2525                 if(Nicks_sanlist[0][i] != ' ') // here it's what's NOT copied
2526                 {
2527                         tempstr[l++] = Nicks_sanlist[0][i];
2528                 }
2529         }
2530         tempstr[l] = 0;
2531
2532         for(i = 1; i < count; ++i)
2533         {
2534                 a = tempstr;
2535                 b = Nicks_sanlist[i];
2536                 while(1)
2537                 {
2538                         if(!*a)
2539                                 break;
2540                         if(!*b)
2541                         {
2542                                 *a = 0;
2543                                 break;
2544                         }
2545                         if(tolower(*a) == tolower(*b))
2546                         {
2547                                 ++a;
2548                                 ++b;
2549                                 continue;
2550                         }
2551                         if(*b != ' ')
2552                         {
2553                                 *a = 0;
2554                                 break;
2555                         }
2556                         ++b;
2557                 }
2558         }
2559         // Just so you know, if cutmatchesnormal doesn't kill the first entry, then even the non-alnums fit
2560         Nicks_CutMatchesNormal(count);
2561         //if(!Nicks_sanlist[0][0])
2562         //Con_Printf("TS: %s\n", tempstr);
2563         if(Nicks_strcleanlen(Nicks_sanlist[0]) < strlen(tempstr))
2564         {
2565                 // if the clean sanitized one is longer than the current one, use it, it has crap chars which definitely are in there
2566                 strlcpy(Nicks_sanlist[0], tempstr, sizeof(tempstr));
2567         }
2568 }
2569
2570 void Nicks_CutMatches(int count)
2571 {
2572         if(con_nickcompletion_flags.integer & NICKS_ALPHANUMERICS_ONLY)
2573                 Nicks_CutMatchesAlphaNumeric(count);
2574         else if(con_nickcompletion_flags.integer & NICKS_NO_SPACES)
2575                 Nicks_CutMatchesNoSpaces(count);
2576         else
2577                 Nicks_CutMatchesNormal(count);
2578 }
2579
2580 const char **Nicks_CompleteBuildList(int count)
2581 {
2582         const char **buf;
2583         int bpos = 0;
2584         // the list is freed by Con_CompleteCommandLine, so create a char**
2585         buf = (const char **)Mem_Alloc(tempmempool, count * sizeof(const char *) + sizeof (const char *));
2586
2587         for(; bpos < count; ++bpos)
2588                 buf[bpos] = Nicks_sanlist[bpos] + Nicks_offset[bpos];
2589
2590         Nicks_CutMatches(count);
2591
2592         buf[bpos] = NULL;
2593         return buf;
2594 }
2595
2596 /*
2597         Nicks_AddLastColor
2598         Restores the previous used color, after the autocompleted name.
2599 */
2600 int Nicks_AddLastColor(char *buffer, int pos)
2601 {
2602         qboolean quote_added = false;
2603         int match;
2604         int color = STRING_COLOR_DEFAULT + '0';
2605         char r = 0, g = 0, b = 0;
2606
2607         if(con_nickcompletion_flags.integer & NICKS_ADD_QUOTE && buffer[Nicks_matchpos-1] == '\"')
2608         {
2609                 // we'll have to add a quote :)
2610                 buffer[pos++] = '\"';
2611                 quote_added = true;
2612         }
2613
2614         if((!quote_added && con_nickcompletion_flags.integer & NICKS_ADD_COLOR) || con_nickcompletion_flags.integer & NICKS_FORCE_COLOR)
2615         {
2616                 // add color when no quote was added, or when flags &4?
2617                 // find last color
2618                 for(match = Nicks_matchpos-1; match >= 0; --match)
2619                 {
2620                         if(buffer[match] == STRING_COLOR_TAG)
2621                         {
2622                                 if( isdigit(buffer[match+1]) )
2623                                 {
2624                                         color = buffer[match+1];
2625                                         break;
2626                                 }
2627                                 else if(buffer[match+1] == STRING_COLOR_RGB_TAG_CHAR)
2628                                 {
2629                                         if ( isxdigit(buffer[match+2]) && isxdigit(buffer[match+3]) && isxdigit(buffer[match+4]) )
2630                                         {
2631                                                 r = buffer[match+2];
2632                                                 g = buffer[match+3];
2633                                                 b = buffer[match+4];
2634                                                 color = -1;
2635                                                 break;
2636                                         }
2637                                 }
2638                         }
2639                 }
2640                 if(!quote_added)
2641                 {
2642                         if( pos >= 2 && buffer[pos-2] == STRING_COLOR_TAG && isdigit(buffer[pos-1]) ) // when thes use &4
2643                                 pos -= 2;
2644                         else if( pos >= 5 && buffer[pos-5] == STRING_COLOR_TAG && buffer[pos-4] == STRING_COLOR_RGB_TAG_CHAR
2645                                          && isxdigit(buffer[pos-3]) && isxdigit(buffer[pos-2]) && isxdigit(buffer[pos-1]) )
2646                                 pos -= 5;
2647                 }
2648                 buffer[pos++] = STRING_COLOR_TAG;
2649                 if (color == -1)
2650                 {
2651                         buffer[pos++] = STRING_COLOR_RGB_TAG_CHAR;
2652                         buffer[pos++] = r;
2653                         buffer[pos++] = g;
2654                         buffer[pos++] = b;
2655                 }
2656                 else
2657                         buffer[pos++] = color;
2658         }
2659         return pos;
2660 }
2661
2662 int Nicks_CompleteChatLine(char *buffer, size_t size, unsigned int pos)
2663 {
2664         int n;
2665         /*if(!con_nickcompletion.integer)
2666           return; is tested in Nicks_CompletionCountPossible */
2667         n = Nicks_CompleteCountPossible(buffer, pos, &buffer[pos], false);
2668         if(n == 1)
2669         {
2670                 size_t len;
2671                 char *msg;
2672
2673                 msg = Nicks_list[0];
2674                 len = min(size - Nicks_matchpos - 3, strlen(msg));
2675                 memcpy(&buffer[Nicks_matchpos], msg, len);
2676                 if( len < (size - 7) ) // space for color (^[0-9] or ^xrgb) and space and \0
2677                         len = Nicks_AddLastColor(buffer, Nicks_matchpos+len);
2678                 buffer[len++] = ' ';
2679                 buffer[len] = 0;
2680                 return len;
2681         } else if(n > 1)
2682         {
2683                 int len;
2684                 char *msg;
2685                 Con_Printf("\n%i possible nicks:\n", n);
2686                 Cmd_CompleteNicksPrint(n);
2687
2688                 Nicks_CutMatches(n);
2689
2690                 msg = Nicks_sanlist[0];
2691                 len = min(size - Nicks_matchpos, strlen(msg));
2692                 memcpy(&buffer[Nicks_matchpos], msg, len);
2693                 buffer[Nicks_matchpos + len] = 0;
2694                 //pos += len;
2695                 return Nicks_matchpos + len;
2696         }
2697         return pos;
2698 }
2699
2700
2701 /*
2702         Con_CompleteCommandLine
2703
2704         New function for tab-completion system
2705         Added by EvilTypeGuy
2706         Thanks to Fett erich@heintz.com
2707         Thanks to taniwha
2708         Enhanced to tab-complete map names by [515]
2709
2710 */
2711 void Con_CompleteCommandLine (void)
2712 {
2713         const char *cmd = "";
2714         char *s;
2715         const char **list[4] = {0, 0, 0, 0};
2716         char s2[512];
2717         char command[512];
2718         int c, v, a, i, cmd_len, pos, k;
2719         int n; // nicks --blub
2720         const char *space, *patterns;
2721
2722         //find what we want to complete
2723         pos = key_linepos;
2724         while(--pos)
2725         {
2726                 k = key_line[pos];
2727                 if(k == '\"' || k == ';' || k == ' ' || k == '\'')
2728                         break;
2729         }
2730         pos++;
2731
2732         s = key_line + pos;
2733         strlcpy(s2, key_line + key_linepos, sizeof(s2));        //save chars after cursor
2734         key_line[key_linepos] = 0;                                      //hide them
2735
2736         space = strchr(key_line + 1, ' ');
2737         if(space && pos == (space - key_line) + 1)
2738         {
2739                 strlcpy(command, key_line + 1, min(sizeof(command), (unsigned int)(space - key_line)));
2740
2741                 patterns = Cvar_VariableString(va("con_completion_%s", command)); // TODO maybe use a better place for this?
2742                 if(patterns && !*patterns)
2743                         patterns = NULL; // get rid of the empty string
2744
2745                 if(!strcmp(command, "map") || !strcmp(command, "changelevel") || (patterns && !strcmp(patterns, "map")))
2746                 {
2747                         //maps search
2748                         char t[MAX_QPATH];
2749                         if (GetMapList(s, t, sizeof(t)))
2750                         {
2751                                 // first move the cursor
2752                                 key_linepos += (int)strlen(t) - (int)strlen(s);
2753
2754                                 // and now do the actual work
2755                                 *s = 0;
2756                                 strlcat(key_line, t, MAX_INPUTLINE);
2757                                 strlcat(key_line, s2, MAX_INPUTLINE); //add back chars after cursor
2758
2759                                 // and fix the cursor
2760                                 if(key_linepos > (int) strlen(key_line))
2761                                         key_linepos = (int) strlen(key_line);
2762                         }
2763                         return;
2764                 }
2765                 else
2766                 {
2767                         if(patterns)
2768                         {
2769                                 char t[MAX_QPATH];
2770                                 stringlist_t resultbuf, dirbuf;
2771
2772                                 // Usage:
2773                                 //   // store completion patterns (space separated) for command foo in con_completion_foo
2774                                 //   set con_completion_foo "foodata/*.foodefault *.foo"
2775                                 //   foo <TAB>
2776                                 //
2777                                 // Note: patterns with slash are always treated as absolute
2778                                 // patterns; patterns without slash search in the innermost
2779                                 // directory the user specified. There is no way to "complete into"
2780                                 // a directory as of now, as directories seem to be unknown to the
2781                                 // FS subsystem.
2782                                 //
2783                                 // Examples:
2784                                 //   set con_completion_playermodel "models/player/*.zym models/player/*.md3 models/player/*.psk models/player/*.dpm"
2785                                 //   set con_completion_playdemo "*.dem"
2786                                 //   set con_completion_play "*.wav *.ogg"
2787                                 //
2788                                 // TODO somehow add support for directories; these shall complete
2789                                 // to their name + an appended slash.
2790
2791                                 stringlistinit(&resultbuf);
2792                                 stringlistinit(&dirbuf);
2793                                 while(COM_ParseToken_Simple(&patterns, false, false))
2794                                 {
2795                                         fssearch_t *search;
2796                                         if(strchr(com_token, '/'))
2797                                         {
2798                                                 search = FS_Search(com_token, true, true);
2799                                         }
2800                                         else
2801                                         {
2802                                                 const char *slash = strrchr(s, '/');
2803                                                 if(slash)
2804                                                 {
2805                                                         strlcpy(t, s, min(sizeof(t), (unsigned int)(slash - s + 2))); // + 2, because I want to include the slash
2806                                                         strlcat(t, com_token, sizeof(t));
2807                                                         search = FS_Search(t, true, true);
2808                                                 }
2809                                                 else
2810                                                         search = FS_Search(com_token, true, true);
2811                                         }
2812                                         if(search)
2813                                         {
2814                                                 for(i = 0; i < search->numfilenames; ++i)
2815                                                         if(!strncmp(search->filenames[i], s, strlen(s)))
2816                                                                 if(FS_FileType(search->filenames[i]) == FS_FILETYPE_FILE)
2817                                                                         stringlistappend(&resultbuf, search->filenames[i]);
2818                                                 FS_FreeSearch(search);
2819                                         }
2820                                 }
2821
2822                                 // In any case, add directory names
2823                                 {
2824                                         fssearch_t *search;
2825                                         const char *slash = strrchr(s, '/');
2826                                         if(slash)
2827                                         {
2828                                                 strlcpy(t, s, min(sizeof(t), (unsigned int)(slash - s + 2))); // + 2, because I want to include the slash
2829                                                 strlcat(t, "*", sizeof(t));
2830                                                 search = FS_Search(t, true, true);
2831                                         }
2832                                         else
2833                                                 search = FS_Search("*", true, true);
2834                                         if(search)
2835                                         {
2836                                                 for(i = 0; i < search->numfilenames; ++i)
2837                                                         if(!strncmp(search->filenames[i], s, strlen(s)))
2838                                                                 if(FS_FileType(search->filenames[i]) == FS_FILETYPE_DIRECTORY)
2839                                                                         stringlistappend(&dirbuf, search->filenames[i]);
2840                                                 FS_FreeSearch(search);
2841                                         }
2842                                 }
2843
2844                                 if(resultbuf.numstrings > 0 || dirbuf.numstrings > 0)
2845                                 {
2846                                         const char *p, *q;
2847                                         unsigned int matchchars;
2848                                         if(resultbuf.numstrings == 0 && dirbuf.numstrings == 1)
2849                                         {
2850                                                 dpsnprintf(t, sizeof(t), "%s/", dirbuf.strings[0]);
2851                                         }
2852                                         else
2853                                         if(resultbuf.numstrings == 1 && dirbuf.numstrings == 0)
2854                                         {
2855                                                 dpsnprintf(t, sizeof(t), "%s ", resultbuf.strings[0]);
2856                                         }
2857                                         else
2858                                         {
2859                                                 stringlistsort(&resultbuf, true); // dirbuf is already sorted
2860                                                 Con_Printf("\n%i possible filenames\n", resultbuf.numstrings + dirbuf.numstrings);
2861                                                 for(i = 0; i < dirbuf.numstrings; ++i)
2862                                                 {
2863                                                         Con_Printf("^4%s^7/\n", dirbuf.strings[i]);
2864                                                 }
2865                                                 for(i = 0; i < resultbuf.numstrings; ++i)
2866                                                 {
2867                                                         Con_Printf("%s\n", resultbuf.strings[i]);
2868                                                 }
2869                                                 matchchars = sizeof(t) - 1;
2870                                                 if(resultbuf.numstrings > 0)
2871                                                 {
2872                                                         p = resultbuf.strings[0];
2873                                                         q = resultbuf.strings[resultbuf.numstrings - 1];
2874                                                         for(; *p && *p == *q; ++p, ++q);
2875                                                         matchchars = (unsigned int)(p - resultbuf.strings[0]);
2876                                                 }
2877                                                 if(dirbuf.numstrings > 0)
2878                                                 {
2879                                                         p = dirbuf.strings[0];
2880                                                         q = dirbuf.strings[dirbuf.numstrings - 1];
2881                                                         for(; *p && *p == *q; ++p, ++q);
2882                                                         matchchars = min(matchchars, (unsigned int)(p - dirbuf.strings[0]));
2883                                                 }
2884                                                 // now p points to the first non-equal character, or to the end
2885                                                 // of resultbuf.strings[0]. We want to append the characters
2886                                                 // from resultbuf.strings[0] to (not including) p as these are
2887                                                 // the unique prefix
2888                                                 strlcpy(t, (resultbuf.numstrings > 0 ? resultbuf : dirbuf).strings[0], min(matchchars + 1, sizeof(t)));
2889                                         }
2890
2891                                         // first move the cursor
2892                                         key_linepos += (int)strlen(t) - (int)strlen(s);
2893
2894                                         // and now do the actual work
2895                                         *s = 0;
2896                                         strlcat(key_line, t, MAX_INPUTLINE);
2897                                         strlcat(key_line, s2, MAX_INPUTLINE); //add back chars after cursor
2898
2899                                         // and fix the cursor
2900                                         if(key_linepos > (int) strlen(key_line))
2901                                                 key_linepos = (int) strlen(key_line);
2902                                 }
2903                                 stringlistfreecontents(&resultbuf);
2904                                 stringlistfreecontents(&dirbuf);
2905
2906                                 return; // bail out, when we complete for a command that wants a file name
2907                         }
2908                 }
2909         }
2910
2911         // Count number of possible matches and print them
2912         c = Cmd_CompleteCountPossible(s);
2913         if (c)
2914         {
2915                 Con_Printf("\n%i possible command%s\n", c, (c > 1) ? "s: " : ":");
2916                 Cmd_CompleteCommandPrint(s);
2917         }
2918         v = Cvar_CompleteCountPossible(s);
2919         if (v)
2920         {
2921                 Con_Printf("\n%i possible variable%s\n", v, (v > 1) ? "s: " : ":");
2922                 Cvar_CompleteCvarPrint(s);
2923         }
2924         a = Cmd_CompleteAliasCountPossible(s);
2925         if (a)
2926         {
2927                 Con_Printf("\n%i possible alias%s\n", a, (a > 1) ? "es: " : ":");
2928                 Cmd_CompleteAliasPrint(s);
2929         }
2930         n = Nicks_CompleteCountPossible(key_line, key_linepos, s, true);
2931         if (n)
2932         {
2933                 Con_Printf("\n%i possible nick%s\n", n, (n > 1) ? "s: " : ":");
2934                 Cmd_CompleteNicksPrint(n);
2935         }
2936
2937         if (!(c + v + a + n))   // No possible matches
2938         {
2939                 if(s2[0])
2940                         strlcpy(&key_line[key_linepos], s2, sizeof(key_line) - key_linepos);
2941                 return;
2942         }
2943
2944         if (c)
2945                 cmd = *(list[0] = Cmd_CompleteBuildList(s));
2946         if (v)
2947                 cmd = *(list[1] = Cvar_CompleteBuildList(s));
2948         if (a)
2949                 cmd = *(list[2] = Cmd_CompleteAliasBuildList(s));
2950         if (n)
2951                 cmd = *(list[3] = Nicks_CompleteBuildList(n));
2952
2953         for (cmd_len = (int)strlen(s);;cmd_len++)
2954         {
2955                 const char **l;
2956                 for (i = 0; i < 3; i++)
2957                         if (list[i])
2958                                 for (l = list[i];*l;l++)
2959                                         if ((*l)[cmd_len] != cmd[cmd_len])
2960                                                 goto done;
2961                 // all possible matches share this character, so we continue...
2962                 if (!cmd[cmd_len])
2963                 {
2964                         // if all matches ended at the same position, stop
2965                         // (this means there is only one match)
2966                         break;
2967                 }
2968         }
2969 done:
2970
2971         // prevent a buffer overrun by limiting cmd_len according to remaining space
2972         cmd_len = min(cmd_len, (int)sizeof(key_line) - 1 - pos);
2973         if (cmd)
2974         {
2975                 key_linepos = pos;
2976                 memcpy(&key_line[key_linepos], cmd, cmd_len);
2977                 key_linepos += cmd_len;
2978                 // if there is only one match, add a space after it
2979                 if (c + v + a + n == 1 && key_linepos < (int)sizeof(key_line) - 1)
2980                 {
2981                         if(n)
2982                         { // was a nick, might have an offset, and needs colors ;) --blub
2983                                 key_linepos = pos - Nicks_offset[0];
2984                                 cmd_len = strlen(Nicks_list[0]);
2985                                 cmd_len = min(cmd_len, (int)sizeof(key_line) - 3 - pos);
2986
2987                                 memcpy(&key_line[key_linepos] , Nicks_list[0], cmd_len);
2988                                 key_linepos += cmd_len;
2989                                 if(key_linepos < (int)(sizeof(key_line)-4)) // space for ^, X and space and \0
2990                                         key_linepos = Nicks_AddLastColor(key_line, key_linepos);
2991                         }
2992                         key_line[key_linepos++] = ' ';
2993                 }
2994         }
2995
2996         // use strlcat to avoid a buffer overrun
2997         key_line[key_linepos] = 0;
2998         strlcat(key_line, s2, sizeof(key_line));
2999
3000         // free the command, cvar, and alias lists
3001         for (i = 0; i < 4; i++)
3002                 if (list[i])
3003                         Mem_Free((void *)list[i]);
3004 }
3005