1 // Emacs style mode select   -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: console.c 1566 2020-12-19 06:22:58Z wesleyjohnson $
5 //
6 // Copyright (C) 1998-2016 by DooM Legacy Team.
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License
10 // as published by the Free Software Foundation; either version 2
11 // of the License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 // GNU General Public License for more details.
17 //
18 //
19 // $Log: console.c,v $
20 // Revision 1.23  2003/08/11 13:50:03  hurdler
21 // go final + translucent HUD + fix spawn in net game
22 //
23 // Revision 1.22  2003/05/04 02:27:49  sburke
24 // Fix for big-endian machines.
25 //
26 // Revision 1.21  2002/09/10 19:29:46  hurdler
27 // Add log file under Linux
28 //
29 // Revision 1.20  2002/08/25 14:59:32  hurdler
30 //
31 // Revision 1.19  2002/07/23 15:07:09  mysterial
32 // Messages to second player appear on his half of the screen
33 //
34 // Revision 1.18  2001/12/26 17:24:46  hurdler
35 // Update Linux version
36 //
37 // Revision 1.17  2001/08/20 20:40:39  metzgermeister
38 // Revision 1.16  2001/05/16 21:21:14  bpereira
39 //
40 // Revision 1.15  2001/03/03 19:41:22  ydario
41 // I_OutputMsg not implemented in OS/2
42 //
43 // Revision 1.14  2001/03/03 06:17:33  bpereira
44 // Revision 1.13  2001/02/24 13:35:19  bpereira
45 //
46 // Revision 1.12  2001/01/25 22:15:41  bpereira
47 // added heretic support
48 //
49 // Revision 1.11  2000/11/12 09:48:15  bpereira
50 //
51 // Revision 1.10  2000/11/02 17:50:06  stroggonmeth
52 // Big 3Dfloors & FraggleScript commit!!
53 //
54 // Revision 1.9  2000/09/28 20:57:14  bpereira
55 // Revision 1.8  2000/08/31 14:30:55  bpereira
56 //
57 // Revision 1.7  2000/08/10 15:01:06  ydario
58 // OS/2 port
59 //
60 // Revision 1.6  2000/08/03 17:57:41  bpereira
61 //
62 // Revision 1.5  2000/04/24 15:10:56  hurdler
63 // Support colormap for text
64 //
65 // Revision 1.4  2000/04/16 18:38:06  bpereira
66 //
67 // Revision 1.3  2000/04/07 23:09:12  metzgermeister
68 // fixed array boundary error
69 //
70 // Revision 1.2  2000/02/27 00:42:10  hurdler
71 // Revision 1.1.1.1  2000/02/22 20:32:33  hurdler
72 // Initial import into CVS (v1.29 pr3)
73 //
74 //
75 // DESCRIPTION:
76 //      console for Doom LEGACY
77 //
78 //-----------------------------------------------------------------------------
79 
80 
81 #include "doomincl.h"
82 #include "console.h"
83 #include "g_game.h"
84 #include "g_input.h"
85   // keys.h, gc_console
86 #include "hu_stuff.h"
87 #include "s_sound.h"
88   // sounds.h, S_StartSound
89 #include "v_video.h"
90 #include "i_video.h"
91 #include "i_system.h"
92   // I_OutputMessage
93 #include "z_zone.h"
94 #include "d_main.h"
95 
96 //#include <unistd.h>
97 
98 #ifdef HWRENDER
99 #include "hardware/hw_main.h"
100 #endif
101 
102 #define CONSOLE_PROPORTIONAL
103 
104 // External control
105 boolean  con_self_refresh=false;  // true at game startup, screen need refreshing
106 boolean  con_recalc;     // set true when screen size has changed
107 
108 // Internal state
109 static boolean  con_started=false;  // console has been initialised
110 static boolean  con_video=false;  // text mode until video started
111 static boolean  con_forcepic=true;  // at startup toggle console translucency when
112                              // first off
113 
114 static int  con_tick;    // console ticker for anim or blinking prompt cursor
115                          // con_scrollup should use time (currenttime - lasttime)..
116 
117 static boolean  consoletoggle;  // true when console key pushed, ticker will handle
118 static boolean  console_ready;  // console prompt is ready
119 boolean  console_open = false;  // console is open
120 
121 int      con_destlines;  // vid lines used by console at final position
122 static int  con_curlines;  // vid lines currently used by console
123 
124 // Clip value for planes & sprites, so that the part of the view covered by the
125 // console is not drawn.
126 // It is set to the first drawable line under the console, and 0 when console is off.
127 int      con_clipviewtop;
128 
129 // TODO: choose max hud msg lines
130 #define  CON_MAXHUDLINES      5
131 
132 // Global interface with hu_stuff.
133 int      con_clearlines; // top screen lines to refresh when view reduced
134 boolean  con_hudupdate;  // when messages scroll, we need a backgrnd refresh
135 
136 // Internal state.
137 static int  con_hudlines;  // number of console heads up message lines
138 static int  con_hudtime[CON_MAXHUDLINES];  // remaining time of display for hud msg lines
139 
140 // To support splitscreen, 0=upper, 1=lower, 5=console only
141 static byte  con_viewnum[CON_MAXHUDLINES];
142 
143 
144 // console text output
145 static char* con_line;   // console text output current line
146 static int  con_cx;      // cursor position in current line
147 static int  con_cy;      // cursor line number in con_buffer, is always
148                          //  increasing, and wrapped around in the text
149                          //  buffer using modulo.
150 
151 static int  con_totallines; // lines of console text into the console buffer
152 static int  con_width;      // columns of chars, depend on vid mode width
153 static int  con_indent;     // pixel indent of console
154 
155 static int  con_scrollup;   // how many rows of text to scroll up (pgup/pgdn)
156 
157 
158 #define  CON_PROMPTCHAR        '>'
159 
160 // Hold last CON_MAX_LINEHIST prompt lines.
161 // [WDJ] Power 2 only, due to INDEXMASK.
162 #define  CON_MAX_LINEHIST    32
163 #define  CON_MAX_LINEHIST_INDEXMASK  (CON_MAX_LINEHIST-1)
164 #define  CON_MAX_LINELEN    256
165 
166 // First char is prompt.
167 static char inputlines[CON_MAX_LINEHIST][CON_MAX_LINELEN];
168 
169 static int  inputline;  // current input line number
170 static int  inputhist;  // line number of history input line to restore
171 static int  input_cx;   // position in current input line
172 
173 static pic_t*  con_backpic;  // console background picture, loaded static
174 static pic_t*  con_bordleft;
175 static pic_t*  con_bordright;  // console borders in translucent mode
176 
177 
178 // protos.
179 static void CON_Init_Input (void);
180 static void CON_Print (byte control, char *msg);
181 static void CONS_Clear_f (void);
182 static void CON_RecalcSize ( int width );
183 
184 static void CONS_speed_Change (void);
185 static void CON_Draw_Backpic (pic_t *pic, int startx, int destwidth);
186 
187 
188 //======================================================================
189 //                   CONSOLE VARS AND COMMANDS
190 //======================================================================
191 #if defined( MACOS_DI ) && ! defined( __GNUC__ )
192 #define  CON_BUFFERSIZE   4096  //my compiler cant handle local vars >32k
193 #else
194 #define  CON_BUFFERSIZE   16384
195 #endif
196 
197 static char  con_buffer[CON_BUFFERSIZE];
198 
199 
200 // how many seconds the hud messages lasts on the screen
201 consvar_t   cons_msgtimeout = {"con_hudtime","5",CV_VALUE|CV_SAVE,CV_Unsigned};
202 
203 // number of lines console move per frame
204 consvar_t   cons_speed = {"con_speed","8",CV_VALUE|CV_CALL|CV_SAVE,CV_Unsigned,&CONS_speed_Change};
205 
206 // percentage of screen height to use for console
207 consvar_t   cons_height = {"con_height","50",CV_SAVE,CV_Unsigned};
208 
209 CV_PossibleValue_t backpic_cons_t[]={{0,"translucent"},{1,"picture"},{0,NULL}};
210 // whether to use console background picture, or translucent mode
211 consvar_t   cons_backpic = {"con_backpic","0",CV_SAVE,backpic_cons_t};
212 
213 
214 //  Check CONS_speed value (must be positive and >0)
215 //
CONS_speed_Change(void)216 static void CONS_speed_Change (void)
217 {
218     if (cons_speed.value<1)
219         CV_SetValue (&cons_speed,1);
220 }
221 
222 
223 //  Clear console text buffer
224 //
CONS_Clear_f(void)225 static void CONS_Clear_f (void)
226 {
227     memset(con_buffer,0,CON_BUFFERSIZE);
228 
229     con_cx = 0;
230     con_cy = con_totallines-1;
231     con_line = &con_buffer[con_cy*con_width];
232     con_scrollup = 0;
233 }
234 
235 // Keys defined by the BIND command.
236 static char *bindtable[NUMINPUTS];
237 
CONS_Bind_f(void)238 static void CONS_Bind_f(void)
239 {
240     int  key;
241     COM_args_t  carg;
242 
243     COM_Args( &carg );
244 
245     if ( carg.num!=2 && carg.num!=3 )
246     {
247         int nb = 0;
248         CONS_Printf("bind <keyname> [<command>]\n");
249         CONS_Printf("\2bind table :\n");
250         for(key=0;key<NUMINPUTS;key++)
251         {
252             if(bindtable[key])
253             {
254                 CONS_Printf("%s : \"%s\"\n",G_KeynumToString (key),bindtable[key]);
255                 nb=1;
256             }
257         }
258         if(!nb)
259             CONS_Printf("Empty\n");
260         return;
261     }
262 
263     key=G_KeyStringtoNum( carg.arg[1] );
264     if(!key)
265     {
266         CONS_Printf("Invalid key name\n");
267         return;
268     }
269 
270     if(bindtable[key]!=NULL)
271     {
272         Z_Free(bindtable[key]);
273         bindtable[key]=NULL;
274     }
275 
276     if( carg.num==3 )
277         bindtable[key]=Z_StrDup( carg.arg[2] );
278 }
279 
280 
281 //======================================================================
282 //                          CONSOLE SETUP
283 //======================================================================
284 
285 // Prepare a colormap for GREEN ONLY translucency over background
286 //
287 byte*   whitemap = NULL;
288 byte*   greenmap;
289 byte*   graymap;
290 
291 // May be called again after command_restart
CON_SetupBackColormap(void)292 static void CON_SetupBackColormap (void)
293 {
294     int   i,j,k;
295     byte* pal;
296 
297 //
298 //  setup the green translucent background colormap
299 //
300     if( ! whitemap )
301     {
302         //  setup the green translucent background colormap
303         greenmap = (byte *) Z_Malloc(256,PU_STATIC,NULL);
304         whitemap = (byte *) Z_Malloc(256,PU_STATIC,NULL);
305         graymap  = (byte *) Z_Malloc(256,PU_STATIC,NULL);
306     }
307 
308     // wad containing PLAYPAL may not be found yet.
309     if( ! VALID_LUMP( W_CheckNumForName( "PLAYPAL" ) ) )
310         return;
311 
312     pal = W_CacheLumpName ("PLAYPAL",PU_CACHE); // temp, only used next loop
313 
314     for(i=0,k=0; i<768; i+=3,k++)
315     {
316         j = pal[i] + pal[i+1] + pal[i+2];
317 
318         if( EN_heretic )
319         {
320             greenmap[k] = 209 + (float)j*15/(3*255);   //remaps to greens(209-224)
321             graymap[k]  =       (float)j*35/(3*255);   //remaps to grays(0-35)
322             whitemap[k] = 145 + (float)j*15/(3*255);   //remaps to reds(145-168)
323         }
324         else
325             greenmap[k] = 127 - (j>>6);
326     }
327 
328 //
329 //  setup the white and gray text colormap
330 //
331     // this one doesn't need to be aligned, unless you convert the
332     // V_DrawMappedPatch() into optimised asm.
333 
334     if( EN_doom_etc )
335     {
336         for(i=0; i<256; i++)
337         {
338             whitemap[i] = i;        //remap each color to itself...
339             graymap[i]  = i;
340         }
341 
342         for(i=168;i<192;i++)
343         {
344             whitemap[i]=i-88;     //remaps reds(168-192) to whites(80-104)
345             graymap[i]=i-80;      //remaps reds(168-192) to gray(88-...)
346         }
347         whitemap[45]=190-88; // the color[45]=color[190] !
348         graymap [45]=190-80;
349         whitemap[47]=191-88; // the color[47]=color[191] !
350         graymap [47]=191-80;
351     }
352 }
353 
354 
355 //  Setup the console text buffer
356 //
357 // Init messaging, before zone memory, before video
358 // CON buffer will save all messages for display, so must be started very early
CON_Init_Setup(void)359 void CON_Init_Setup(void)
360 {
361     int i;
362 
363     // clear all lines
364     con_width = 0;  // no current text
365     CON_RecalcSize ( INITIAL_WINDOW_WIDTH );  // before vid is set
366     CONS_Clear_f ();   // clear all lines
367     con_destlines = INITIAL_WINDOW_HEIGHT;
368     con_curlines = INITIAL_WINDOW_HEIGHT;
369 
370     con_hudlines = CON_MAXHUDLINES;
371     CON_Clear_HUD ();
372 
373     // setup console input filtering
374     CON_Init_Input ();
375 
376     for(i=0;i<NUMINPUTS;i++)
377         bindtable[i]=NULL;
378 
379     consoletoggle = false;
380     con_started = true;
381 }
382 
383 // after zone memory init
CON_Register(void)384 void CON_Register(void)
385 {
386     // register our commands
387     CV_RegisterVar (&cons_msgtimeout);
388     CV_RegisterVar (&cons_speed);
389     CV_RegisterVar (&cons_height);
390     CV_RegisterVar (&cons_backpic);
391     COM_AddCommand ("cls", CONS_Clear_f, CC_console);
392     COM_AddCommand ("bind", CONS_Bind_f, CC_console);
393 }
394 
395 // after FullGraphics
CON_Init_Video(void)396 void CON_Init_Video(void)
397 {
398     // vid : from video setup
399     if(dedicated)
400         return;
401 
402     // make sure it is ready for the loading screen
403     CON_RecalcSize ( vid.width );
404 
405     CON_SetupBackColormap ();
406 
407     //note: CON_Ticker should always execute at least once before D_Display()
408     con_clipviewtop = 0;  // does not clip
409 
410     // load console background pic
411     con_backpic = (pic_t*) W_CachePicName ("CONSBACK",PU_STATIC);
412 
413     // borders MUST be there
414     con_bordleft  = (pic_t*) W_CachePicName ("CBLEFT",PU_STATIC);
415     con_bordright = (pic_t*) W_CachePicName ("CBRIGHT",PU_STATIC);
416 
417     // set console full screen for game startup after FullGraphics
418     con_destlines = vid.height;
419     con_curlines = vid.height;
420 
421     con_self_refresh = true; // need explicit screen refresh
422                         // until we are in Doomloop
423     con_video = true;   // if move CON init to before video startup
424 }
425 
426 
427 //  Console input initialization
428 //
CON_Init_Input(void)429 static void CON_Init_Input (void)
430 {
431     int    i;
432 
433     // prepare the first prompt line
434     memset (inputlines,0,sizeof(inputlines));
435     for (i=0; i<CON_MAX_LINEHIST; i++)
436         inputlines[i][0] = CON_PROMPTCHAR;
437     inputline = 0;
438     input_cx = 1;
439 }
440 
441 
442 
443 //======================================================================
444 //                        CONSOLE EXECUTION
445 //======================================================================
446 
447 // [WDJ] This originally was (BASEVIDWIDTH>>3)-2, but that gives 38,
448 // and the LARGE font overruns the right margin at 30.
449 #define MIN_CONWIDTH  32
450 #define MAX_CONWIDTH  200
451 
452 //  Called at screen size change to set the rows and line size of the
453 //  console text buffer.
454 //
CON_RecalcSize(int width)455 static void CON_RecalcSize ( int width )
456 {
457     int   new_conwidth, oldcon_width, oldnumlines, oldcon_cy;
458     int   i, conw;
459     char  con2[CON_BUFFERSIZE];
460     char  line2[MAX_CONWIDTH+4]; // BP: it is a line but who know ([WDJ] 30..94)
461 
462     con_recalc = false;
463 
464     // Calculate the new con_width.
465     if( width != vid.width )
466     {
467         // Before vid setup, so without any vid information
468         con_indent = 10;
469         if( width > 630 )
470            new_conwidth = (width>>4) - 2;
471         else
472            new_conwidth = (width>>3) - 2;
473     }
474     else
475     {
476         // Have graphics and vid information.
477         con_indent = vid.dupx * 10;  // indent of console text to avoid edge
478         V_SetupFont( cv_con_fontsize.value, NULL, 0 );
479 #ifdef CONSOLE_PROPORTIONAL
480         // Averages shorter text, but could overrun right margin.
481         // Proportional width of print is (hu_font[c].width * dupx + 1).
482         // As drawfont.xinc is fixed, and some characters are wider ('M', 'W'),
483         // it is still possible to overrun the right margin.
484         new_conwidth = (width - con_indent - con_indent) / (drawfont.xinc + 1);
485 #else
486         // Fixed font size, between left and right margin.
487         new_conwidth = (width - con_indent - con_indent) / drawfont.xinc;
488 #endif
489     }
490     if ( new_conwidth < MIN_CONWIDTH )
491     {
492         con_indent -= MIN_CONWIDTH - new_conwidth;
493         if( con_indent < 2 )  con_indent = 2;
494         new_conwidth = MIN_CONWIDTH;
495     }
496     if (new_conwidth > MAX_CONWIDTH)
497         new_conwidth = MAX_CONWIDTH;
498 
499     // check for change of video width
500     if (new_conwidth == con_width)
501         return;                 // didnt change
502 
503 
504     // save current
505     oldcon_width = con_width;
506     oldnumlines = con_totallines;
507     oldcon_cy = con_cy;
508     memcpy(con2, con_buffer, CON_BUFFERSIZE);
509 
510     // setup to new width
511     con_width = new_conwidth;
512     con_totallines = CON_BUFFERSIZE / con_width;
513     CONS_Clear_f ();
514 
515     // re-arrange console text buffer to keep text
516     if(oldcon_width) // not the first time
517     {
518         for(i=oldcon_cy+1; i<oldcon_cy+oldnumlines; i++)
519         {
520             char * con2p = &con2[(i% oldnumlines)*oldcon_width];
521             if( *con2p )
522             {
523                 memcpy(line2, con2p, oldcon_width);
524                 conw=oldcon_width-1;
525                 while(line2[conw]==' ' && conw) conw--;
526                 line2[conw+1]='\n';
527                 line2[conw+2]='\0';
528                 CON_Print( 5, line2);  // console only
529             }
530         }
531     }
532 }
533 
534 
535 // Handles Console moves in/out of screen (per frame)
536 //
CON_MoveConsole(void)537 static void CON_MoveConsole (void)
538 {
539     // up/down move to dest
540     if (con_curlines < con_destlines)
541     {
542         con_curlines+=cons_speed.value;
543         if (con_curlines > con_destlines)
544            con_curlines = con_destlines;
545     }
546     else if (con_curlines > con_destlines)
547     {
548         con_curlines-=cons_speed.value;
549         if (con_curlines < con_destlines)
550             con_curlines = con_destlines;
551     }
552 
553 }
554 
555 
556 //  Clear time of console heads up messages
557 //
CON_Clear_HUD(void)558 void CON_Clear_HUD (void)
559 {
560     int    i;
561 
562     for(i=0; i<con_hudlines; i++)
563         con_hudtime[i]=0;
564 }
565 
566 
567 // Force console to move out immediately
568 // note: con_ticker will set console_ready false
CON_ToggleOff(void)569 void CON_ToggleOff (void)
570 {
571     if (!con_destlines)
572         return;
573 
574     con_destlines = 0;
575     con_curlines = 0;
576     CON_Clear_HUD ();
577     con_forcepic = 0;
578     con_clipviewtop = 0;  // turn off console clip
579     console_open = false;  // instant off
580 }
581 
582 
583 static event_t  con_autorepeat_ev;
584 static byte     con_autorepeat_tick = 0;
585 
586 
587 //  Console ticker : handles console move in/out, cursor blinking
588 //
589 // Call once per tic.
CON_Ticker(void)590 void CON_Ticker (void)
591 {
592     // vid : from video setup
593     int    i;
594 
595     // cursor blinking
596     con_tick++;
597     con_tick &= 7;
598 
599     // console key was pushed
600     if (consoletoggle)
601     {
602         consoletoggle = false;
603 
604         if (con_destlines > 0)
605         {
606             // toggle off console
607             con_destlines = 0;
608             CON_Clear_HUD ();
609         }
610         else
611         {
612             // toggle console in
613             con_destlines = (cons_height.value*vid.height)/100;
614             if (con_destlines < 20)
615                 con_destlines = 20;
616             else
617             if (con_destlines > (vid.height - stbar_height) )
618                 con_destlines = vid.height - stbar_height;
619 
620             con_destlines &= ~0x3;      // multiple of text row height
621         }
622     }
623 
624     // console movement
625     if (con_destlines!=con_curlines)
626         CON_MoveConsole ();  // update con_curlines
627 
628 
629     // clip the view, so that the part under the console is not drawn
630     con_clipviewtop = 0;
631     if (cons_backpic.value)   // clip only when using an opaque background
632     {
633         if (con_curlines > 0)
634         {
635             // [WDJ] Set to highest drawable line under console.
636 //            con_clipviewtop = con_curlines;  // [WDJ] what it should be
637             con_clipviewtop = con_curlines - viewwindowy - 10;
638 //NOTE: BIG HACK::SUBTRACT 10, SO THAT WATER DON'T COPY LINES OF THE CONSOLE
639 //      WINDOW!!! (draw some more lines behind the bottom of the console)
640         }
641 
642         if (con_clipviewtop<0)
643             con_clipviewtop = 0;  // limit to drawable window
644     }
645 
646     // check if console ready for prompt
647 //    if ((con_curlines==con_destlines) && (con_destlines>=20))
648     console_ready = (con_destlines >= 20);
649 
650     // To detect console.
651     console_open = ((con_destlines + con_curlines) != 0);
652 
653     // make overlay messages disappear after a while
654     for (i=0 ; i<con_hudlines; i++)
655     {
656         con_hudtime[i]--;
657         if (con_hudtime[i]<0)
658             con_hudtime[i]=0;
659     }
660 
661     if( con_autorepeat_tick )
662     {
663         con_autorepeat_tick --;
664         if( con_autorepeat_tick == 1 )
665             CON_Responder( & con_autorepeat_ev );
666     }
667 }
668 
669 
670 //  Handles console key input
671 //
CON_Responder(event_t * ev)672 boolean CON_Responder(event_t *ev)
673 {
674 // sequential completions a la 4dos
675 static char    completion[80];
676 static int     comskips,varskips;
677 
678     const char * cmd = NULL;
679 
680     // [WDJ]  The compiler re-optimizes the returns.  Collecting them
681     // into common return true, and return false, has no net effect.
682     //
683     // Return true: eat the key
684     //        false: reject the key
685 
686     if(chat_on)
687         return false;
688 
689     // let go keyup events, don't eat them
690     if (ev->type != ev_keydown)
691     {
692         con_autorepeat_tick = 0;
693         return false;
694     }
695 
696     int key = ev->data1;
697 
698     // Detect console activate key (user definable).
699     if (key == gamecontrol[gc_console][0] ||
700         key == gamecontrol[gc_console][1] )   goto toggle_console;
701 
702     if (! console_ready)
703     {
704         // Console prompt not active.  This is the path during game play.
705         // Check game playing keys defined by BIND command.
706         // metzgermeister: boundary check !!
707         if((key < NUMINPUTS) && bindtable[key])
708         {
709             // [WDJ] Must be done as one string, it could try to execute a partial string.
710             COM_BufAddText ( va( "%s\n", bindtable[key] ) );
711             return true;
712         }
713         return false;
714     }
715 
716     if( key >= KEY_NUMKB )
717         return false;  // mouse, joystick button
718 
719 
720     // Console prompt active
721     // [WDJ] Trying to use a switch stmt, increases the size for unknown
722     // reasons related to optimization.  It uses extra tests to gain speed.
723     // It optimizes the repeated tests of the key better than the switch.
724 
725     // eat shift only if console active
726     if (key == KEY_RSHIFT || key == KEY_LSHIFT)
727       return true;
728 
729     // escape key toggle off console
730     if (key == KEY_ESCAPE)   goto toggle_console;
731 
732     // command completion forward (tab) and backward (shift-tab)
733     if (key == KEY_TAB)
734     {
735         // TOTALLY UTTERLY UGLY NIGHT CODING BY FAB!!! :-)
736         //
737         // sequential command completion forward and backward
738 
739         // remember typing for several completions (�-la-4dos)
740         if (inputlines[inputline][input_cx-1] != ' ')
741         {
742             if (strlen (inputlines[inputline]+1)<80)
743                 strcpy (completion, inputlines[inputline]+1);
744             else
745                 completion[0] = 0;
746 
747             comskips = varskips = 0;
748         }
749         else
750         {
751             // comskips < 0  indicates var name completion
752             if (shiftdown)
753             {
754                 if (comskips<0)
755                 {
756                     if (--varskips<0)
757                         comskips = -(comskips+2);
758                 }
759                 else
760                 if (comskips>0)
761                     comskips--;
762             }
763             else
764             {
765                 if (comskips<0)
766                     varskips++;
767                 else
768                     comskips++;
769             }
770         }
771 
772         if (comskips>=0)
773         {
774             cmd = COM_CompleteCommand (completion, comskips);
775             if (!cmd)
776             {
777                 // No command, try var completion.
778                 // dirty:make sure if comskips is zero, to have a neg value
779                 comskips = -(comskips+1);
780             }
781         }
782         if (comskips<0)
783             cmd = CV_CompleteVar (completion, varskips);
784 
785         if (cmd)
786         {
787             memset(inputlines[inputline]+1,0,CON_MAX_LINELEN-1);
788             strcpy (inputlines[inputline]+1, cmd);
789             input_cx = strlen(cmd)+1;
790             inputlines[inputline][input_cx] = ' ';
791             input_cx++;
792             inputlines[inputline][input_cx] = 0;
793         }
794         else
795         {
796             // No command, no var completion.  Backup off this candidate.
797             if (comskips>0)
798                 comskips--;
799             else
800             if (varskips>0)
801                 varskips--;
802         }
803 
804         return true;
805     }
806 
807     // move up (backward) in console textbuffer
808     if (key == KEY_PGUP)
809     {
810         if (con_scrollup < (con_totallines-((con_curlines-16)>>3)) )
811             con_scrollup++;
812         goto enable_autorepeat;
813     }
814     if (key == KEY_PGDN)
815     {
816         if (con_scrollup>0)
817             con_scrollup--;
818         goto enable_autorepeat;
819     }
820 
821     // oldset text in buffer
822     if (key == KEY_HOME)
823     {
824         con_scrollup = (con_totallines-((con_curlines-16)>>3));
825         return true;
826     }
827     // most recent text in buffer
828     if (key == KEY_END)
829     {
830         con_scrollup = 0;
831         return true;
832     }
833 
834     // command enter
835     if (key == KEY_ENTER)
836     {
837         if (input_cx<2)
838             return true;  // nothing significant
839 
840         // push the command
841         // [WDJ] Must be done as one string, it could try to execute a partial string.
842         // The first char is prompt, not part of the command.
843         COM_BufAddText ( va( "%s\n", inputlines[inputline]+1 ));
844 
845         CONS_Printf("%s\n",inputlines[inputline]);
846 
847         // Advance to next inputline.
848         inputline = (inputline+1) & CON_MAX_LINEHIST_INDEXMASK;
849         inputhist = inputline;
850 
851         memset(inputlines[inputline],0,CON_MAX_LINELEN);
852         inputlines[inputline][0] = CON_PROMPTCHAR;
853         input_cx = 1;
854 
855         return true;
856     }
857 
858     // backspace command prompt
859     if (key == KEY_BACKSPACE)
860     {
861         if (input_cx>1)  // back to prompt
862         {
863             input_cx--;
864             inputlines[inputline][input_cx] = 0;
865         }
866         return true;
867     }
868 
869     // move back in input history
870     if (key == KEY_UPARROW)
871     {
872         // copy one of the previous inputlines to the current
873         do{
874             inputhist = (inputhist - 1) & CON_MAX_LINEHIST_INDEXMASK; // cycle back
875         }while (inputhist!=inputline && !inputlines[inputhist][1]);
876 
877         // stop at the last history input line, which is the
878         // current line + 1 because we cycle through the 32 input lines
879         if (inputhist==inputline)
880             inputhist = (inputline + 1) & CON_MAX_LINEHIST_INDEXMASK;
881 
882         memcpy (inputlines[inputline],inputlines[inputhist],CON_MAX_LINELEN);
883         input_cx = strlen(inputlines[inputline]);
884 
885         return true;
886     }
887 
888     // move forward in input history
889     if (key == KEY_DOWNARROW)
890     {
891         if (inputhist==inputline) return true;
892 
893         do{
894             inputhist = (inputhist + 1) & CON_MAX_LINEHIST_INDEXMASK;
895         } while (inputhist!=inputline && !inputlines[inputhist][1]);
896 
897         memset (inputlines[inputline],0,CON_MAX_LINELEN);
898 
899         // back to currentline
900         if (inputhist==inputline)
901         {
902             inputlines[inputline][0] = CON_PROMPTCHAR;
903             input_cx = 1;
904         }
905         else
906         {
907             // [WDJ] GCC 10 complains that strings may overlap so cannot use strcpy.
908             // But overlap case handled above.
909             memmove( inputlines[inputline], inputlines[inputhist], CON_MAX_LINELEN );
910             input_cx = strlen(inputlines[inputline]);
911         }
912         return true;
913     }
914 
915     // interpret it as input char
916     char c = ev->data2;
917 
918     // allow people to use keypad in console (good for typing IP addresses) - Calum
919     if (key >= KEY_KEYPAD0 && key <= KEY_PLUSPAD)
920     {
921       const char keypad_translation[] = {'0','1','2','3','4','5','6','7','8','9','.','/','*','-','+'};
922       c = keypad_translation[key - KEY_KEYPAD0];
923     }
924 
925     // enter a printable char into the command prompt
926     if (c < ' ' || c > '~')
927       return false;
928 
929     // add key to cmd line here
930     if (input_cx<CON_MAX_LINELEN)
931     {
932         // make sure letters are lowercase for commands & cvars
933         if (c >= 'A' && c <= 'Z')
934             c = c + 'a' - 'A';
935 
936         inputlines[inputline][input_cx] = c;
937         input_cx++;
938         inputlines[inputline][input_cx] = 0;
939     }
940 
941     return true;
942 
943 toggle_console:
944     consoletoggle = true;  // signal to CON_Ticker
945     return true;
946 
947 enable_autorepeat:
948     con_autorepeat_ev = *ev;
949     con_autorepeat_tick = 4;
950     return true;
951 }
952 
953 
954 //  Insert a new line in the console text buffer
955 //
956 //  viewnum : splitscreen 0=upper, 1=lower, single player uses 0, 5=console only
CON_Linefeed(byte viewnum)957 static void CON_Linefeed (byte viewnum)
958 {
959     con_viewnum[con_cy%con_hudlines] = viewnum; // May be msg for player2
960 
961     // set time for heads up messages
962     con_hudtime[con_cy%con_hudlines] =
963         (viewnum < 2)? cons_msgtimeout.value*TICRATE : 0;
964 
965     con_cy++;
966     con_cx = 0;
967 
968     con_line = &con_buffer[(con_cy%con_totallines)*con_width];
969     memset(con_line,' ',con_width);
970 
971     // make sure the view borders are refreshed if hud messages scroll
972     con_hudupdate = true;         // see HU_Erase()
973 }
974 
975 
976 //  Outputs text into the console text buffer
977 //
CON_Print(byte control,char * msg)978 static void CON_Print (byte control, char *msg)
979 {
980     int  l;  // word length
981     int  text_color=0;  // 0x80 is white text flag
982     int  viewnum=0;  // 0=upper, 1=lower, Single player uses 0. 5=console only
983 
984     viewnum = control & 0x0F;
985 
986     //TODO: finish text colors
987     if (*msg<5)
988     {
989         switch( *msg )
990         {
991          case '\2' :  // white text
992             text_color = 0x80;
993             break;
994          case '\3' :  // white text + sound
995             text_color = 0x80; // white text
996             if ( gamemode == doom2_commercial )
997                 S_StartSound(sfx_radio);
998             else
999                 S_StartSound(sfx_tink);
1000             break;
1001          default:
1002             break;
1003         }
1004     }
1005 
1006     while (*msg)
1007     {
1008         // handle non-printable characters and white spaces
1009         while ( *msg <= ' ' )
1010         {
1011             switch( *msg )
1012             {
1013              case '\r':  // carriage return
1014                 con_cy--;
1015                 CON_Linefeed (viewnum);
1016                 break;
1017              case '\n':  // linefeed
1018                 CON_Linefeed (viewnum);
1019                 break;
1020              case ' ':  // leading space
1021                 con_line[con_cx++] = ' ';
1022                 if (con_cx >= con_width)
1023                     CON_Linefeed(viewnum);
1024                 break;
1025              case '\t':  // tab
1026                 //adds tab spaces for nice layout in console
1027                 do
1028                 {
1029                     con_line[con_cx++] = ' ';
1030                 } while (con_cx%4 != 0);
1031 
1032                 if (con_cx>=con_width)
1033                     CON_Linefeed(viewnum);
1034                 break;
1035              case 0:  // End of string
1036                 return;
1037 
1038              default:
1039                 break;
1040             }
1041             msg++;
1042         }
1043 
1044         // printable character
1045         // Find end of word
1046         for (l=0; l<con_width; l++)
1047             if( msg[l] <= ' ' )  break;  // until space, or EOS
1048 
1049         // word wrap when word is too long for the rest of the width
1050         if (con_cx+l>con_width)
1051             CON_Linefeed (viewnum);
1052 
1053         // a word at a time
1054         for ( ; l>0; l--)
1055             con_line[con_cx++] = *(msg++) | text_color;
1056     }
1057 }
1058 
1059 
1060 //  Console print! Wahooo! Lots o fun!
1061 //
1062 #define CONS_BUF_SIZE 1024
1063 
1064 
1065 // Tables for comparison to cv_gameplay.
1066 // {0,"Off"},{1,"Minimal"},{2,"Play"},{3,"Verbose"},{4,"Debug"},{5,"Dev"},
1067 
1068 // Show messages on hud when cv_gameplay is set at or higher than this table.
1069 // Otherwise route message only to the console.
1070 // 0 always shows the message on the hud.
1071 // indexed by EMSG_cat
1072 static byte gameplay_hud_message_table[ 16 ] =
1073 {
1074  3, // EMSG_CONS
1075  0, // EMSG_playmsg
1076  0, // EMSG_playmsg2
1077  0, // unk3
1078  0, // unk4
1079  250, // EMSG_console
1080  1, // EMSG_hud
1081  0, // unk7
1082  2, // EMSG_info
1083  3, // EMSG_ver
1084  4, // EMSG_debug
1085  5, // EMSG_dev
1086  2, // EMSG_warn
1087  0, // EMSG_errlog
1088  0, // EMSG_error
1089  0  // EMSG_error2
1090 };
1091 // Show messages on console when cv_gameplay is set at or higher than this table.
1092 // 0 always shows the message on the console.
1093 // indexed by EMSG_cat
1094 static byte gameplay_con_message_table[ 16 ] =
1095 {
1096  0, // EMSG_CONS  (cannot be blocking CONS_Printf from the console)
1097  0, // EMSG_playmsg
1098  0, // EMSG_playmsg2
1099  0, // unk3
1100  0, // unk4
1101  0, // EMSG_console  (interactive console specific)
1102  0, // EMSG_hud
1103  0, // unk7
1104  1, // EMSG_info
1105  2, // EMSG_ver
1106  3, // EMSG_debug
1107  4, // EMSG_dev
1108  0, // EMSG_warn
1109  0, // EMSG_errlog
1110  0, // EMSG_error
1111  0  // EMSG_error2
1112 };
1113 
1114 // [WDJ] print from va_list
1115 // Caller must have va_start, va_end, or else run-time segfault will occur.
GenPrintf_va(const byte emsg,const char * fmt,va_list ap)1116 void GenPrintf_va (const byte emsg, const char *fmt, va_list ap)
1117 {
1118     byte eout = EOUT_flags;  // default for CONS_Printf
1119     byte ecat = emsg & EMSG_cat;
1120     byte viewnum = 0;
1121     // vid : from video setup
1122     char  txt[CONS_BUF_SIZE];
1123 
1124     // print the error
1125     vsnprintf(txt, CONS_BUF_SIZE, fmt, ap);
1126     txt[CONS_BUF_SIZE-1] = '\0'; // term, when length limited
1127 
1128     // Route the message to various outputs according to category.
1129     switch( ecat )
1130     {
1131      case EMSG_playmsg:
1132         eout = EOUT_hud | EOUT_con;
1133         viewnum = 0;
1134         break;
1135      case EMSG_playmsg2:  // player2 splitscreen
1136         eout = EOUT_hud | EOUT_con;
1137         viewnum = 1;
1138         break;
1139      case EMSG_console:  // console interactive
1140         eout = EOUT_con;  // console only
1141         viewnum = 5;
1142         break;
1143      case EMSG_hud:
1144         eout |= EOUT_hud | EOUT_con;
1145         break;
1146      case EMSG_warn:
1147         eout |= EOUT_all;
1148         break;
1149      case EMSG_errlog:  // stderr and log, but not console
1150         eout = EOUT_text | EOUT_log;
1151         break;
1152      case EMSG_error: // error soft
1153         eout |= EOUT_all;
1154         break;
1155      case EMSG_error2: // error severe
1156         eout = EOUT_all | EOUT_hud;
1157         break;
1158      case EMSG_debug: // debug category
1159 #if defined(SMIF_PC_DOS) || defined(WIN32) || defined(SMIF_OS2_NATIVE)
1160         eout = EOUT_text | EOUT_con | EOUT_log;
1161 #else
1162         // Linux, Mac
1163         eout = EOUT_text | EOUT_log;
1164 #endif
1165         if( cv_showmessages.EV >= 4 )
1166             eout |= EOUT_con | EOUT_hud;
1167         break;
1168      case EMSG_dev:   // development category
1169         eout &= ~EOUT_hud;
1170 #if defined(SMIF_PC_DOS) || defined(WIN32) || defined(SMIF_OS2_NATIVE)
1171         eout |= EOUT_con;
1172 #else
1173         // Linux, Mac
1174         eout = EOUT_text | EOUT_log;
1175 #endif
1176         break;
1177      default:
1178         break;
1179     }
1180 
1181     if( emsg & EMSG_all )
1182        eout |= EOUT_text | EOUT_con | EOUT_log;
1183 
1184 #ifdef LOGMESSAGES
1185     if( eout & EOUT_log )
1186     {
1187         // echo console prints to log file
1188         if (logstream)
1189             fputs(txt, logstream);
1190     }
1191 #endif
1192     DEBFILE(txt);
1193 
1194 #ifndef  DEBUG_MESSAGES_ON
1195     // Hide debug messages for release version
1196     if((ecat == EMSG_debug) && (verbose == 0) && (cv_showmessages.EV < 4))
1197        goto done;  // disable debug messages
1198 #endif
1199 
1200     if( (eout & EOUT_text) || (! vid.draw_ready) )
1201     {
1202         // Errors to terminal, and before graphics
1203         I_OutputMsg ("%s",txt);
1204         fflush(NULL);
1205     }
1206 
1207 #if 0
1208 #ifdef LINUX
1209     // Keep debug messages off console, for some versions
1210     if( ecat == EMSG_debug )  goto done;
1211 #endif
1212 #endif
1213 
1214     if( ! con_started )  goto done;
1215 
1216     // During gameplay the hud is dedicated to the game, according
1217     // to the cv_showmessages setting.
1218     if( gameplay_msg )
1219     {
1220         // During game playing, honor the showmessage option.
1221         if( cv_showmessages.EV < gameplay_hud_message_table[ ecat ] )
1222             viewnum = 5;  // console only
1223         if( cv_showmessages.EV < gameplay_con_message_table[ ecat ] )
1224             return;
1225     }
1226 
1227 #if 0
1228     // Other ecat tests have already forced EOUT_con for error messages.
1229 #ifdef LAUNCHER
1230     // Allow errors to go to Launcher fatal error display.
1231 #else
1232     if( (ecat == EMSG_error) || (ecat == EMSG_error2) )
1233     {
1234         // Errors to CON, unless no con_video yet.
1235         if( ! con_video )  goto done;
1236     }
1237 #endif
1238 #endif
1239 
1240     // Situations inherited from EMSG_ settings.
1241     if( (eout & (EOUT_hud|EOUT_con)) == 0  )  goto done;  // no CONS flag
1242 
1243     if( (eout & (EOUT_hud|EOUT_con)) == EOUT_con )
1244        viewnum = 5;  // console only
1245 
1246     // Output to EOUT_con, EOUT_hud, splitscreen.
1247     // Write the message in con text buffer.
1248     CON_Print ( viewnum, txt );
1249 
1250     // make sure new text is visible
1251     con_scrollup = 0;
1252 
1253     // if not in display loop, force screen update
1254     if ( con_self_refresh || (emsg & EMSG_now) )
1255     {
1256         // Protect against segfaults during video mode switch.
1257         if( ! vid.draw_ready )   goto done;
1258         // Have graphics, but do not have refresh loop running.
1259 #if defined(SMIF_WIN_NATIVE) || defined(SMIF_OS2_NATIVE)
1260         // show startup screen and message using only 'software' graphics
1261         // (rendermode may be hardware accelerated, but the video mode is not set yet)
1262         CON_Draw_Backpic (con_backpic, 0, vid.width);    // put console background
1263         I_LoadingScreen ( txt );
1264 #else
1265         V_Clear_Display();
1266         // here we display the console background and console text
1267         // (no hardware accelerated support for these versions)
1268         CON_Drawer ();
1269         I_FinishUpdate ();              // page flip or blit buffer
1270 #endif
1271     }
1272     else if ( ! con_video )
1273     {
1274         // Protect against segfaults during video mode switch.
1275         if( ! vid.draw_ready )   goto done;
1276         // Text messages without con_video graphics.
1277         CON_Draw_Console ();  // Text with or without con_video
1278         I_FinishUpdate ();
1279     }
1280  done:
1281     return;
1282 }
1283 
1284 // General printf interface for CONS_Printf
1285 // Due to script files and indirect commands, many error messages
1286 // still go through here, so they are seen on stderr.
1287 // Global param: EOUT_flags
CONS_Printf(const char * fmt,...)1288 void CONS_Printf (const char *fmt, ...)
1289 {
1290     va_list ap;
1291 
1292     va_start(ap, fmt);
1293     GenPrintf_va( EMSG_CONS, fmt, ap );
1294     va_end(ap);
1295 }
1296 
1297 //  Print an error message, and wait for ENTER key to continue.
1298 //  To make sure the user has seen the message
1299 //
CONS_Error(char * msg)1300 void CONS_Error (char *msg)
1301 {
1302 #ifdef SMIF_WIN_NATIVE
1303     if( graphics_state < VGS_active )
1304     {
1305         I_MsgBox (msg);
1306         return;
1307     }
1308 #endif
1309     // Must pass msg through an interface that uses va_start, va_end.
1310     GenPrintf( EMSG_error, "\2%s", msg);   // write error msg in different colour
1311 
1312     // CONS_Printf ("Press ENTER to continue\n");
1313     // dirty quick hack, but for the good cause
1314     // while (I_GetKey() != KEY_ENTER)
1315     //   ;
1316 }
1317 
1318 // Console interaction printf interface.
1319 // This only routes the print to the console, not the logs, not to stderr.
1320 // Do not use this for command error messages, because many commands are
1321 // used in scripts, or exec indirectly, and the user would not see
1322 // the error messages.
con_Printf(const char * fmt,...)1323 void con_Printf (const char *fmt, ...)
1324 {
1325     va_list ap;
1326 
1327     va_start(ap, fmt);
1328     GenPrintf_va( EMSG_console, fmt, ap );
1329     va_end(ap);
1330 }
1331 
1332 // Debug printf interface.
1333 // Easy to use replacement for debug messages going through CONS_Printf.
debug_Printf(const char * fmt,...)1334 void debug_Printf (const char *fmt, ...)
1335 {
1336     va_list ap;
1337 
1338     // It is still possible to use GenPrintf(EMSG_debug, ) which is
1339     // why there will be no special tests here.
1340     va_start(ap, fmt);
1341     GenPrintf_va( EMSG_debug, fmt, ap );
1342     va_end(ap);
1343 }
1344 
1345 // For info, debug, dev, verbose messages.
1346 // Print to output set by EOUT_flags.
GenPrintf(const byte emsg,const char * fmt,...)1347 void GenPrintf (const byte emsg, const char *fmt, ...)
1348 {
1349     va_list ap;
1350     va_start(ap, fmt);
1351     GenPrintf_va( emsg, fmt, ap );  // print to text, console, and logs
1352     va_end(ap);
1353 }
1354 
1355 
1356 //======================================================================
1357 //                          CONSOLE DRAW
1358 //======================================================================
1359 
1360 
1361 // draw console prompt line
1362 //
CON_DrawInput(int y)1363 static void CON_DrawInput ( int y )
1364 {
1365     char    *p;
1366     int     x;
1367 
1368     // Draw console text, screen0
1369     // V_SetupDraw( 0 | V_NOSCALE );
1370 
1371     // input line scrolls left if it gets too long
1372     //
1373     p = inputlines[inputline];
1374     if (input_cx >= con_width)
1375         p += input_cx - con_width + 1;
1376 
1377 #ifdef CONSOLE_PROPORTIONAL
1378     int xj, xcursor = 0;
1379     x = con_indent;
1380     for(xj=0; xj<con_width; xj++)
1381     {
1382         if( xj == input_cx )
1383            xcursor = x;
1384         x += V_DrawCharacter( x, y, p[xj] );  // red
1385     }
1386 #else
1387     // Fixed width font.
1388     for(x=0; x<con_width; x++)
1389         V_DrawCharacter( x * drawfont.xinc + con_indent, y, p[x] );  // red
1390 #endif
1391 
1392     // draw the blinking cursor
1393     //
1394 #ifdef CONSOLE_PROPORTIONAL
1395     if (con_tick<4)
1396         V_DrawCharacter( xcursor, y, 0x80 | '_' );  // white
1397 #else
1398     x = (input_cx>=con_width) ? con_width - 1 : input_cx;
1399     if (con_tick<4)
1400         V_DrawCharacter( x * drawfont.xinc + con_indent, y, 0x80 | '_' );  // white
1401 #endif
1402 }
1403 
1404 
1405 // draw the last lines of console text to the top of the screen
1406 //
1407 #ifdef HWRENDER //Added by Mysterial
1408     extern float gr_viewheight; //needed for drawing second player's messages
1409                                 //halfway down
1410 #endif
1411 
CON_Draw_Hudlines(void)1412 static void CON_Draw_Hudlines (void)
1413 {
1414     fontinfo_t * fip = V_FontInfo();  // draw font1 and wad font strings
1415     byte     viewnum;
1416     char       *p;
1417     int        y1,y2,x,y,i;
1418 
1419     if (con_hudlines<=0)
1420         return;
1421 
1422     V_SetupFont( cv_msg_fontsize.value, fip, V_NOSCALE );
1423 
1424     // player1 message y
1425     y1 = (chat_on) ?
1426       drawfont.yinc  // leave place for chat input in the first row of text
1427       : 0;
1428     y = y1;
1429     // player2 message y in splitscreen
1430 #ifdef HWRENDER
1431     // by Mysterial, moved by [WDJ]
1432     y2 = (rendermode==render_soft)? rdraw_viewheight : gr_viewheight;
1433 #else
1434     y2 = rdraw_viewheight;
1435 #endif
1436 
1437     for (i= con_cy-con_hudlines+1; i<=con_cy; i++)
1438     {
1439         if (i < 0)
1440             continue;
1441         if (con_hudtime[i%con_hudlines] == 0)
1442             continue;
1443 
1444         viewnum = con_viewnum[i%con_hudlines];
1445         // viewnum: 0=upper, 1=lower, 5=console only
1446         if( viewnum > 1 )  continue;  // console only
1447         y = (viewnum == 1)? y2 : y1;
1448 
1449         p = &con_buffer[(i%con_totallines)*con_width];
1450 
1451 #ifdef CONSOLE_PROPORTIONAL
1452         x = drawfont.xinc;  // indent
1453         int xj;
1454         for (xj=0; xj<con_width; xj++)
1455         {
1456             // red, proportional width font
1457             x += V_DrawCharacter ( x, y, p[xj] );
1458 //            x += V_DrawCharacter ( x, y, (p[x]&0x7f) );  // force red
1459         }
1460 #else
1461         for (x=0; x<con_width; x++)
1462         {
1463             // red, fixed width font
1464             V_DrawCharacter ( (x+1)*drawfont.xinc, y, p[x] );
1465 //            V_DrawCharacter ( (x+1)*drawfont.xinc, y, (p[x]&0x7f) );
1466         }
1467 #endif
1468 
1469         // viewnum: 0=upper, 1=lower
1470         if ( viewnum == 1 )
1471            y2 += drawfont.yinc;
1472         else
1473            y1 += drawfont.yinc;
1474     }
1475 
1476     // top screen lines that might need clearing when view is reduced
1477     con_clearlines = y;      // this is handled by HU_Erase ();
1478 }
1479 
1480 
1481 //  Scale a pic_t at 'startx' pos, to 'destwidth' columns.
1482 //                startx,destwidth is resolution dependent
1483 //  Used to draw console borders, console background.
1484 //  The pic must be sized BASEVIDHEIGHT height.
1485 //
1486 //  TODO: ASM routine!!! lazy Fab!!
1487 //
CON_Draw_Backpic(pic_t * pic,int startx,int destwidth)1488 static void CON_Draw_Backpic (pic_t *pic, int startx, int destwidth)
1489 {
1490     // vid : from video setup
1491     int   pic_h = pic->height;
1492     int   pic_w = pic->width;
1493     int   x, y;
1494     int   v;
1495     fixed_t  frac, fracstep;
1496     byte  *src;
1497     byte  *dest;  // within screen buffer
1498 
1499     // [WDJ] Draw picture for all bpp, bytepp, and padded lines.
1500     dest = V_GetDrawAddr( startx, 0 );  // screen0 buffer
1501 
1502     for (y=0 ; y<con_curlines ; y++, dest += vid.ybytes)
1503     {
1504         // scale the picture to the resolution
1505         v = pic_h - ((con_curlines - y)*(BASEVIDHEIGHT-1)/vid.height) - 1;
1506 
1507         src = pic->data + v*pic_w;
1508 
1509         // in case of the console backpic, simplify
1510         if (pic_w == destwidth && vid.bytepp == 1)
1511             memcpy (dest, src, destwidth);
1512         else
1513         {
1514             // scale pic to screen width
1515             frac = 0;
1516             fracstep = (pic_w<<16)/destwidth;
1517             for (x=0 ; x<destwidth ; x+=4)
1518             {
1519                 V_DrawPixel( dest, x, src[frac>>16] );
1520                 frac += fracstep;
1521                 V_DrawPixel( dest, x+1, src[frac>>16] );
1522                 frac += fracstep;
1523                 V_DrawPixel( dest, x+2, src[frac>>16] );
1524                 frac += fracstep;
1525                 V_DrawPixel( dest, x+3, src[frac>>16] );
1526                 frac += fracstep;
1527             }
1528         }
1529     }
1530 
1531 }
1532 
1533 
1534 // Draw the console background, text, and prompt if enough places.
1535 // May use font1 or wad fonts.
1536 // Uses screens[0].
1537 //
CON_Draw_Console(void)1538 void CON_Draw_Console (void)
1539 {
1540     // vid : from video setup
1541     fontinfo_t * fip = V_FontInfo();  // draw font1 and wad font strings
1542     char  *p;
1543     int   i,x,y;
1544     int   w = 0, x2 = 0;
1545 
1546     if (con_curlines <= 0)
1547         return;
1548 
1549     if ( rendermode != render_soft && use_font1 )
1550         return;  // opengl graphics, hu_font not loaded yet
1551 
1552     V_SetupFont( cv_con_fontsize.value, fip, V_NOSCALE );
1553 
1554     //FIXME: refresh borders only when console bg is translucent
1555     con_clearlines = con_curlines;    // clear console draw from view borders
1556     con_hudupdate = true;             // always refresh while console is on
1557 
1558     // draw console background
1559     if (!con_video)
1560     {
1561         V_Clear_Display();
1562     }
1563     else
1564     if (cons_backpic.value || con_forcepic)
1565     {
1566 #ifdef HWRENDER // not win32 only 19990829 by Kin
1567         if (rendermode!=render_soft)
1568             V_DrawScalePic_Num (0, con_curlines-200*vid.fdupy,
1569                                 W_GetNumForName ("CONSBACK") );
1570         else
1571 #endif
1572             CON_Draw_Backpic (con_backpic,0,vid.width);   // picture as background
1573     }
1574     else
1575     {
1576 #ifdef HWRENDER // not win32 only 19990829 by Kin
1577         if( rendermode == render_soft )
1578 #endif
1579         {
1580             w = fip->xinc * vid.dupx;  // font1 or wad font
1581             x2 = vid.width - w;
1582             CON_Draw_Backpic (con_bordleft,0,w);
1583             CON_Draw_Backpic (con_bordright,x2,w);
1584         }
1585         // translucent background
1586         //Hurdler: what's the correct value of w and x2 in hardware mode ???
1587 #if 0
1588         // Darken the borders too
1589         if( cv_darkback.value )
1590             V_FadeConsBack (0, vid.width, con_curlines);
1591         else
1592             V_FadeConsBack (w, x2, con_curlines);
1593 #else
1594         V_FadeConsBack (w, x2, con_curlines);
1595 #endif
1596     }
1597 
1598     // draw console text lines from bottom to top
1599     // (going backward in console buffer text)
1600     //
1601     if (con_curlines <20)       //8+8+4
1602         return;
1603 
1604     i = con_cy - con_scrollup;
1605 
1606     // skip the last empty line due to the cursor being at the start
1607     // of a new line
1608     if (!con_scrollup && !con_cx)
1609         i--;
1610 
1611     // draw lines with font1 or wad font
1612     for (y=con_curlines - (drawfont.yinc * 2); y>=0; y-=drawfont.yinc)
1613     {
1614         if (i<0)
1615             i=0;
1616 
1617         p = &con_buffer[(i%con_totallines)*con_width];
1618 
1619 #ifdef CONSOLE_PROPORTIONAL
1620         x = con_indent;  // indent
1621         int xj;
1622         for (xj=0; xj<con_width; xj++)
1623         {
1624             // red, proportional width font
1625             x += V_DrawCharacter ( x, y, p[xj] );
1626 //            x += V_DrawCharacter ( x, y, (p[x]&0x7f) );  // force red
1627         }
1628 #else
1629         // red, fixed width font
1630         for (x=0;x<con_width;x++)
1631             V_DrawCharacter( x * drawfont.xinc + con_indent, y, p[x] );
1632 #endif
1633         i--;
1634     }
1635 
1636 
1637     // draw prompt if enough place (not while game startup)
1638     //
1639     if ((con_curlines==con_destlines) && (con_curlines>=20) && !con_self_refresh)
1640         CON_DrawInput ( con_curlines - drawfont.yinc );
1641 }
1642 
1643 
1644 //  Console refresh drawer, call each frame
1645 //
CON_Drawer(void)1646 void CON_Drawer (void)
1647 {
1648     if (!con_started)
1649         return;
1650 
1651     if (con_recalc)
1652         CON_RecalcSize ( vid.width );
1653 
1654     if ( use_font1 )
1655         return;  // hu_font not loaded yet
1656 
1657 #ifndef CONSOLE_PROPORTIONAL
1658     //Fab: bighack: patch 'I' letter leftoffset so it centers
1659     hu_font['I'-HU_FONTSTART]->leftoffset = -2;
1660 #endif
1661 
1662     if (con_curlines>0)
1663         CON_Draw_Console ();
1664     else
1665     if (gamestate==GS_LEVEL)
1666         CON_Draw_Hudlines ();
1667 
1668 #ifndef CONSOLE_PROPORTIONAL
1669     hu_font['I'-HU_FONTSTART]->leftoffset = 0;
1670 #endif
1671 }
1672