1 /*
2 Copyright (C) 1997-2001 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 #include "client.h"
23 
24 console_t	con;
25 
26 cvar_t		*con_notifytime;
27 
28 
29 #define		MAXCMDLINE	256
30 extern	char	key_lines[32][MAXCMDLINE];
31 extern	int		edit_line;
32 extern	int		key_linepos;
33 
34 int stringLength( const char *string );
35 int stringLengthExtra ( const char *string);
stringLen(const char * string)36 int stringLen (const char *string)
37 {
38 	return stringLength(string);
39 }
40 
setParams(char modifier,int * red,int * green,int * blue,int * bold,int * shadow,int * italic,int * reset)41 qboolean setParams(char modifier, int *red, int *green, int *blue, int *bold, int *shadow, int *italic, int *reset)
42 {
43 	switch (modifier)
44 	{
45 		case 'R':
46 		case 'r':
47 			*reset = true;
48 			return true;
49 		case 'B':
50 		case 'b':
51 			if (*bold)
52 				*bold = false;
53 			else
54 				*bold=true;
55 			return true;
56 		case 'S':
57 		case 's':
58 			if (*shadow)
59 				*shadow=false;
60 			else
61 				*shadow=true;
62 			return true;
63 		case 'I':
64 		case 'i':
65 			if (*italic)
66 				*italic=false;
67 			else
68 				*italic=true;
69 			return true;
70 		case '1':	//red
71 			*red =	255;
72 			*green=	0;
73 			*blue =	0;
74 			return true;
75 		case '2':	//green
76 			*red =	0;
77 			*green=	255;
78 			*blue =	0;
79 			return true;
80 		case '3':	//yellow
81 			*red =	255;
82 			*green=	255;
83 			*blue =	0;
84 			return true;
85 		case '4':	//blue
86 			*red =	0;
87 			*green=	0;
88 			*blue =	255;
89 			return true;
90 		case '5':	//teal
91 			*red =	0;
92 			*green=	255;
93 			*blue =	255;
94 			return true;
95 		case '6':	//pink
96 			*red =	255;
97 			*green=	0;
98 			*blue =	255;
99 			return true;
100 		case '7':	//white
101 			*red =	255;
102 			*green=	255;
103 			*blue =	255;
104 			return true;
105 		case '8':	//black
106 			*red =	0;
107 			*green=	0;
108 			*blue =	0;
109 			return true;
110 		case '9':	//dark red
111 			*red =	185;
112 			*green=	0;
113 			*blue =	0;
114 			return true;
115 		case '0':	//gray
116 			*red =	185;
117 			*green=	185;
118 			*blue =	185;
119 			return true;
120 	}
121 
122 	return false;
123 }
124 
125 
DrawString(int x,int y,const char * string,int alpha)126 void DrawString( int x, int y, const char *string, int alpha )
127 {
128 	unsigned i, j;
129 	char modifier, character;
130 	int len, red, green, blue, italic, shadow, bold, reset;
131 	qboolean modified;
132 
133 	//default
134 	red = 255;
135 	green = 255;
136 	blue = 255;
137 	italic = false;
138 	shadow = false;
139 	bold = false;
140 
141 	len = strlen( string );
142 	for ( i = 0, j = 0; i < len; i++ )
143 	{
144 		modifier = string[i];
145 		if (modifier&128) modifier &= ~128;
146 
147 		if (modifier == '^' && i < len)
148 		{
149 			i++;
150 
151 			reset = 0;
152 			modifier = string[i];
153 			if (modifier&128) modifier &= ~128;
154 
155 			if (modifier!='^')
156 			{
157 				modified = setParams(modifier, &red, &green, &blue, &bold, &shadow, &italic, &reset);
158 
159 				if (reset)
160 				{
161 					red = 255;
162 					green = 255;
163 					blue = 255;
164 					italic = false;
165 					shadow = false;
166 					bold = false;
167 				}
168 				if (modified)
169 					continue;
170 				else
171 					i--;
172 			}
173 		}
174 		j++;
175 
176 		character = string[i];
177 		if (bold && character<128)
178 			character += 128;
179 		else if (bold && character>128)
180 			character -= 128;
181 
182 		if (shadow)
183 			re.DrawScaledChar( ( x + j*FONT_SIZE+FONT_SIZE/4 ), y+1,  character,1.0f, 0, 0, 0, alpha, italic);
184 		re.DrawScaledChar( ( x + j*FONT_SIZE ), y, character ,1.0f, red, green, blue, alpha, italic);
185 	}
186 }
187 
DrawAltString(int x,int y,const char * string,int alpha)188 void DrawAltString( int x, int y, const char *string, int alpha )
189 {
190 	unsigned i, j;
191 	char modifier, character;
192 	int len, red, green, blue, italic, shadow, bold, reset;
193 	qboolean modified;
194 
195 
196 	//default
197 	red = 255;
198 	green = 255;
199 	blue = 255;
200 	italic = false;
201 	shadow = false;
202 	bold = true;
203 
204 	len = strlen( string );
205 	for ( i = 0, j = 0; i < len; i++ )
206 	{
207 		modifier = string[i];
208 		if (modifier&128) modifier &= ~128;
209 
210 		if (modifier == '^' && i < len)
211 		{
212 			i++;
213 
214 			reset = 0;
215 			modifier = string[i];
216 			if (modifier&128) modifier &= ~128;
217 
218 			if (modifier!='^')
219 			{
220 				modified = setParams(modifier, &red, &green, &blue, &bold, &shadow, &italic, &reset);
221 
222 				if (reset)
223 				{
224 					red = 255;
225 					green = 255;
226 					blue = 255;
227 					italic = false;
228 					shadow = false;
229 					bold = true;
230 				}
231 				if (modified)
232 					continue;
233 				else
234 					i--;
235 			}
236 		}
237 		j++;
238 
239 		character = string[i];
240 		if (bold && character<128)
241 			character += 128;
242 		else if (bold && character>128)
243 			character -= 128;
244 
245 		if (shadow)
246 			re.DrawScaledChar( ( x + j*FONT_SIZE+FONT_SIZE/4 ), y+1,  character,1.0f, 0, 0, 0, alpha, italic);
247 		re.DrawScaledChar( ( x + j*FONT_SIZE ), y, character ,1.0f, red, green, blue, alpha, italic);
248 	}
249 }
250 
Key_ClearTyping(void)251 void Key_ClearTyping (void)
252 {
253 	key_lines[edit_line][1] = 0;	// clear any typing
254 	key_linepos = 1;
255 	con.backedit = 0;
256 }
257 
258 /*
259 ================
260 Con_ToggleConsole_f
261 ================
262 */
Con_ToggleConsole_f(void)263 void Con_ToggleConsole_f (void)
264 {
265 //	SCR_EndLoadingPlaque ();	// get rid of loading plaque
266 
267 	if (cls.state == ca_disconnected)
268 	{
269 		Cbuf_AddText ("d1\n");
270 		return;
271 	}
272 
273 	Key_ClearTyping ();
274 	Con_ClearNotify ();
275 
276 	if (cls.consoleActive)
277 	{
278 		cls.consoleActive = false;
279 
280 		if (cls.key_dest != key_menu)
281 		Cvar_Set ("paused", "0");
282 	}
283 	else
284 	{
285 		cls.consoleActive = true;
286 
287 		if (Cvar_VariableValue ("maxclients") == 1 && Com_ServerState () && cls.key_dest != key_menu)
288 			Cvar_Set ("paused", "1");
289 	}
290 }
291 
292 /*
293 ================
294 Con_ToggleChat_f
295 ================
296 */
Con_ToggleChat_f(void)297 void Con_ToggleChat_f (void)
298 {
299 	Key_ClearTyping ();
300 
301 	if (cls.consoleActive)
302 	{
303 		if (cls.state == ca_active)
304 		{
305 			M_ForceMenuOff ();
306 			cls.consoleActive = false;
307 			cls.key_dest = key_game;
308 		}
309 	}
310 	else
311 		cls.consoleActive = true;
312 
313 	Con_ClearNotify ();
314 }
315 
316 /*
317 ================
318 Con_Clear_f
319 ================
320 */
Con_Clear_f(void)321 void Con_Clear_f (void)
322 {
323 	memset (con.text, ' ', CON_TEXTSIZE);
324 }
325 
326 
327 /*
328 ================
329 Con_Dump_f
330 
331 Save the console contents out to a file
332 ================
333 */
Con_Dump_f(void)334 void Con_Dump_f (void)
335 {
336 	int		l, x;
337 	char	*line;
338 	FILE	*f;
339 	char	buffer[1024];
340 	char	name[MAX_OSPATH];
341 
342 	if (Cmd_Argc() != 2)
343 	{
344 		Com_Printf ("usage: condump <filename>\n");
345 		return;
346 	}
347 
348 	Com_sprintf (name, sizeof(name), "%s/%s.txt", FS_Gamedir(), Cmd_Argv(1));
349 
350 	Com_Printf ("Dumped console text to %s.\n", name);
351 	FS_CreatePath (name);
352 	f = fopen (name, "w");
353 	if (!f)
354 	{
355 		Com_Printf ("^2ERROR: couldn't open.\n");
356 		return;
357 	}
358 
359 	// skip empty lines
360 	for (l = con.current - con.totallines + 1 ; l <= con.current ; l++)
361 	{
362 		line = con.text + (l%con.totallines)*con.linewidth;
363 		for (x=0 ; x<con.linewidth ; x++)
364 			if (line[x] != ' ')
365 				break;
366 		if (x != con.linewidth)
367 			break;
368 	}
369 
370 	// write the remaining lines
371 	buffer[con.linewidth] = 0;
372 	for ( ; l <= con.current ; l++)
373 	{
374 		line = con.text + (l%con.totallines)*con.linewidth;
375 		strncpy (buffer, line, con.linewidth);
376 		for (x=con.linewidth-1 ; x>=0 ; x--)
377 		{
378 			if (buffer[x] == ' ')
379 				buffer[x] = 0;
380 			else
381 				break;
382 		}
383 		for (x=0; buffer[x]; x++)
384 			buffer[x] &= 0x7f;
385 
386 		fprintf (f, "%s\n", buffer);
387 	}
388 
389 	fclose (f);
390 }
391 
392 
393 /*
394 ================
395 Con_ClearNotify
396 ================
397 */
Con_ClearNotify(void)398 void Con_ClearNotify (void)
399 {
400 	int		i;
401 
402 	for (i=0 ; i<NUM_CON_TIMES ; i++)
403 		con.times[i] = 0;
404 }
405 
406 
407 /*
408 ================
409 Con_MessageMode_f
410 ================
411 */
Con_MessageMode_f(void)412 void Con_MessageMode_f (void)
413 {
414 	chat_team = false;
415 	cls.key_dest = key_message;
416 	cls.consoleActive = false;
417 }
418 
419 /*
420 ================
421 Con_MessageMode2_f
422 ================
423 */
Con_MessageMode2_f(void)424 void Con_MessageMode2_f (void)
425 {
426 	chat_team = true;
427 	cls.key_dest = key_message;
428 	cls.consoleActive = false;
429 }
430 
431 /*
432 ================
433 Con_CheckResize
434 
435 If the line width has changed, reformat the buffer.
436 ================
437 */
Con_CheckResize(void)438 void Con_CheckResize (void)
439 {
440 	int		i, j, width, oldwidth, oldtotallines, numlines, numchars;
441 	char	tbuf[CON_TEXTSIZE];
442 
443 	if (con_font_size)
444 		width = viddef.width/con_font_size->value - 2;
445 	else
446 		width = (viddef.width >> 3) - 2;
447 
448 	if (width == con.linewidth)
449 		return;
450 
451 	if (width < 1)			// video hasn't been initialized yet
452 	{
453 		width = 38;
454 		con.linewidth = width;
455 		con.backedit = 0;
456 		con.totallines = CON_TEXTSIZE / con.linewidth;
457 		memset (con.text, ' ', CON_TEXTSIZE);
458 	}
459 	else
460 	{
461 		oldwidth = con.linewidth;
462 		con.linewidth = width;
463 		con.backedit = 0;
464 		oldtotallines = con.totallines;
465 		con.totallines = CON_TEXTSIZE / con.linewidth;
466 		numlines = oldtotallines;
467 
468 		if (con.totallines < numlines)
469 			numlines = con.totallines;
470 
471 		numchars = oldwidth;
472 
473 		if (con.linewidth < numchars)
474 			numchars = con.linewidth;
475 
476 		memcpy (tbuf, con.text, CON_TEXTSIZE);
477 		memset (con.text, ' ', CON_TEXTSIZE);
478 
479 		for (i=0 ; i<numlines ; i++)
480 		{
481 			for (j=0 ; j<numchars ; j++)
482 			{
483 				con.text[(con.totallines - 1 - i) * con.linewidth + j] =
484 						tbuf[((con.current - i + oldtotallines) %
485 							  oldtotallines) * oldwidth + j];
486 			}
487 		}
488 
489 		Con_ClearNotify ();
490 	}
491 
492 	con.current = con.totallines - 1;
493 	con.display = con.current;
494 }
495 
496 
497 /*
498 ================
499 Con_Init
500 ================
501 */
Con_Init(void)502 void Con_Init (void)
503 {
504 	con.linewidth = -1;
505 	con.backedit = 0;
506 
507 	Con_CheckResize ();
508 
509 	Com_Printf ("Console initialized.\n");
510 
511 //
512 // register our commands
513 //
514 	con_notifytime = Cvar_Get ("con_notifytime", "5", 0);
515 
516 	Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
517 	Cmd_AddCommand ("togglechat", Con_ToggleChat_f);
518 	Cmd_AddCommand ("messagemode", Con_MessageMode_f);
519 	Cmd_AddCommand ("messagemode2", Con_MessageMode2_f);
520 	Cmd_AddCommand ("clear", Con_Clear_f);
521 	Cmd_AddCommand ("condump", Con_Dump_f);
522 	con.initialized = true;
523 }
524 
525 
526 /*
527 ===============
528 Con_Linefeed
529 ===============
530 */
Con_Linefeed(void)531 void Con_Linefeed (void)
532 {
533 	con.x = 0;
534 	if (con.display == con.current)
535 		con.display++;
536 	con.current++;
537 	memset (&con.text[(con.current%con.totallines)*con.linewidth]
538 	, ' ', con.linewidth);
539 }
540 
541 /*
542 ================
543 Con_Print
544 
545 Handles cursor positioning, line wrapping, etc
546 All console printing must go through this in order to be logged to disk
547 If no console is visible, the text will appear at the top of the game window
548 ================
549 */
Con_Print(char * txt)550 void Con_Print (char *txt)
551 {
552 	int		y;
553 	int		c, l;
554 	static int	cr;
555 	int		mask;
556 
557 	if (!con.initialized)
558 		return;
559 
560 	if (txt[0] == 1 || txt[0] == 2)
561 	{
562 		mask = 128;		// go to colored text
563 		txt++;
564 	}
565 	else
566 		mask = 0;
567 
568 
569 	while ( (c = *txt) )
570 	{
571 	// count word length
572 		for (l=0 ; l< con.linewidth ; l++)
573 			if ( txt[l] <= ' ')
574 				break;
575 
576 	// word wrap
577 		if (l != con.linewidth && (con.x + l > con.linewidth) )
578 			con.x = 0;
579 
580 		txt++;
581 
582 		if (cr)
583 		{
584 			con.current--;
585 			cr = false;
586 		}
587 
588 
589 		if (!con.x)
590 		{
591 			Con_Linefeed ();
592 		// mark time for transparent overlay
593 			if (con.current >= 0)
594 				con.times[con.current % NUM_CON_TIMES] = cls.realtime;
595 		}
596 
597 		switch (c)
598 		{
599 		case '\n':
600 			con.x = 0;
601 			break;
602 
603 		case '\r':
604 			con.x = 0;
605 			cr = 1;
606 			break;
607 
608 		default:	// display character and advance
609 			y = con.current % con.totallines;
610 			con.text[y*con.linewidth+con.x] = c | mask | con.ormask;
611 			con.x++;
612 			if (con.x >= con.linewidth)
613 				con.x = 0;
614 			break;
615 		}
616 
617 	}
618 }
619 
620 
621 /*
622 ==============
623 Con_CenteredPrint
624 ==============
625 */
Con_CenteredPrint(char * text)626 void Con_CenteredPrint (char *text)
627 {
628 	int		l;
629 	char	buffer[1024];
630 
631 	l = strlen(text);
632 	l = (con.linewidth-l)/2;
633 	if (l < 0)
634 		l = 0;
635 	memset (buffer, ' ', l);
636 	strcpy (buffer+l, text);
637 	strcat (buffer, "\n");
638 	Con_Print (buffer);
639 }
640 
641 /*
642 ==============================================================================
643 
644 DRAWING
645 
646 ==============================================================================
647 */
648 
649 
650 /*
651 ================
652 Con_DrawInput
653 
654 The input line scrolls horizontally if typing goes beyond the right edge
655 ================
656 */
Con_DrawInput(void)657 void Con_DrawInput (void)
658 {
659 	int		y;
660 	int		i;
661 	char	*text, output[2048];
662 
663 	if (!cls.consoleActive && cls.state == ca_active)
664 		return;		// don't draw anything (always draw if not active)
665 
666 	text = key_lines[edit_line];
667 
668 // fill out remainder with spaces
669 	for (i=key_linepos ; i< con.linewidth ; i++)
670 		text[i] = ' ';
671 
672 //	prestep if horizontally scrolling
673 	if (key_linepos >= con.linewidth)
674 		text += 1 + key_linepos - con.linewidth;
675 
676 // draw it
677 	y = con.vislines-FONT_SIZE*2;
678 
679 	Com_sprintf (output, sizeof(output), "");
680 	for (i=0 ; i<con.linewidth ; i++)
681 	{
682 		if (con.backedit == key_linepos-i && ((int)(cls.realtime>>8)&1))
683 			Com_sprintf (output, sizeof(output), "%s%c", output, 11 );
684 		else
685 			Com_sprintf (output, sizeof(output), "%s%c", output, text[i]);
686 	}
687 
688 
689 
690 	DrawString ( FONT_SIZE, con.vislines - 2.75*FONT_SIZE, output, 256);
691 
692 // remove cursor
693 	key_lines[edit_line][key_linepos] = 0;
694 }
695 
696 
697 /*
698 ================
699 Con_DrawNotify
700 
701 Draws the last few lines of output transparently over the game top
702 ================
703 */
704 
Con_DrawNotify(void)705 void Con_DrawNotify (void)
706 {
707 	int		x, xtra;
708 	char	*text, output[2048];
709 	int		i,j;
710 	char	*s;
711 	int		skip;
712 	int		alpha;
713 	float	v, time, lines;
714 
715 	lines = 0;
716 
717 	v = 0;
718 
719 	Com_sprintf (output, sizeof(output), "");
720 
721 	//this is the say msg while typeing...
722 	if (cls.key_dest == key_message)
723 	{
724 		if (chat_team)
725 			Com_sprintf (output, sizeof(output), "%s", " say_team: ");
726 		else
727 			Com_sprintf (output, sizeof(output), "%s", " say: ");
728 
729 		s = chat_buffer;
730 		x = 0;
731 		if (chat_bufferlen > (viddef.width/FONT_SIZE)-(strlen(output)+1))
732 			x += chat_bufferlen - (int)((viddef.width/FONT_SIZE)-(strlen(output)+1));
733 
734 		while(s[x])
735 		{
736 			if (chat_backedit && chat_backedit == chat_bufferlen-x && ((int)(cls.realtime>>8)&1))
737 				Com_sprintf (output, sizeof(output), "%s%c", output, 11 );
738 			else
739 				Com_sprintf (output, sizeof(output), "%s%c", output, (char)s[x]);
740 
741 			x++;
742 		}
743 
744 		if (!chat_backedit)
745 			Com_sprintf (output, sizeof(output), "%s%c", output, 10+((int)(cls.realtime>>8)&1) );
746 
747 		DrawString (0, v, output, 255);
748 
749 		v += FONT_SIZE*2; //make extra space so we have room
750 	}
751 
752 	for (i= con.current-NUM_CON_TIMES+1 ; i<=con.current ; i++)
753 	{
754 		if (i < 0)
755 			continue;
756 		time = con.times[i % NUM_CON_TIMES];
757 		if (time == 0)
758 			continue;
759 		time = cls.realtime - time;
760 		if (time > con_notifytime->value*1000)
761 			continue;
762 
763 		//vertical offset set by closest to dissapearing
764 		lines++;
765 	}
766 
767 	if (lines)
768 		for (j=0, i= con.current-NUM_CON_TIMES+1 ; i<=con.current ; i++, j++)
769 		{
770 			if (i < 0)
771 				continue;
772 			time = con.times[i % NUM_CON_TIMES];
773 			if (time == 0)
774 				continue;
775 			time = cls.realtime - time;
776 			if (time > con_notifytime->value*1000)
777 				continue;
778 
779 			text = con.text + (i % con.totallines)*con.linewidth;
780 
781 
782 			alpha = 255 * sqrt( (1.0-time/(con_notifytime->value*1000.0+1.0)) * (((float)v+8.0)) / (8.0*lines) );
783 			if (alpha<0)alpha=0; if (alpha>255)alpha=255;
784 
785 
786 			Com_sprintf (output, sizeof(output), "");
787 			for (x = 0 ; x < con.linewidth ; x++)
788 				Com_sprintf (output, sizeof(output), "%s%c", output, (char)text[x]);
789 
790 			DrawString (FONT_SIZE/2, v, output, alpha);
791 
792 			v += FONT_SIZE;
793 		}
794 
795 	if (v)
796 	{
797 		SCR_AddDirtyPoint (0,0);
798 		SCR_AddDirtyPoint (viddef.width-1, v);
799 	}
800 }
801 
802 /*
803 ================
804 Con_DrawConsole
805 
806 Draws the console with the solid background
807 ================
808 */
Con_DrawConsole(float frac,qboolean ingame)809 void Con_DrawConsole (float frac, qboolean ingame)
810 {
811 	int				i, j, x, y, n;
812 	int				rows;
813 	char			*text, output[1024];
814 	int				row;
815 	int				lines;
816 	char			version[64];
817 	char			dlbar[1024];
818 
819 
820 	lines = viddef.height * frac;
821 	if (lines <= 0)
822 		return;
823 
824 	if (lines > viddef.height)
825 		lines = viddef.height;
826 
827 // draw the background
828 	re.DrawStretchPic (0, -viddef.height+lines, viddef.width, viddef.height, "conback");
829 	SCR_AddDirtyPoint (0,0);
830 	SCR_AddDirtyPoint (viddef.width-1,lines-1);
831 
832 	Com_sprintf (version, sizeof(version), "^b^s^1%s v%s", GAMENAME,  VERSION);
833 
834 	DrawString(viddef.width-FONT_SIZE*(stringLen(version)+1), lines-FONT_SIZE-1, version, 255);
835 
836 // draw the text
837 	con.vislines = lines;
838 
839 
840 	rows = (lines-22)/FONT_SIZE;		// rows of text to draw
841 	y = lines - 3.75*FONT_SIZE;
842 
843 // draw from the bottom up
844 	if (con.display != con.current)
845 	{
846 	// draw arrows to show the buffer is backscrolled
847 		for (x=0 ; x<con.linewidth ; x+=FONT_SIZE*2)
848 			re.DrawChar ( (x+1)*FONT_SIZE, y, '^', 256);
849 
850 		y -= FONT_SIZE;
851 		rows--;
852 	}
853 
854 
855 
856 	row = con.display;
857 	for (i=0 ; i<rows ; i++, y-=FONT_SIZE, row--)
858 	{
859 		if (row < 0)
860 			break;
861 		if (con.current - row >= con.totallines)
862 			break;		// past scrollback wrap point
863 
864 		text = con.text + (row % con.totallines)*con.linewidth;
865 
866 
867 		Com_sprintf (output, sizeof(output), "");
868 		for (x=0 ; x<con.linewidth ; x++)
869 			Com_sprintf (output, sizeof(output), "%s%c", output, text[x]);
870 
871 		DrawString ( 4, y, output, 256);
872 	}
873 
874 //ZOID
875 	// draw the download bar
876 	// figure out width
877 	if (cls.download)
878 	{
879 		if ((text = strrchr(cls.downloadname, '/')) != NULL)
880 			text++;
881 		else
882 			text = cls.downloadname;
883 
884 		x = con.linewidth - ((con.linewidth * 7) / 40);
885 		y = x - strlen(text) - FONT_SIZE;
886 		i = con.linewidth/3;
887 		if (strlen(text) > i) {
888 			y = x - i - 11;
889 			strncpy(dlbar, text, i);
890 			dlbar[i] = 0;
891 			strcat(dlbar, "...");
892 		} else
893 			strcpy(dlbar, text);
894 		strcat(dlbar, ": ");
895 		i = strlen(dlbar);
896 		dlbar[i++] = '\x80';
897 		// where's the dot go?
898 		if (cls.downloadpercent == 0)
899 			n = 0;
900 		else
901 			n = y * cls.downloadpercent / 100;
902 
903 		for (j = 0; j < y; j++)
904 			if (j == n)
905 				dlbar[i++] = '\x83';
906 			else
907 				dlbar[i++] = '\x81';
908 		dlbar[i++] = '\x82';
909 		dlbar[i] = 0;
910 
911 		sprintf(dlbar + strlen(dlbar), " %02d%%", cls.downloadpercent);
912 
913 		// draw it
914 		y = con.vislines-12;
915 
916 		DrawString ( 4, y, dlbar, 256);
917 	}
918 //ZOID
919 
920 // draw the input prompt, user text, and cursor if desired
921 	Con_DrawInput ();
922 }
923 
924 
925