1 // Emacs style mode select   -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: d_main.c 1567 2020-12-21 02:43:17Z wesleyjohnson $
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 // Copyright (C) 1998-2016 by DooM Legacy Team.
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 //
20 // $Log: d_main.c,v $
21 // Revision 1.66  2005/12/20 14:58:25  darkwolf95
22 // Monster behavior CVAR - Affects how monsters react when they shoot each other
23 //
24 // Revision 1.65  2004/07/27 08:19:34  exl
25 // New fmod, fs functions, bugfix or 2, patrol nodes
26 //
27 // Revision 1.64  2004/04/20 00:34:26  andyp
28 // Linux compilation fixes and string cleanups
29 //
30 // Revision 1.63  2003/11/21 17:52:05  darkwolf95
31 // added "Monsters Infight" for Dehacked patches
32 //
33 // Revision 1.62  2003/07/23 17:20:37  darkwolf95
34 // Initial Chex Quest 1 Support
35 //
36 // Revision 1.61  2003/07/14 21:22:23  hurdler
37 // Revision 1.60  2003/07/13 13:16:15  hurdler
38 //
39 // Revision 1.59  2003/05/04 02:28:34  sburke
40 // Fix for big-endian machines.
41 //
42 // Revision 1.58  2002/12/13 22:34:27  ssntails
43 // MP3/OGG support!
44 //
45 // Revision 1.57  2002/09/27 16:40:08  tonyd
46 // First commit of acbot
47 //
48 // Revision 1.56  2002/09/17 21:20:02  hurdler
49 // Quick hack for hacx freeze
50 //
51 // Revision 1.55  2002/08/24 22:42:02  hurdler
52 // Apply Robert Hogberg patches
53 //
54 // Revision 1.54  2002/07/26 15:21:36  hurdler
55 //
56 // Revision 1.53  2001/12/31 16:56:39  metzgermeister
57 // see Dec 31 log
58 //
59 // Revision 1.52  2001/08/20 20:40:39  metzgermeister
60 //
61 // Revision 1.51  2001/08/12 15:21:03  bpereira
62 // see my log
63 //
64 // Revision 1.50  2001/07/16 22:35:40  bpereira
65 // - fixed crash of e3m8 in heretic
66 // - fixed crosshair not drawed bug
67 //
68 // Revision 1.49  2001/05/27 13:42:47  bpereira
69 // Revision 1.48  2001/05/16 21:21:14  bpereira
70 //
71 // Revision 1.47  2001/05/16 17:12:52  crashrl
72 // Added md5-sum support, removed recursiv wad search
73 //
74 // Revision 1.46  2001/04/27 13:32:13  bpereira
75 //
76 // Revision 1.45  2001/04/17 22:26:07  calumr
77 // Initial Mac add
78 //
79 // Revision 1.44  2001/04/04 20:24:21  judgecutor
80 // Added support for the 3D Sound
81 //
82 // Revision 1.43  2001/04/02 18:54:32  bpereira
83 // Revision 1.42  2001/04/01 17:35:06  bpereira
84 // Revision 1.41  2001/03/30 17:12:49  bpereira
85 //
86 // Revision 1.40  2001/03/19 18:25:02  hurdler
87 // Is there a GOOD reason to check for modified game with shareware version?
88 //
89 // Revision 1.39  2001/03/03 19:43:09  ydario
90 // OS/2 code cleanup
91 //
92 // Revision 1.38  2001/02/24 13:35:19  bpereira
93 // Revision 1.37  2001/02/10 12:27:13  bpereira
94 //
95 // Revision 1.36  2001/01/25 22:15:41  bpereira
96 // added heretic support
97 //
98 // Revision 1.35  2000/11/06 20:52:15  bpereira
99 // Revision 1.34  2000/11/03 03:27:17  stroggonmeth
100 // Revision 1.33  2000/11/02 19:49:35  bpereira
101 //
102 // Revision 1.32  2000/11/02 17:50:06  stroggonmeth
103 // Big 3Dfloors & FraggleScript commit!!
104 //
105 // Revision 1.31  2000/10/21 08:43:28  bpereira
106 // Revision 1.30  2000/10/08 13:29:59  bpereira
107 // Revision 1.29  2000/10/02 18:25:44  bpereira
108 // Revision 1.28  2000/10/01 10:18:16  bpereira
109 // Revision 1.27  2000/09/28 20:57:14  bpereira
110 // Revision 1.26  2000/08/31 14:30:55  bpereira
111 //
112 // Revision 1.25  2000/08/29 15:53:47  hurdler
113 // Remove master server connect timeout on LAN (not connected to Internet)
114 //
115 // Revision 1.24  2000/08/21 21:13:00  metzgermeister
116 // Implementation of I_GetKey() in Linux
117 //
118 // Revision 1.23  2000/08/10 14:50:19  ydario
119 // OS/2 port
120 //
121 // Revision 1.22  2000/05/07 08:27:56  metzgermeister
122 // Revision 1.21  2000/04/30 10:30:10  bpereira
123 //
124 // Revision 1.20  2000/04/25 19:49:46  metzgermeister
125 // support for automatic wad search
126 //
127 // Revision 1.19  2000/04/24 20:24:38  bpereira
128 // Revision 1.18  2000/04/23 16:19:52  bpereira
129 //
130 // Revision 1.17  2000/04/22 20:27:35  metzgermeister
131 // support for immediate fullscreen switching
132 //
133 // Revision 1.16  2000/04/21 20:04:20  hurdler
134 // fix a problem with my last SDL merge
135 //
136 // Revision 1.15  2000/04/19 15:21:02  hurdler
137 // add SDL midi support
138 //
139 // Revision 1.14  2000/04/18 12:55:39  hurdler
140 // Revision 1.13  2000/04/16 18:38:07  bpereira
141 //
142 // Revision 1.12  2000/04/07 23:10:15  metzgermeister
143 // fullscreen support under X in Linux
144 //
145 // Revision 1.11  2000/04/06 20:40:22  hurdler
146 // Mostly remove warnings under windows
147 //
148 // Revision 1.10  2000/04/05 15:47:46  stroggonmeth
149 // Added hack for Dehacked lumps. Transparent sprites are now affected by colormaps.
150 //
151 // Revision 1.9  2000/04/04 00:32:45  stroggonmeth
152 // Initial Boom compatability plus few misc changes all around.
153 //
154 // Revision 1.8  2000/03/29 19:39:48  bpereira
155 //
156 // Revision 1.7  2000/03/28 16:18:41  linuxcub
157 // Added a command to the Linux sound-server which sets a master volume.
158 // Added code to the main parts of doomlegacy which uses this command to
159 // implement volume control for sound effects.
160 //
161 // Added code so the (really cool) cd music works for me. The volume didn't
162 // work for me (with a Teac 532E drive): It always started at max (31) no-
163 // matter what the setting in the config-file was. The added code "jiggles"
164 // the volume-control, and now it works for me :-)
165 // If this code is unacceptable, perhaps another solution is to periodically
166 // compare the cd_volume.value with an actual value _read_ from the drive.
167 // Ie. not trusting that calling the ioctl with the correct value actually
168 // sets the hardware-volume to the requested value. Right now, the ioctl
169 // is assumed to work perfectly, and the value in cd_volume.value is
170 // compared periodically with cdvolume.
171 //
172 // Updated the spec file, so an updated RPM can easily be built, with
173 // a minimum of editing. Where can I upload my pre-built (S)RPMS to ?
174 //
175 // Erling Jacobsen, linuxcub@email.dk
176 //
177 // Revision 1.6  2000/03/23 22:54:00  metzgermeister
178 // added support for HOME/.legacy under Linux
179 //
180 // Revision 1.5  2000/03/06 17:33:36  hurdler
181 // Revision 1.4  2000/03/05 17:10:56  bpereira
182 // Revision 1.3  2000/02/27 00:42:10  hurdler
183 // fix CR+LF problem
184 //
185 //
186 // DESCRIPTION:
187 //      DOOM main program (D_DoomMain) and game loop (D_DoomLoop),
188 //      plus functions to determine game mode (shareware, doom_registered),
189 //      parse command line parameters, configure game parameters (turbo),
190 //      and call the startup functions.
191 //
192 //-----------------------------------------------------------------------------
193 
194 #include "doomincl.h"
195   // MAX_WADPATH
196 
197 #ifdef __WIN32__
198 #include <direct.h>
199 #else
200 #include <unistd.h>     // for access
201 #define _MAX_PATH   MAX_WADPATH
202 #endif
203 
204 // using MAX_WADPATH as buffer limit, so _MAX_PATH must be as long
205 #if _MAX_PATH < MAX_WADPATH
206 #undef _MAX_PATH
207 #define _MAX_PATH   MAX_WADPATH
208 #endif
209 
210 
211 #include "doomstat.h"
212 
213 #include "command.h"
214 #include "console.h"
215 
216 #include "am_map.h"
217 #include "d_net.h"
218 #include "d_netcmd.h"
219 #include "dehacked.h"
220 #include "dstrings.h"
221 
222 #include "f_wipe.h"
223 #include "f_finale.h"
224 
225 #include "g_game.h"
226 #include "g_input.h"
227 
228 #include "hu_stuff.h"
229 
230 #include "i_sound.h"
231 #include "i_system.h"
232 #include "i_video.h"
233 
234 #include "m_argv.h"
235 #include "m_menu.h"
236 #include "m_misc.h"
237 #include "m_swap.h"
238 
239 #include "p_setup.h"
240 #include "p_fab.h"
241 #include "p_info.h"
242 #include "p_local.h"
243 
244 #include "r_main.h"
245 #include "r_local.h"
246 
247 #include "s_sound.h"
248 #include "st_stuff.h"
249 
250 #include "t_script.h"
251 
252 #include "v_video.h"
253 
254 #include "wi_stuff.h"
255 #include "w_wad.h"
256 
257 #include "z_zone.h"
258 #include "d_main.h"
259 #include "d_netfil.h"
260 #include "m_cheat.h"
261 #include "p_chex.h"
262 
263 #ifdef HWRENDER
264 #include "hardware/hw_main.h"
265   // 3D View Rendering
266 #endif
267 
268 #include "hardware/hw3sound.h"
269 
270 #include "b_game.h"
271   //added by AC for acbot
272 
273 #ifdef FRENCH_INLINE
274 #include "d_french.h"
275 #endif
276 
277 // Versioning
278 #ifndef SVN_REV
279 #define SVN_REV "1567"
280 #endif
281 
282 
283 // Version number: major.minor.revision
284 const int  VERSION  = 148; // major*100 + minor
285 const int  REVISION = 8;   // for bugfix releases, should not affect compatibility. has nothing to do with svn revisions.
286 static const char VERSIONSTRING[] = "(rev " SVN_REV ")";
287 //static const char VERSIONSTRING[] = "beta (rev " SVN_REV ")";
288 char VERSION_BANNER[80];
289 
290 // [WDJ] change this if legacy.wad is changed
291 const short cur_wadversion = 148;	// release wadversion
292 // usually allow one version behind cur_wadversion, for easier testing
293 // does not have to be full featured
294 const short min_wadversion = 145;
295 
296 
297 //
298 //  DEMO LOOP
299 //
300 int demosequence;
301 int pagetic;
302 static const char * pagename = "TITLEPIC";
303 
304 //  PROTOS
305 static void Help(void);
306 static void Clear_SoftError(void);
307 void Heretic_PatchEngine(void);
308 //void Chex1_PatchEngine(void);
309 
310 // Null terminated list of files.
311 char * startupwadfiles[MAX_WADFILES+1];
312 
313 // command line switches
314 boolean nomonsters;             // checkparm of -nomonsters
315 
316 boolean singletics = false;     // timedemo
317 
318 boolean nomusic;
319 boolean nosoundfx; // had clash with WATCOM i86.h nosound() function
320 
321 boolean dedicated = false;  // dedicated server
322 
323 byte    verbose = 0;
324 byte    devparm = 0;
325     // set by -devparm, plus verbose level.
326     // devparm enables development mode, with CONS messages reporting
327     // on memory usage and other significant events.
328 
329 byte    demo_ctrl;
330 byte    init_sequence = 0;
331 byte    fatal_error = 0;
332 
333 // name buffer sizes including directory and everything
334 #define FILENAME_SIZE  256
335 
336 #if defined SMIF_PC_DOS || defined __WIN32__ || defined __OS2__
337 # define  SLASH  "\\"
338 #else
339 # define  SLASH  "/"
340 #endif
341 
342 // to make savegamename and directories, in m_menu.c
343 char *legacyhome = NULL;
344 int   legacyhome_len;
345 
346 char *dirlist[] =
347   { DEFWADS01,
348 #ifdef DEFWADS02
349     DEFWADS02,
350 #endif
351 #ifdef DEFWADS03
352     DEFWADS03,
353 #endif
354 #ifdef DEFWADS04
355     DEFWADS04,
356 #endif
357 #ifdef DEFWADS05
358     DEFWADS05,
359 #endif
360 #ifdef DEFWADS06
361     DEFWADS06,
362 #endif
363 #ifdef DEFWADS07
364     DEFWADS07,
365 #endif
366 #ifdef DEFWADS08
367     DEFWADS08,
368 #endif
369 #ifdef DEFWADS09
370     DEFWADS09,
371 #endif
372 #ifdef DEFWADS10
373     DEFWADS10,
374 #endif
375 #ifdef DEFWADS11
376     DEFWADS11,
377 #endif
378 #ifdef DEFWADS12
379     DEFWADS12,
380 #endif
381 #ifdef DEFWADS13
382     DEFWADS13,
383 #endif
384 #ifdef DEFWADS14
385     DEFWADS14,
386 #endif
387 #ifdef DEFWADS15
388     DEFWADS15,
389 #endif
390 #ifdef DEFWADS16
391     DEFWADS16,
392 #endif
393 #ifdef DEFWADS17
394     DEFWADS17,
395 #endif
396 #ifdef DEFWADS18
397     DEFWADS18,
398 #endif
399 #ifdef DEFWADS19
400     DEFWADS19,
401 #endif
402 #ifdef DEFWADS20
403     DEFWADS20,
404 #endif
405 #ifdef DEFWADS21
406     DEFWADS21,
407 #endif
408   };
409 // Doomwaddir allocation:
410 // [0] = DOOMWADDIR.  (ref)
411 // [1] = reserved for dynamic use  (ref)
412 // [2] = reserved for dynamic use  (ref)
413 #define DOOMWADDIR_DIRLIST   3
414 // [3.. (MAX_NUM_DOOMWADDIR - 3)] = DEFWADSxx   (ref or malloc)
415 // [MAX_NUM_DOOMWADDIR-2] = reserved for dynamic use  (ref)
416 // [MAX_NUM_DOOMWADDIR-1] = reserved for dynamic use  (ref)
417 char * doomwaddir[MAX_NUM_DOOMWADDIR];
418 
419 static byte defdir_stat = 0;  // when defdir valid
420 static byte defdir_search = 0;  // when defdir search is reasonable
421 static char * defdir = NULL;  // default dir  (malloc)
422 static char * progdir = NULL;  // program dir  (malloc)
423 static char * progdir_wads = NULL;  // program wads directory  (malloc)
424 
425 #ifdef LAUNCHER
426 consvar_t cv_home = {"home", "", CV_HIDEN, NULL};
427 consvar_t cv_doomwaddir = {"doomwaddir", "", CV_HIDEN, NULL};
428 consvar_t cv_iwad = {"iwad", "", CV_HIDEN, NULL};
429 #endif
430 
431 
432 #if defined(__APPLE__) && defined(__MACH__)
433 // [WDJ] This is very likely only for a development setup using an .app folder
434 #ifdef EXT_MAC_DIR_SPEC
435 //[segabor]: for Mac specific resources
436 extern char mac_legacy_wad[FILENAME_SIZE];  // legacy.wad in Resources
437 extern char mac_md2_wad[FILENAME_SIZE];	    // md2.wad in Resources
438 extern char mac_user_home[FILENAME_SIZE];   // for config and savegames
439 #endif
440 #endif
441 
442 // Setup variable doomwaddir for owner usage.
owner_wad_search_order(void)443 void  owner_wad_search_order( void )
444 {
445     // Wad search order.
446     defdir_search = 0;
447     if( defdir_stat )
448     {
449         if( ( access( "Desktop", R_OK) == 0 )
450           ||( access( "Pictures", R_OK) == 0 )
451           ||( access( "Music", R_OK) == 0 ) )
452         {
453             if( verbose )
454                 GenPrintf( EMSG_ver, "Desktop, Pictures, or Music dir detected, default dir not searched.\n");
455         }
456         else
457         if( defdir
458             &&( !(strcmp( defdir, cv_home.string ) == 0) ) // not home directory
459             &&( !(progdir && (strcmp( defdir, progdir ) == 0)) ) // not program directory
460             &&( !(progdir_wads && (strcmp( defdir, progdir_wads ) == 0)) ) // not wads directory
461           )
462         {
463             defdir_search = 1;
464             // Search current dir near first, for other wad searches.
465             doomwaddir[1] = defdir;
466         }
467     }
468     // Search progdir/wads early, for other wad searches.
469     doomwaddir[2] = progdir_wads;
470     // Search last, for other wad searches.
471     doomwaddir[MAX_NUM_DOOMWADDIR-1] = progdir;
472 }
473 
474 
475 
476 //
477 // EVENT HANDLING
478 //
479 // Events are asynchronous inputs generally generated by the game user.
480 // Events can be discarded if no responder claims them
481 // referenced from i_system.c for I_GetKey()
482 
483 event_t events[MAXEVENTS];
484 int eventhead = 0;
485 int eventtail = 0;
486 
487 //
488 // D_PostEvent
489 // Called by the I/O functions when input is detected
490 //
D_PostEvent(const event_t * ev)491 void D_PostEvent(const event_t * ev)
492 {
493     events[eventhead] = *ev;
494     eventhead = (eventhead + 1) & (MAXEVENTS - 1);
495 }
496 
497 // just for lock this function
498 #ifdef SMIF_PC_DOS
D_PostEvent_end(void)499 void D_PostEvent_end(void)
500 {
501 };
502 #endif
503 
504 // Clear the input events before re-enabling play
505 static
D_Clear_Events(void)506 void D_Clear_Events( void )
507 {
508    eventhead = eventtail = 0;
509    mousex = mousey = 0;  // clear accumulated motion
510 }
511 
512 //
513 // D_Process_Events
514 // Send all the events of the given timestamp down the responder chain
515 //
D_Process_Events(void)516 void D_Process_Events(void)
517 {
518     event_t *ev;
519 
520     while (eventtail != eventhead)
521     {
522         ev = &events[eventtail];
523 
524         if (M_Responder(ev)) // Menu input
525           ;   // menu ate the event
526         else if (CON_Responder(ev)) // console input
527           ;
528         else
529           G_Responder(ev);
530 
531         eventtail++;
532         eventtail = eventtail & (MAXEVENTS - 1);
533     }
534 }
535 
536 //
537 // D_Display
538 //  draw current display, possibly wiping it from the previous
539 //
540 
541 // There is a wipe each change of the gamestate.
542 // wipegamestate can be set to GS_FORCEWIPE to force a wipe on the next draw.
543 gamestate_e wipegamestate = GS_DEMOSCREEN;
544 
545 CV_PossibleValue_t screenslink_cons_t[] = { {0, "None"}, {wipe_ColorXForm + 1, "Crossfade"}, {wipe_Melt + 1, "Melt"}, {0, NULL} };
546 consvar_t cv_screenslink = { "screenlink", "2", CV_SAVE, screenslink_cons_t };
547 
548 // Not called when dedicated.
549 static
D_Display(void)550 void D_Display(void)
551 {
552     // vid : from video setup
553     static boolean menuactivestate = false;
554     static boolean draw_refresh = false;
555     static gamestate_e oldgamestate = GS_FORCEWIPE; // invalid state
556     static int borderdrawcount = 0;
557 
558     tic_t nowtime;
559     tic_t tics;
560     tic_t wipestart;
561     int y;
562     boolean wipe_done;
563     boolean wipe;
564     boolean redrawsbar;
565 
566     if (nodrawers)
567         return; // for comparative timing / profiling
568 
569     wipe = false;
570     redrawsbar = false;
571     wipe_done = false;
572 
573     //added:21-01-98: check for change of screen size (video mode)
574     if( setmodeneeded.modetype || drawmode_recalc )
575     {
576         SCR_SetMode( 1 );  // change video mode
577         //added:26-01-98: NOTE! setsizeneeded is set by SCR_Recalc()
578         SCR_Recalc();
579           // setsizeneeded -> redrawsbar
580           // con_recalc, stbar_recalc, am_recalc
581         drawmode_recalc = false;
582     }
583 
584     // change the view size if needed
585     if (setsizeneeded)
586     {
587         R_ExecuteSetViewSize();  // set rdraw, view scale, limits, projection
588         oldgamestate = GS_FORCEWIPE;  // force background redraw
589         redrawsbar = true;
590         draw_refresh = true;
591     }
592 
593     if( rendermode_recalc )
594     {
595         if( gamestate == GS_LEVEL )
596         {
597 //            R_FillBackScreen();
598             R_Setup_Drawmode();
599             draw_refresh = true;
600             oldgamestate = GS_FORCEWIPE;  // force background redraw
601 #ifdef HWRENDER
602             if( rendermode != render_soft )
603             {
604                 // Hardware draw only
605                 HWR_SetupLevel();
606                 HWR_Preload_Graphics();
607             }
608 #endif
609         }
610     }
611 
612     // save the current screen if about to wipe
613     if (gamestate != wipegamestate && rendermode == render_soft)
614     {
615         wipe = true;
616         wipe_StartScreen();
617     }
618 
619     // draw buffered stuff to screen
620     // BP: Used only by linux GGI version
621     I_UpdateNoBlit();
622 
623     // do buffered drawing
624     switch (gamestate)
625     {
626         case GS_LEVEL:
627             if (!gametic)
628                 break;
629 
630             // On each gametic
631             HU_Erase();
632             if (automapactive)
633                 AM_Drawer();
634 
635             if (wipe || menuactivestate
636 #ifdef HWRENDER
637                 || rendermode != render_soft
638 #endif
639                 || vid.recalc)
640             {
641                 redrawsbar = true;
642             }
643             break;
644 
645         case GS_INTERMISSION:
646             WI_Drawer();
647             break;
648 
649         case GS_FINALE:
650             F_Drawer();
651             break;
652 
653         case GS_DEDICATEDSERVER:
654         case GS_DEMOSCREEN:
655             D_PageDrawer(pagename);
656             break;
657 
658         case GS_WAITINGPLAYERS:
659             // [WDJ] Because hardware may double buffer, need to overwrite
660             // background. Waiting loop may get alternating backgrounds.
661             D_PageDrawer(pagename);  // provide background
662             D_WaitPlayer_Drawer();
663             break;
664 
665         case GS_NULL:
666         default:
667             break;
668     }
669 
670     if (gamestate == GS_LEVEL)
671     {
672         if (oldgamestate != GS_LEVEL)
673         {
674             // Level map play display initialize
675             R_FillBackScreen(); // draw the pattern into the back screen
676             // the border needs to be initially drawn
677             draw_refresh = true;
678         }
679 
680         // Level map play display
681         // draw the view directly
682         if (!automapactive)
683         {
684             // see if the border needs to be updated to the screen
685             if( rdraw_scaledviewwidth != vid.width )
686             {
687                 // the menu may draw over parts out of the view window,
688                 // which are refreshed only when needed
689                 if( menuactive || menuactivestate || draw_refresh )
690                     borderdrawcount = 3;
691 
692                 if( borderdrawcount )
693                 {
694                     borderdrawcount--;
695                     R_DrawViewBorder();     // erase old menu stuff
696                 }
697             }
698 
699             if (displayplayer_ptr->mo)
700             {
701 #ifdef CLIENTPREDICTION2
702                 displayplayer_ptr->mo->flags2 |= MF2_DONTDRAW;
703 #endif
704 #ifdef HWRENDER
705                 if (rendermode != render_soft)
706                     HWR_RenderPlayerView(0, displayplayer_ptr);
707                 else    //if (rendermode == render_soft)
708 #endif
709                     R_RenderPlayerView(0, displayplayer_ptr);
710 #ifdef CLIENTPREDICTION2
711                 displayplayer_ptr->mo->flags2 &= ~MF2_DONTDRAW;
712 #endif
713             }
714 
715             // added 16-6-98: render the second screen
716             if ( displayplayer2_ptr && displayplayer2_ptr->mo)
717             {
718 #ifdef CLIENTPREDICTION2
719                 displayplayer2_ptr->mo->flags2 |= MF2_DONTDRAW;
720 #endif
721 #ifdef HWRENDER
722                 if (rendermode != render_soft)
723                     HWR_RenderPlayerView(1, displayplayer2_ptr);
724                 else
725 #endif
726                 {
727                     // Alter the draw tables to draw into second player window
728                     //faB: Boris hack :P !!
729                     viewwindowy = vid.height / 2;
730                     memcpy(ylookup, ylookup2, rdraw_viewheight * sizeof(ylookup[0]));
731 
732                     R_RenderPlayerView(1, displayplayer2_ptr);
733 
734                     // Restore first player tables
735                     viewwindowy = 0;
736                     memcpy(ylookup, ylookup1, rdraw_viewheight * sizeof(ylookup[0]));
737                 }
738 #ifdef CLIENTPREDICTION2
739                 displayplayer2_ptr->mo->flags2 &= ~MF2_DONTDRAW;
740 #endif
741             }
742         }
743 
744         HU_Drawer();
745 
746         ST_Drawer(redrawsbar);
747 
748         draw_refresh = false;
749     }
750     else
751     {
752         // not GS_LEVEL
753         // change gamma if needed
754         if( gamestate != oldgamestate )
755             V_SetPalette(0);
756     }
757 
758     menuactivestate = menuactive;
759     oldgamestate = wipegamestate = gamestate;
760 
761     // draw pause pic
762     if (paused && (!menuactive || netgame))
763     {
764         patch_t *patch;
765         if (automapactive)
766             y = 4;
767         else
768             y = viewwindowy + 4;
769         patch = W_CachePatchName("M_PAUSE", PU_CACHE);  // endian fix
770         // 0
771         V_SetupDraw( 0 | V_SCALEPATCH | V_SCALESTART );
772         V_DrawScaledPatch(viewwindowx + (BASEVIDWIDTH - patch->width) / 2, y, patch);
773     }
774 
775     //added:24-01-98:vid size change is now finished if it was on...
776     vid.recalc = 0;
777     rendermode_recalc = false;
778 
779     // Exl: draw a faded background
780     if( fs_fadealpha != 0 )
781     {
782 #ifdef HWRENDER
783         if( rendermode != render_soft)
784         {
785             HWR_FadeScreenMenuBack(fs_fadecolor, fs_fadealpha, vid.height);
786         }
787         else
788 #endif
789         {
790             // Fade for software draw.
791             V_FadeRect(0, vid.width, vid.height, (0xFF - fs_fadealpha),
792                        (fs_fadealpha * 32 / 0x100), fs_fadecolor );
793         }
794     }
795 
796         //FIXME: draw either console or menu, not the two
797     CON_Drawer();
798 
799     M_Drawer(); // menu is drawn even on top of everything
800     NetUpdate();        // send out any new accumulation
801 
802 //
803 // normal update
804 //
805     if (!wipe)
806     {
807         if (cv_netstat.value)
808         {
809             char s[50];
810             Net_GetNetStat();
811             sprintf(s, "recv %d b/s", netstat_recv_bps);
812             V_DrawString(BASEVIDWIDTH - V_StringWidth(s), BASEVIDHEIGHT - ST_HEIGHT - 40, V_WHITEMAP, s);
813             sprintf(s, "send %d b/s", netstat_send_bps);
814             V_DrawString(BASEVIDWIDTH - V_StringWidth(s), BASEVIDHEIGHT - ST_HEIGHT - 30, V_WHITEMAP, s);
815             sprintf(s, "GameMiss %.2f%%", netstat_gamelost_percent);
816             V_DrawString(BASEVIDWIDTH - V_StringWidth(s), BASEVIDHEIGHT - ST_HEIGHT - 20, V_WHITEMAP, s);
817             sprintf(s, "SysMiss %.2f%%", netstat_lost_percent);
818             V_DrawString(BASEVIDWIDTH - V_StringWidth(s), BASEVIDHEIGHT - ST_HEIGHT - 10, V_WHITEMAP, s);
819         }
820 
821 #ifdef TILTVIEW
822         //added:12-02-98: tilt view when marine dies... just for fun
823         if (gamestate == GS_LEVEL && cv_tiltview.value
824             && displayplayer_ptr->playerstate == PST_DEAD)
825         {
826             V_DrawTiltView(screens[0]);
827         }
828         else
829 #endif
830 #ifdef PERSPCORRECT
831         if (gamestate == GS_LEVEL && cv_perspcorr.value
832            && rendermode == render_soft )
833         {
834             V_DrawPerspView(screens[0], displayplayer_ptr->aiming);
835         }
836         else
837 #endif
838         {
839             //I_BeginProfile();
840             // display a graph of ticrate
841             if (cv_ticrate.value )
842                 V_Draw_ticrate_graph();
843             I_FinishUpdate();   // page flip or blit buffer
844             //debug_Printf("last frame update took %d\n", I_EndProfile());
845         }
846         return;
847     }
848 
849 //
850 // wipe update
851 //
852     if (!cv_screenslink.value)
853         return;
854 
855     wipe_EndScreen();
856 
857     wipestart = I_GetTime() - 1;
858     y = wipestart + 2 * TICRATE;        // init a timeout
859     do
860     {
861         do
862         {
863             nowtime = I_GetTime();
864             tics = nowtime - wipestart;
865         } while (!tics);
866         wipestart = nowtime;
867         wipe_done = wipe_ScreenWipe(cv_screenslink.value - 1, tics);
868         I_OsPolling();
869         I_UpdateNoBlit();
870         M_Drawer();     // menu is drawn even on top of wipes
871         I_FinishUpdate();       // page flip or blit buffer
872     } while (!wipe_done && I_GetTime() < (unsigned) y);
873 
874     ST_Invalidate();
875 }
876 
877 // =========================================================================
878 //   D_DoomLoop
879 // =========================================================================
880 
881 tic_t rendergametic;  // The last gametic that was rendered.
882 #ifdef CLIENTPREDICTION2
883 boolean spirit_update;
884 #endif
885 
886 //#define SAVECPU_EXPERIMENTAL
887 
888 // Called by port main program.
D_DoomLoop(void)889 void D_DoomLoop(void)
890 {
891     char acbuf[_MAX_PATH ];
892     tic_t oldentertics, entertic, realtics, rendertimeout = -1;
893 
894     if (demorecording)
895         G_BeginRecording();
896 
897     // [WDJ] DoomLegacy may be installed local or in system directory.
898     // Feature Request by Leonardo Montenegro.
899     // There may be a local autoexec, and/or a system autoexec.
900     // Standard: The local file is preferred, and can chain to the system file when preferable.
901     cat_filename( acbuf, legacyhome, "autoexec.cfg");  // local file in doomlegacy home
902     if( access( acbuf, R_OK) == 0 )
903     {
904         // user settings
905         GenPrintf( EMSG_ver, "Exec Local autoexec: %s\n", acbuf );
906         COM_BufAddText( va( "exec %s\n", acbuf) );
907     }
908     else if( access( "autoexec.cfg", R_OK) == 0 )  // file with executable
909     {
910         // file with executable, may be system settings
911         GenPrintf( EMSG_ver, "Exec System autoexec\n" );
912         COM_BufAddText("exec autoexec.cfg\n");
913     }
914 
915     // end of loading screen: CONS_Printf() will no more call FinishUpdate()
916     con_self_refresh = false;
917 
918     oldentertics = I_GetTime();
919 
920     // make sure to do a d_display to init mode _before_ load a level
921     if( setmodeneeded.modetype || drawmode_recalc )
922     {
923         // This may also execute accumulated commands.
924         SCR_SetMode( 1 );      // change video mode
925         SCR_Recalc();
926         drawmode_recalc = false;
927     }
928     if( rendermode_recalc )
929     {
930         I_Rendermode_setup();
931         rendermode_recalc = 0;
932     }
933 
934     D_Clear_Events();  // clear input events to prevent startup jerks,
935                          // motion during screen wipe still gets through
936 
937     // Execute accumulated commands.
938     COM_BufExecute( CFG_none );
939 
940     while (1)
941     {
942         // get real tics
943         entertic = I_GetTime();
944         realtics = entertic - oldentertics;
945         oldentertics = entertic;
946 
947 #ifdef SAVECPU_EXPERIMENTAL
948         if (realtics == 0)
949         {
950             I_Sleep(10);
951             continue;
952         }
953 #endif
954 
955         // frame synchronous IO operations
956         // UNUSED for the moment (18/12/98)
957         I_StartFrame();
958 
959 #ifdef HW3SOUND
960         if( ! dedicated )
961         {
962             HW3S_BeginFrameUpdate();
963         }
964 #endif
965 
966         // process tics (but maybe not if realtic==0)
967         TryRunTics(realtics);
968 #ifdef CLIENTPREDICTION2
969         if (singletics || spirit_update)
970 #else
971         if (singletics || gametic > rendergametic)
972 #endif
973         {
974             rendergametic = gametic;
975             rendertimeout = entertic + TICRATE / 17;
976 
977             if( ! dedicated )
978             {
979                 //added:16-01-98:consoleplayer -> displayplayer (hear sounds from viewpoint)
980                 S_UpdateSounds();   // move positional sounds
981                 // Update display, next frame, with current state.
982                 D_Display();
983             }
984 #ifdef CLIENTPREDICTION2
985             spirit_update = false;
986 #endif
987         }
988         else if (rendertimeout < entertic)      // in case the server hang or netsplit
989         {
990             if( ! dedicated )
991             {
992                 D_Display();
993             }
994         }
995 
996         if( ! dedicated )
997         {
998             //Other implementations might need to update the sound here.
999 #ifndef SNDSERV
1000             // Sound mixing for the buffer is snychronous.
1001             I_UpdateSound();
1002 #endif
1003             // Synchronous sound output is explicitly called.
1004 #ifndef SNDINTR
1005             // Update sound output.
1006             I_SubmitSound();
1007 #endif
1008 
1009 #ifdef CDMUS
1010             // check for media change, loop music..
1011             I_UpdateCD();
1012 #endif
1013 
1014 #ifdef HW3SOUND
1015             HW3S_EndFrameUpdate();
1016 #endif
1017         }
1018     }
1019 }
1020 
1021 // =========================================================================
1022 //  Demo
1023 // =========================================================================
1024 
1025 //
1026 // D_PageTicker
1027 // Handles timing for warped projection
1028 //
D_PageTicker(void)1029 void D_PageTicker(void)
1030 {
1031     if (--pagetic < 0)
1032         D_AdvanceDemo();
1033 }
1034 
1035 //
1036 // D_PageDrawer : draw a patch supposed to fill the screen,
1037 //                fill the borders with a background pattern (a flat)
1038 //                if the patch doesn't fit all the screen.
1039 //
D_PageDrawer(const char * lumpname)1040 void D_PageDrawer(const char *lumpname)
1041 {
1042     int x, y;
1043     byte *src;
1044     byte *dest;  // within screen buffer
1045 
1046     // [WDJ] Draw patch for all bpp, bytepp, and padded lines.
1047     V_SetupDraw( 0 | V_SCALESTART | V_SCALEPATCH | V_CENTERHORZ );
1048 
1049     // software mode which uses generally lower resolutions doesn't look
1050     // good when the pic is scaled, so it fills space around with a pattern,
1051     // and the pic is only scaled to integer multiples (x2, x3...)
1052     if (rendermode == render_soft)
1053     {
1054         if ((vid.width > BASEVIDWIDTH) || (vid.height > BASEVIDHEIGHT))
1055         {
1056             src = scr_borderflat;
1057             dest = screens[0];
1058 
1059             for (y = 0; y < vid.height; y++)
1060             {
1061                 // repeatly draw a 64 pixel wide flat
1062                 dest = screens[0] + (y * vid.ybytes);  // within screen buffer
1063                 for (x = 0; x < vid.width / 64; x++)
1064                 {
1065                     V_DrawPixels( dest, 0, 64, &src[(y & 63) << 6]);
1066                     dest += (64 * vid.bytepp);
1067                 }
1068                 if (vid.width & 63)
1069                 {
1070                     V_DrawPixels( dest, 0, (vid.width & 63), &src[(y & 63) << 6]);
1071                 }
1072             }
1073         }
1074     }
1075     // big hack for legacy's credits
1076     if (EN_heretic_hexen && (demosequence != 2))
1077     {
1078         V_DrawRawScreen_Num(0, 0, W_GetNumForName(lumpname), 320, 200);
1079         if (demosequence == 0 && pagetic <= 140)
1080             V_DrawScaledPatch_Name(4, 160, "ADVISOR" );
1081     }
1082     else
1083     {
1084         V_DrawScaledPatch_Name(0, 0, lumpname );
1085     }
1086 }
1087 
1088 
1089 //
1090 // D_AdvanceDemo
1091 // Called after each demo or intro demosequence finishes
1092 //
1093 // Called by D_StartTitle, with init of demosequence
1094 // Called by D_PageTicker, when gamestate == GS_DEMOSCREEN
1095 // Called by G_CheckDemoStatus when timing or playing a demo
D_AdvanceDemo(void)1096 void D_AdvanceDemo(void)
1097 {
1098     // [WDJ] do not start a demo when a menu or console is open
1099     if( !(demo_ctrl & DEMO_seq_disabled) && ! menuactive
1100         && ! console_open )
1101         demo_ctrl = DEMO_seq_advance;    // flag to trigger D_DoAdvanceDemo
1102 }
1103 
1104 //
1105 // This cycles through the demo sequences.
1106 // FIXME - version dependent demo numbers?
1107 //
1108 // Called by TryRunTics when demo_ctrl == DEMO_seq_advance
D_DoAdvanceDemo(void)1109 void D_DoAdvanceDemo(void)
1110 {
1111     const char * demo_name;
1112 
1113     demo_ctrl = 0;  // cancel DEMO_seq_advance
1114     players[consoleplayer].playerstate = PST_LIVE;      // not reborn
1115     gameaction = ga_nothing;
1116 
1117     if (gamemode == ultdoom_retail)
1118         demosequence = (demosequence + 1) % 7;
1119     else
1120         demosequence = (demosequence + 1) % 6;
1121 
1122     switch (demosequence)
1123     {
1124         case 0:
1125             pagename = "TITLEPIC";
1126             switch (gamemode)
1127             {
1128                 case hexen:
1129                 case heretic:
1130                     pagetic = 210 + 140;
1131                     pagename = "TITLE";
1132                     S_StartMusic(mus_htitl);
1133                     break;
1134                 case doom2_commercial:
1135                     pagetic = TICRATE * 11;
1136                     S_StartMusic(mus_dm2ttl);
1137                     break;
1138                 default:
1139                     pagetic = 170;
1140                     S_StartMusic(mus_intro);
1141                     break;
1142             }
1143             gamestate = GS_DEMOSCREEN;
1144             break;
1145         case 1:
1146             demo_name = "demo1";
1147             goto playdemo;
1148         case 2:
1149             pagetic = 200;
1150             gamestate = GS_DEMOSCREEN;
1151             pagename = "CREDIT";
1152             break;
1153         case 3:
1154             demo_name = "demo2";
1155             goto playdemo;
1156         case 4:
1157             gamestate = GS_DEMOSCREEN;
1158             if (gamemode == doom2_commercial)
1159             {
1160                 pagetic = TICRATE * 11;
1161                 pagename = "TITLEPIC";
1162                 S_StartMusic(mus_dm2ttl);
1163             }
1164             else if (gamemode == heretic)
1165             {
1166                 pagetic = 200;
1167                 if( ! VALID_LUMP( W_CheckNumForName("e2m1") ) )
1168                     pagename = "ORDER";
1169                 else
1170                     pagename = "CREDIT";
1171             }
1172             else
1173             {
1174                 pagetic = 200;
1175 
1176                 if (gamemode == ultdoom_retail)
1177                     pagename = text[CREDIT_NUM];
1178                 else
1179                     pagename = text[HELP2_NUM];
1180             }
1181             break;
1182         case 5:
1183             demo_name = "demo3";
1184             goto playdemo;
1185             // THE DEFINITIVE DOOM Special Edition demo
1186         case 6:
1187             demo_name = "demo4";
1188             goto playdemo;
1189     }
1190     return;
1191 
1192  playdemo:
1193     G_DeferedPlayDemo( demo_name );
1194     demo_ctrl = DEMO_seq_playdemo;  // demo started here (not console)
1195     pagetic = 9999999;
1196     return;
1197 }
1198 
1199 // Disable demos
1200 // Called when load game or init new game
D_DisableDemo(void)1201 void D_DisableDemo(void)
1202 {
1203     if( demoplayback )
1204         G_StopDemo();
1205     // stop DEMO_seq_advance, but preserve DEMO_seq_playdemo so can abort it
1206     demo_ctrl = (demo_ctrl & DEMO_seq_playdemo) | DEMO_seq_disabled;
1207 }
1208 
1209 // =========================================================================
1210 
1211 //
1212 // D_StartTitle
1213 //
1214 // Called by D_DoomMain(), when not server and not starting game
1215 // Called by Command_ExitGame_f
1216 // Called by CL_ConnectToServer when cannot join server, or aborting
1217 // Called by Got_KickCmd, when player kicked from game
1218 // Called by Net_Packet_Handler, upon server shutdown, timeout, or refused (NACK)
1219 // Called by G_InitNew, when aborting game because cannot downgrade
1220 // Called by M_Responder and M_Setup_prevMenu, when exiting menu and not playing game
D_StartTitle(void)1221 void D_StartTitle(void)
1222 {
1223     D_End_commandline();
1224 
1225     gameaction = ga_nothing;
1226     playerdeadview = false;
1227     displayplayer = consoleplayer = statusbarplayer = 0;
1228     displayplayer_ptr = consoleplayer_ptr = &players[0]; // [WDJ]
1229     paused = 0;
1230     demo_ctrl = 0;  // enable screens and seq demos
1231     demosequence = -1;
1232     CON_ToggleOff();
1233     D_AdvanceDemo();
1234 }
1235 
1236 // End commandline game setup.
D_End_commandline(void)1237 void D_End_commandline( void )
1238 {
1239     // Does not affect video settings (CS_EV_PROT)
1240     if( command_EV_param )
1241         CV_Restore_User_Settings();  // remove temp settings
1242 }
1243 
1244 
1245 
1246 // =========================================================================
1247 //   D_DoomMain
1248 // =========================================================================
1249 
1250 
1251 // Print out the search directories for verbose, and error.
1252 //   emf: EMSG_ver, EMSG_error, EMSG_warn
1253 //   enables: 0x01 legacy.wad order
1254 //            0x02 IWAD order
1255 //            0x0F verbose all
1256 static
Print_search_directories(byte emf,byte enables)1257 void  Print_search_directories( byte emf, byte enables )
1258 {
1259     int wdi;
1260     GenPrintf(emf, "Search directories:\n");
1261     // Extra legacy.wad search, and verbose.
1262     if( (enables&0x01) && progdir )
1263         GenPrintf(emf, " progdir: %s\n", progdir );
1264     // Verbose only. For IWAD or legacy.wad they are in doomwaddir entries.
1265     if( (enables==0x0F) && progdir_wads )
1266         GenPrintf(emf, "        : %s\n", progdir_wads );
1267     if( (enables==0x0F) && defdir && defdir_search )
1268         GenPrintf(emf, " defdir: %s\n", defdir );
1269 #ifdef LEGACYWADDIR
1270     GenPrintf(emf, " LEGACYWADDIR: %s\n", LEGACYWADDIR );
1271 #endif
1272     for( wdi=0; wdi<MAX_NUM_DOOMWADDIR; wdi++ )
1273     {
1274         if( doomwaddir[wdi] )
1275             GenPrintf(emf, " Doomwaddir[%i]: %s\n", wdi, doomwaddir[wdi] );
1276     }
1277 }
1278 
1279 
1280 
1281 //
1282 // D_AddFile
1283 //
1284 static
D_AddFile(const char * filename)1285 void D_AddFile(const char *filename)
1286 {
1287     int numwadfiles;
1288     char *newfile;
1289 
1290     if( filename == NULL )
1291         return;
1292 
1293     // find end of wad files by counting
1294     for (numwadfiles = 0; startupwadfiles[numwadfiles]; numwadfiles++)
1295         ;
1296     if( numwadfiles >= MAX_WADFILES )
1297         I_Error ( "Too many wadfiles, max=%i.\n", MAX_WADFILES );
1298 
1299     newfile = malloc(strlen(filename) + 1);
1300     strcpy(newfile, filename);
1301 
1302     startupwadfiles[numwadfiles] = newfile;
1303 }
1304 
1305 
1306 #ifdef LAUNCHER
D_Clear_Files(void)1307 static void D_Clear_Files( void )
1308 {
1309     int i;
1310     for (i = 0; startupwadfiles[i]; i++)
1311     {
1312         free( startupwadfiles[i] );
1313         startupwadfiles[i] = NULL;
1314     }
1315 }
1316 #endif
1317 
1318 
1319 // ==========================================================================
1320 // Identify the Doom version, and IWAD file to use.
1321 // Sets 'gamemode' to determine whether doom_registered/doom2_commercial
1322 // features are available (notable loading PWAD files).
1323 // ==========================================================================
1324 
1325 // [WDJ] Title and file names used in GDESC_other table entry, and other uses.
1326 #define DESCNAME_SIZE	 64
1327 char other_iwad_filename[ DESCNAME_SIZE ];
1328 char other_gname[ DESCNAME_SIZE ];
1329 char public_title[] = "Public DOOM";
1330 
1331 game_desc_e     gamedesc_id;     // unique game id
1332 game_desc_t     gamedesc;	 // active desc
1333 
1334 // [WDJ] List of standard lump names to be checked, that appear in many
1335 // game wads.  The game_desc_table also has a list of names that
1336 // are unique to that game wad.
1337 // This list sets a byte value of which are found.
1338 // Each table entry will have required and reject bit masks.
1339 
1340 #define COMMON_LUMP_LIST_SIZE   4
1341 enum lumpname_e {
1342    LN_MAP01 =0x01, LN_E1M1 =0x02, LN_E2M2 =0x04, LN_TITLE =0x08
1343 };
1344 const char * common_lump_names[ COMMON_LUMP_LIST_SIZE ] =
1345 {
1346    "MAP01", "E1M1", "E2M2", "TITLE"
1347 };
1348 
1349 // [WDJ] The gname (first) is used to recognize save games, so don't change it.
1350 // Some of the lump check information was obtained from ZDoom docs.
1351 // The startup_title will be centered on the Title page.
1352 // Switch names (idstr) need to be kept to 8 chars, all lowercase, so they
1353 // can be used in file names on all systems.
1354 // This table is the game search order.
1355 // The first entry matching all characteristics will be used !
1356 #define  NUM_GDESC   (GDESC_other+1)	// number of entries in game_desc_table
1357 game_desc_t  game_desc_table[ NUM_GDESC ] =
1358 {
1359 // Free wads should get their own gamemode identity
1360 // GDESC_freedoom: FreeDoom project, DoomII replacement
1361 //  freedoom : prboom-plus, edge
1362 //  freedoom2 : edge, chocolate
1363    { "FreeDoom", NULL, "freedoom",
1364         {"freedoom2.wad", "freedoom.wad","fdoom2.wad"}, NULL,
1365         {"FREEDOOM", NULL}, LN_MAP01, 0,
1366         0, GDESC_freedoom, doom2_commercial },
1367 // doom2f: french version Doom2, eternity.
1368 //         prboom-plus
1369 
1370 // GDESC_freedm: FreeDM project, DoomII deathmatch
1371 //  freedm : edge, chocolate
1372    { "FreeDM", NULL, "freedm",
1373         {"freedm.wad","fdoomdm.wad",NULL}, NULL,
1374         {"FREEDOOM", "FREEDM"}, LN_MAP01, 0,
1375         0, GDESC_freedm, doom2_commercial },
1376 // GDESC_doom2: doom2wad
1377    { "Doom2", "DOOM 2: Hell on Earth", "doom2",
1378         {"doom2.wad",NULL,NULL}, NULL,
1379         {NULL, NULL}, LN_MAP01, LN_TITLE,
1380         GD_idwad, GDESC_doom2, doom2_commercial },
1381 // GDESC_freedoom_ultimate: FreeDoom project, Ultimate Doom replacement
1382 //  freedoom1 : edge, chocolate
1383    { "Ultimate FreeDoom", NULL, "freedu",
1384         {"freedoom1.wad", "freedu.wad","fdoomu.wad"}, NULL,
1385         {"FREEDOOM", "E4M1"}, LN_E1M1+LN_E2M2, 0,
1386         0, GDESC_freedoom_ultimate, ultdoom_retail },
1387 // GDESC_ultimate: Doom1 1995, doomuwad
1388 //                 Doom1 1995 on floppy (doom_se.wad)
1389    { "Ultimate Doom", "The Ultimate DOOM", "doomu",
1390         {"doomu.wad","doom_se.wad","doom.wad"}, NULL,
1391         {"E4M1", NULL}, LN_E1M1+LN_E2M2, LN_TITLE,
1392         GD_idwad, GDESC_ultimate, ultdoom_retail },
1393 // GDESC_doom: DoomI 1994, doomwad
1394    { "Doom", "DOOM Registered", "doom",
1395         {"doom.wad",NULL,NULL}, NULL,
1396         {"E3M9", NULL}, LN_E1M1+LN_E2M2, LN_TITLE,
1397         GD_idwad, GDESC_doom, doom_registered },
1398 // GDESC_doom_shareware: DoomI shareware, doom1wad
1399 //  doom1 : edge, chocolate,
1400    { "Doom shareware", "DOOM Shareware", "doom1",
1401         {"doom1.wad","doom.wad",NULL}, NULL,
1402         {NULL, NULL}, LN_E1M1, LN_TITLE,
1403         GD_idwad, GDESC_doom_shareware, doom_shareware },
1404 // GDESC_plutonia: FinalDoom : Plutonia, DoomII engine
1405    { "Plutonia", "DOOM 2: Plutonia Experiment", "plutonia",
1406         {"plutonia.wad",NULL,NULL}, NULL,
1407         {"CAMO1", NULL}, LN_MAP01, LN_TITLE,
1408         GD_idwad, GDESC_plutonia, doom2_commercial },
1409 // GDESC_tnt: FinalDoom : Tnt Evilution, DoomII engine
1410    { "Tnt Evilution", "DOOM 2: TNT - Evilution", "tnt",
1411         {"tnt.wad",NULL,NULL}, NULL,
1412         {"REDTNT2", NULL}, LN_MAP01, LN_TITLE,
1413         GD_idwad, GDESC_tnt, doom2_commercial },
1414 // GDESC_blasphemer: FreeDoom project, DoomII replacement
1415    { "Blasphemer", NULL, "blasphem",
1416         {"BLASPHEM.WAD","blasphem.wad","heretic.wad"}, NULL,
1417         {"BLASPHEM", NULL}, LN_E1M1+LN_TITLE, 0,
1418         0, GDESC_blasphemer, heretic },
1419 // GDESC_heretic: Heretic
1420    { "Heretic", NULL, "heretic",
1421         {"heretic.wad",NULL,NULL}, NULL,
1422         {NULL, NULL}, LN_E1M1+LN_E2M2+LN_TITLE, 0,
1423         GD_idwad, GDESC_heretic, heretic },
1424 // GDESC_heretic_shareware: Heretic shareware
1425 //  heretic1 : chocolate
1426    { "Heretic shareware", NULL, "heretic1",
1427         {"heretic1.wad","heretic.wad",NULL}, NULL,
1428         {NULL, NULL}, LN_E1M1+LN_TITLE, LN_E2M2,
1429         GD_idwad, GDESC_heretic_shareware, heretic },
1430 // GDESC_hexen: Hexen
1431    { "Hexen", NULL, "hexen",
1432         {"hexen.wad",NULL,NULL}, NULL,
1433         {"MAP40", NULL}, LN_MAP01+LN_TITLE, 0,
1434         GD_idwad|GD_unsupported, GDESC_hexen, hexen },
1435 // GDESC_hexen_demo: Hexen
1436    { "Hexen Demo", NULL, "hexen1",
1437         {"hexen1.wad","hexen.wad",NULL}, NULL,
1438         {NULL, NULL}, LN_MAP01+LN_TITLE, 0,
1439         GD_idwad|GD_unsupported, GDESC_hexen_demo, hexen },
1440 // GDESC_strife: Strife
1441    { "Strife", NULL, "strife",
1442         {"strife.wad",NULL,NULL}, NULL,
1443         {"ENDSTRF", "MAP20"}, LN_MAP01, 0,
1444         GD_idwad|GD_unsupported, GDESC_strife, strife },
1445 // GDESC_strife_shareware: Strife shareware
1446    { "Strife shareware", NULL, "strife0",
1447         {"strife0.wad","strife.wad",NULL}, NULL,
1448         {"ENDSTRF", NULL}, 0, LN_MAP01,
1449         GD_idwad|GD_unsupported, GDESC_strife_shareware, strife },
1450 // GDESC_chex1: Chex Quest
1451    { "Chex Quest", NULL, "chex1",
1452         {"chex1.wad","chex.wad",NULL}, NULL,
1453         {"W94_1", "POSSH0M0"}, LN_E1M1, LN_TITLE,
1454         GD_iwad_pref, GDESC_chex1, chexquest1 },
1455 // GDESC_ultimate_mode: Ultimate Doom replacement
1456    { "Ultimate mode", NULL, "ultimode",
1457         {"doomu.wad","doom.wad",NULL}, NULL,
1458         { NULL, NULL}, LN_E1M1, 0,
1459         0, GDESC_ultimate, ultdoom_retail },
1460 // GDESC_doom_mode: DoomI replacement
1461    { "Doom mode", NULL, "doommode",
1462         {"doom1.wad","doom.wad",NULL}, NULL,
1463         { NULL, NULL}, LN_E1M1, 0,
1464         0, GDESC_doom_mode, doom_registered },
1465 // GDESC_heretic_mode: Heretic replacement
1466    { "Heretic mode", NULL, "heremode",
1467         {"heretic.wad",NULL,NULL}, NULL,
1468         { NULL, NULL}, LN_E1M1, 0,
1469         0, GDESC_heretic_mode, heretic },
1470 // GDESC_hexen_mode: Hexen replacement
1471    { "Hexen mode", NULL, "hexemode",
1472         {"hexen.wad",NULL,NULL}, NULL,
1473         { NULL, NULL}, LN_MAP01, 0,
1474         GD_unsupported, GDESC_hexen_mode, hexen },
1475 // GDESC_other: Other iwads, all DoomII features enabled,
1476 // strings are ptrs to buffers
1477    { other_gname, public_title, "",
1478         {other_iwad_filename,NULL,NULL}, NULL,
1479         { NULL, NULL}, LN_MAP01, 0,
1480         GD_iwad_pref, GDESC_other, doom2_commercial }
1481 };
1482 
1483 #ifdef LAUNCHER
1484 // Lookup game by table index
D_GameDesc(int i)1485 game_desc_t *  D_GameDesc( int i )
1486 {
1487     if ( i<0 || i > NUM_GDESC-1 )   return NULL;
1488     return  &game_desc_table[i];
1489 }
1490 #endif
1491 
1492 
1493 // Check all lump names in lumpnames list, count is limited to 8
1494 // Return byte has a bit set for each lumpname found.
1495 static
Check_lumps(const char * wadname,const char * lumpnames[],int count)1496 byte  Check_lumps( const char * wadname, const char * lumpnames[], int count )
1497 {
1498 #ifdef ZIPWAD
1499     byte  zhand;
1500 #else
1501     FILE * 	wadfile;
1502 #endif
1503     const char * reason;
1504     wadinfo_t   header;
1505     filelump_t  lumpx;
1506     int         hli, lc, bc;
1507     byte        result = 0;
1508 
1509     // This routine checks the directory, using the system file cache
1510     // instead of making an internal lumps directory.
1511     // Speed is not required here, so no extra speedup checks.
1512 
1513     // List may be fixed length, with NULL entries.
1514     while( count>0 && (lumpnames[count-1] == NULL))
1515     {
1516        count--;    // Reduce count by the number of NULL entries on end.
1517        // It is easier to consider a NULL as always found.
1518        result |= 1<<count;
1519     }
1520     if( count == 0 )  goto ret_result;  // escape NULL list
1521 
1522     // Read the wad file header and get directory
1523 #ifdef ZIPWAD
1524     // This handles normal files, and zip archive files.
1525     zhand = WZ_open( wadname );
1526     if( zhand == 0 )
1527         goto open_err;
1528 
1529     bc = WZ_read( zhand, sizeof(header), /*OUT*/ (byte*)&header );
1530     if( bc < sizeof(header) )
1531         goto read_err;
1532 #else
1533     wadfile = fopen( wadname, "rb" );
1534     if( wadfile == NULL )
1535         goto open_err;
1536 
1537     fread( &header, sizeof(header), 1, wadfile);
1538 #endif
1539 
1540     // check for IWAD or PWAD
1541     if( strncmp(header.identification+1,"WAD",3) != 0 )
1542         goto not_a_wad;
1543 
1544     // find directory
1545     header.numlumps = LE_SWAP32(header.numlumps);
1546     header.infotableofs = LE_SWAP32(header.infotableofs);
1547 
1548 #ifdef ZIPWAD
1549     bc = WZ_seek( zhand, header.infotableofs );
1550 #else
1551     bc = fseek( wadfile, header.infotableofs, SEEK_SET );
1552 #endif
1553     if( bc < 0 )
1554         goto read_err;
1555 
1556     // Check the directory as it is read out of the system file cache.
1557     for( hli=0; hli<header.numlumps; hli++ )
1558     {
1559 #ifdef ZIPWAD
1560         bc = WZ_read( zhand, sizeof(lumpx), /*OUT*/ (byte*)&lumpx );
1561         if( bc < sizeof(lumpx) )
1562             goto read_err;
1563 #else
1564         bc = fread( &lumpx, sizeof(lumpx), 1, wadfile );
1565         if( bc < 1 )
1566             goto read_err;
1567 #endif
1568 
1569 #ifdef DETECT_TITLES
1570         if( strncasecmp( lumpx.name, "TITLE", 5 ) == 0 )
1571         {
1572             printf( "Lump=%8s : ", lumpx.name );
1573             for( lc=0; lc<count; lc++ )
1574             {
1575                int cmp = strncasecmp( lumpx.name, lumpname[lc], 8 );
1576                printf( "  %c %8s", ( (cmp<0)?'<': (cmp>0)? '>' :'='), lumpname);
1577             }
1578             printf( "\n" );
1579         }
1580 #endif
1581         for( lc=0; lc<count; lc++ )
1582         {
1583             if( strncasecmp( lumpx.name, lumpnames[lc], 8 ) == 0 )
1584                 result |= 1<<lc;  // found it, record it
1585         }
1586     }
1587 #ifdef ZIPWAD
1588     WZ_close( zhand );
1589 #else
1590     fclose( wadfile );
1591 #endif
1592 
1593 ret_result:
1594     return result;	// default is not found
1595 
1596     // Should only be called with known WAD files
1597 not_a_wad:
1598     reason = "Not a WAD file";
1599     goto err_ret0;
1600 
1601 read_err:  // read and seek errors
1602     reason = "Wad file err";
1603     goto err_ret0;
1604 
1605 open_err:
1606     reason = "Wad file open err";
1607 
1608 err_ret0:
1609     GenPrintf( EMSG_error, "File %s: %s\n", wadname, reason );
1610 #ifdef ZIPWAD
1611     if( zhand )    WZ_close( zhand );
1612 #else
1613     if( wadfile )  fclose( wadfile );
1614 #endif
1615     return 0;
1616 }
1617 
1618 
1619 static
Check_keylumps(game_desc_t * gmtp,const char * wadname)1620 boolean Check_keylumps ( game_desc_t * gmtp, const char * wadname )
1621 {
1622     byte lumpbits;
1623     if( gmtp->require_lump | gmtp->reject_lump )
1624     {
1625         lumpbits = Check_lumps( wadname,
1626                                common_lump_names, COMMON_LUMP_LIST_SIZE );
1627         if((gmtp->require_lump & lumpbits) != gmtp->require_lump )  goto fail;
1628         if( gmtp->reject_lump & lumpbits )  goto fail;
1629     }
1630     // Check 2 unique lump names, NULLS are treated as found.
1631     lumpbits = Check_lumps( wadname, &(gmtp->keylump[0]), 2 );
1632     if( lumpbits != 0x03 )   goto fail;
1633     return 1;  // lump checks successful
1634 fail:
1635     return 0;  // does not match
1636 }
1637 
1638 
1639 // Checks the possible wad filenames in GDESC_ entry.
1640 // Return true when found and keylumps verified
1641 // Leaves name in pathbuf_p, which must be of MAX_PATH length.
1642 static
Check_wad_filenames(int gmi,char * pathbuf_p)1643 boolean  Check_wad_filenames( int gmi, /*OUT*/ char * pathbuf_p )
1644 {
1645     game_desc_t * gmtp = &game_desc_table[gmi];
1646     filestatus_e fse;
1647     int w;
1648     // check each possible filename listed
1649     for( w=0; w<3; w++ )
1650     {
1651         if( gmtp->iwad_filename[w] == NULL )
1652             break;
1653 
1654         fse = Search_doomwaddir( gmtp->iwad_filename[w], GAME_SEARCH_DEPTH,
1655                                /*OUT*/ pathbuf_p );
1656 
1657 #ifdef ZIPWAD
1658         if( fse == FS_ZIP )
1659         {
1660             // Check file in archive
1661             WZ_open_archive( pathbuf_p );
1662             byte fnd = Check_keylumps( gmtp, gmtp->iwad_filename[w] );
1663             WZ_close_archive();
1664             if( fnd )
1665                 return true;
1666         }
1667         else
1668 #endif
1669         if( fse == FS_FOUND )
1670         {
1671             // File exists.
1672             if( Check_keylumps( gmtp, pathbuf_p ) )
1673                 return true;
1674         }
1675     }
1676     return false;
1677 }
1678 
1679 
1680 // May be called again after command restart
1681 static
IdentifyVersion()1682 void IdentifyVersion()
1683 {
1684     char pathiwad[_MAX_PATH + 16];
1685     // debug_Printf("MAX_PATH: %i\n", _MAX_PATH);
1686 
1687     boolean  other_names = 0;	// indicates -iwad other names
1688 #ifdef DEVPARM_LOADING
1689     boolean  devgame = false;   // indicates -devgame <game>
1690 #endif
1691 
1692     int gamedesc_index = NUM_GDESC; // nothing
1693     int gmi;
1694 
1695     // find legacy.wad, IWADs
1696     // and... Doom LEGACY !!! :)
1697     char *legacywad = NULL;
1698 
1699     if( verbose )
1700     {
1701         Print_search_directories( EMSG_ver, 0x0F );
1702     }
1703 
1704 
1705 #if defined(__APPLE__) && defined(__MACH__) && defined( EXT_MAC_DIR_SPEC )
1706     //[segabor]: on Mac OS X legacy.wad is within .app folder
1707     // for uniformity, use the strdup at found_legacy_wad
1708     cat_filename(pathiwad, "", mac_legacy_wad );
1709     if( access( mac_legacy_wad, R_OK) == 0 )    goto found_legacy_wad;
1710     // check other locations
1711 #endif
1712 
1713     // [WDJ]: find legacy.wad .
1714     // pathiwad must be MAX_WADPATH to be used by cat_filename.
1715     // Look in program directory first, because executable may have
1716     // its own version of legacy.wad.
1717     if( progdir && ( access( progdir, R_OK) == 0 ) )
1718     {
1719         // [WDJ] look for legacy.wad with doomlegacy
1720         cat_filename(pathiwad, progdir, "legacy.wad");
1721         if( access( pathiwad, R_OK) == 0 )   goto found_legacy_wad;
1722     }
1723 
1724 #ifdef LEGACYWADDIR
1725     // [WDJ] Try LEGACYWADDIR as the first wad dir.
1726     if( access( LEGACYWADDIR , R_OK) == 0 )
1727     {
1728         // [WDJ] legacy.wad is in shared directory
1729         cat_filename(pathiwad, LEGACYWADDIR, "legacy.wad");
1730         if( access( pathiwad, R_OK) == 0 )   goto found_legacy_wad;
1731     }
1732 #endif
1733 
1734     // Search wad directories.
1735     doomwaddir[1] = progdir_wads;
1736     // Should not be zipped
1737     if( Search_doomwaddir( "legacy.wad", 0, /*OUT*/ pathiwad ) == FS_FOUND )
1738          goto found_legacy_wad;
1739 
1740     I_SoftError( "legacy.wad not found\n" );  // fatal exit
1741     GenPrintf(EMSG_error, "Looked for legacy.wad in:\n" );
1742     Print_search_directories( EMSG_error, 0x01 );
1743     goto fatal_err;
1744 
1745 
1746  found_legacy_wad:
1747     legacywad = strdup( pathiwad );  // malloc
1748     doomwaddir[1] = NULL;
1749 
1750     if( verbose )
1751     {
1752         GenPrintf(EMSG_ver, "Legacy.wad: %s\n", legacywad );
1753     }
1754 
1755     owner_wad_search_order();
1756 
1757     /*
1758        French stuff.
1759        doom2fwad = malloc(strlen(doomwaddir)+1+10+1);
1760        sprintf(doom2fwad, "%s/doom2f.wad", doomwaddir);
1761      */
1762 
1763     // [WDJ] were too many chained ELSE. Figured it out once and used direct goto.
1764 
1765 #ifdef DEVPARM_LOADING
1766     // [WDJ] Old switches -shdev, -regdev, -comdev are now -devgame <game>
1767     // Earlier did direct test of -devparm, do not overwrite it.
1768     devgame = M_CheckParm("-devgame");
1769     // [WDJ] search for one of the listed GDESC_ forcing switches
1770     if ( devgame || M_CheckParm("-game") )
1771 #else
1772     if( M_CheckParm("-game") )
1773 #endif
1774     {
1775         char *temp = M_GetNextParm();
1776         if( temp == NULL )
1777         {
1778 #ifdef DEVPARM_LOADING
1779             I_SoftError( "Switch  -game <name> or -devgame <name>\n" );
1780 #else
1781             I_SoftError( "Switch  -game <name>\n" );
1782 #endif
1783             goto fatal_err;
1784         }
1785 
1786         for( gmi=0; gmi<GDESC_other; gmi++ )
1787         {
1788             // compare to recognized game mode names
1789             if (!strcmp(temp, game_desc_table[gmi].idstr))
1790                 goto game_switch_found;
1791         }
1792         I_SoftError( "Switch  -game %s  not recognized\n", temp );
1793         goto fatal_err;
1794 
1795        game_switch_found:
1796         // switch forces the GDESC_ selection
1797         gamedesc_index = gmi;
1798         gamedesc = game_desc_table[gamedesc_index]; // copy the game descriptor
1799         if (gamedesc.gameflags & GD_unsupported)  goto unsupported_wad;
1800 
1801 #ifdef DEVPARM_LOADING
1802         // handle the recognized special -devgame switch
1803         if( devgame )
1804         {
1805             devparm = 1 + verbose;
1806 #if 0
1807             M_Set_configfile_main( DEVDATA CONFIGFILENAME );
1808             // [WDJ] Old, irrelevant, and it was interfering with new
1809             // GDESC changes.
1810             // Better to just use -file so I am disabling it.
1811             switch( gamedesc_index )
1812             {
1813              case GDESC_doom_shareware:
1814                // instead use:
1815                //  doomlegacy -devgame doom1 -file data_se/texture1.lmp data_se/pnames.lmp
1816                D_AddFile(DEVDATA "doom1.wad");
1817                D_AddFile(DEVMAPS "data_se/texture1.lmp");
1818                D_AddFile(DEVMAPS "data_se/pnames.lmp");
1819                goto got_iwad;
1820              case GDESC_doom:
1821                // instead use:
1822                //   doomlegacy -devgame doom1 -file data_se/texture1.lmp data_se/texture2.lmp data_se/pnames.lmp
1823                D_AddFile(DEVDATA "doom.wad");
1824                D_AddFile(DEVMAPS "data_se/texture1.lmp");
1825                D_AddFile(DEVMAPS "data_se/texture2.lmp");
1826                D_AddFile(DEVMAPS "data_se/pnames.lmp");
1827                goto got_iwad;
1828              case GDESC_doom2:
1829                // instead use:
1830                //   doomlegacy -devgame doom2 -file cdata/texture1.lmp cdata/pnames.lmp
1831                D_AddFile(DEVDATA "doom2.wad");
1832                D_AddFile(DEVMAPS "cdata/texture1.lmp");
1833                D_AddFile(DEVMAPS "cdata/pnames.lmp");
1834                goto got_iwad;
1835             }
1836 #endif
1837         }
1838 #endif
1839     }
1840 
1841 
1842     // Specify the name of the IWAD file to use, so we can have several IWAD's
1843     // in the same directory, and/or have legacy.exe only once in a different location
1844     if (M_CheckParm("-iwad"))
1845     {
1846         filestatus_e fse = FS_NOTFOUND;
1847 
1848         // BP: big hack for fullpath wad, we should use wadpath instead in d_addfile
1849         char *s = M_GetNextParm();
1850         if ( s == NULL )
1851         {
1852             I_SoftError("Switch -iwad <filename>.\n");
1853             goto fatal_err;
1854         }
1855 
1856         const char * ipath = file_searchpath( s );
1857         if( ipath )
1858         {
1859             // Absolute or relative path, no search.
1860             cat_filename( pathiwad, ipath, s );
1861         }
1862         else
1863         {
1864             // Simple filename.
1865             // Find the IWAD in the doomwaddir.
1866             fse = Search_doomwaddir( s, IWAD_SEARCH_DEPTH, /*OUT*/ pathiwad );
1867 #ifdef ZIPWAD
1868             if( fse == FS_ZIP )
1869             {
1870                 // Found zip archive in doomwaddir.
1871 //                cat_filename( pathiwad, "", s );
1872             }
1873             else
1874 #endif
1875             if( fse != FS_FOUND )
1876             {
1877                 // Not found in doomwaddir.
1878                 cat_filename( pathiwad, "", s );
1879             }
1880         }
1881 
1882 #ifdef LAUNCHER
1883         CV_Set( & cv_iwad, pathiwad );  // for launcher
1884         cv_iwad.state &= ~CS_MODIFIED;
1885 #endif
1886 
1887         if ( access(pathiwad, R_OK) < 0 )
1888         {
1889             I_SoftError("IWAD %s not found\n", s);
1890             Print_search_directories( EMSG_error, 0x02 );
1891             goto fatal_err;
1892         }
1893 
1894         char *filename = FIL_Filename_of( pathiwad );
1895 #ifdef ZIPWAD
1896         char  filename_wad[MAX_WADPATH];
1897         if( fse == FS_ZIP )
1898         {
1899             // Need archive name for lookup.
1900             WZ_save_archive_name( filename );
1901             // Need wad name for GDESC lookup.
1902             if( WZ_make_name_with_extension( filename, "wad", /*OUT*/ filename_wad )  )
1903                 filename = filename_wad;  // use wad name instead of archive name
1904         }
1905 #endif
1906 
1907         if ( gamedesc_index == NUM_GDESC ) // check forcing switch
1908         {
1909             // No forcing switch
1910             // [WDJ] search game table for matching iwad name
1911 #ifdef ZIPWAD
1912             if( fse == FS_ZIP )
1913             {
1914                 // Check file in archive
1915                 WZ_open_archive( pathiwad );
1916             }
1917 #endif
1918             for( gmi=0; gmi<GDESC_other; gmi++ )
1919             {
1920                 game_desc_t * gmtp = &game_desc_table[gmi];
1921                 int w;
1922                 // check each possible filename listed
1923                 for( w=0; w<3; w++ )
1924                 {
1925                     if( gmtp->iwad_filename[w] == NULL )
1926                         break;  // end of list
1927 
1928                     if( strcasecmp(gmtp->iwad_filename[w], filename) == 0 )
1929                     {
1930 #ifdef ZIPWAD
1931                         if( fse == FS_ZIP )
1932                         {
1933                             // Check file in archive
1934                             byte fnd = Check_keylumps( gmtp, filename );
1935                             if( fnd )
1936                                 goto got_gmi_iwad;
1937                         }
1938                         else
1939 #endif
1940                         if( Check_keylumps( gmtp, pathiwad ) )
1941                             goto got_gmi_iwad;
1942                     }
1943                 }
1944             }
1945             // unknown IWAD is GDESC_other
1946             gamedesc_index = GDESC_other;
1947 #ifdef ZIPWAD
1948             if( archive_open )
1949             {
1950                 WZ_close_archive();
1951             }
1952 #endif
1953         }
1954 
1955         other_names = 1;        // preserve other names when forcing switch
1956         // for save game header
1957         strncpy( other_iwad_filename, filename, DESCNAME_SIZE );
1958         other_iwad_filename[ DESCNAME_SIZE-1 ] = 0; // safe
1959         // create game name from the wad name, used in save game
1960         strncpy( other_gname, other_iwad_filename, DESCNAME_SIZE );
1961         other_gname[ DESCNAME_SIZE-1 ] = 0;	// safe
1962                // use the wad name, without the ".wad" as the gname
1963         {
1964             char * dp = strchr( other_gname, '.' );
1965             if( dp )  *dp = 0;
1966         }
1967         goto got_iwad;
1968     }
1969     // No -iwad switch:
1970     // [WDJ] Select IWAD by game switch
1971     if(gamedesc_index < GDESC_other)  // selected by switch, and no -iwad
1972     {
1973         // make iwad name by switch
1974         // use pathiwad to output wad path from Check_wad_filenames
1975         if( Check_wad_filenames( gamedesc_index, pathiwad ) )
1976             goto got_iwad;
1977 
1978         I_SoftError("IWAD %s not found in:\n",
1979                      game_desc_table[gamedesc_index].iwad_filename[0]);
1980         Print_search_directories( EMSG_error, 0x02 );
1981         goto fatal_err;
1982     }
1983     // No -iwad switch, and no mode select switch:
1984     // [WDJ] search the table for the first iwad filename found
1985     for( gmi=0; gmi<GDESC_other; gmi++ )
1986     {
1987         // use pathiwad to output wad path from Check_wad_filenames
1988         if( Check_wad_filenames( gmi, pathiwad ) )
1989             goto got_gmi_iwad;
1990     }
1991 
1992     I_SoftError("Main WAD file not found\n"
1993             "You need doom.wad, doom2.wad, heretic.wad or some other IWAD file\n"
1994             "from any shareware, commercial or free version of Doom or Heretic!\n"
1995 #if !defined(__WIN32__) && !(defined __DJGPP__)
1996             "If you have one of those files, be sure its name is lowercase\n"
1997             "or use the -iwad command line switch.\n"
1998 #endif
1999             );
2000     goto fatal_err;
2001 
2002  got_gmi_iwad:
2003     gamedesc_index = gmi;  // a search loop found it
2004  got_iwad:
2005     gamedesc = game_desc_table[gamedesc_index]; // copy the game descriptor
2006 
2007     if( other_names )  // keep names from -iwad
2008     {
2009         gamedesc.gname = other_gname;
2010         gamedesc.iwad_filename[0] = other_iwad_filename;
2011     }
2012     gamedesc_id = gamedesc.gamedesc_id;
2013     G_set_gamemode( gamedesc.gamemode );
2014     GenPrintf( EMSG_info, "IWAD recognized: %s\n", gamedesc.gname);
2015 
2016     if (gamedesc.gameflags & GD_unsupported)  goto unsupported_wad;
2017 
2018     D_AddFile(pathiwad);
2019     D_AddFile(legacywad);  // So can replace some graphics with Legacy ones.
2020     if( gamedesc.gameflags & GD_iwad_pref )
2021     {
2022        // Because legacy.wad replaced some things it shouldn't, give the iwad
2023        // preference from both search directions.
2024        // Chexquest1: legacy.wad was replacing the green splats, with bloody ones.
2025        D_AddFile(pathiwad);
2026     }
2027     if( gamedesc.support_wad )
2028        D_AddFile( gamedesc.support_wad );
2029 
2030 cleanup_ret:
2031 #ifdef ZIPWAD
2032     if( archive_open )
2033     {
2034         WZ_close_archive();
2035     }
2036 #endif
2037     free(legacywad);  // from strdup, free local copy of name
2038     return;
2039 
2040 unsupported_wad:
2041     I_SoftError("Doom Legacy currently does not support this game.\n");
2042     goto fatal_err;
2043 
2044 fatal_err:
2045     if( legacywad )
2046     {
2047         // [WDJ] Load legacywad if possible, because it contains parts of the
2048         // user interface, and there will misleading errors.
2049         D_AddFile(legacywad);  // To prevent additional errors.
2050     }
2051     fatal_error = 1;
2052     goto cleanup_ret;
2053 }
2054 
2055 /* ======================================================================== */
2056 // Just print the nice red titlebar like the original DOOM2 for DOS.
2057 /* ======================================================================== */
2058 #ifdef SMIF_PC_DOS
D_Titlebar(const char * title1,const char * title2)2059 void D_Titlebar(const char *title1, const char *title2)
2060 {
2061     // DOOM LEGACY banner
2062     clrscr();
2063     textattr((BLUE << 4) + WHITE);
2064     clreol();
2065     cputs(title1);
2066 
2067     // standard doom/doom2 banner
2068     textattr((RED << 4) + WHITE);
2069     clreol();
2070     gotoxy((80 - strlen(title2)) / 2, 2);
2071     cputs(title2);
2072     normvideo();
2073     gotoxy(1, 3);
2074 }
2075 #endif
2076 
2077 #define MAX_TITLE_LEN   80
2078 static char legacytitle[MAX_TITLE_LEN+1];  // length of line
2079 
2080 //added:11-01-98:
2081 //
2082 //  Center the title string, then add the date and time of compilation.
2083 //
D_Make_legacytitle(void)2084 static void D_Make_legacytitle(void)
2085 {
2086   const char *s = VERSION_BANNER;
2087   int i;
2088 
2089   memset(legacytitle, ' ', sizeof(legacytitle));
2090 
2091   for (i = (MAX_TITLE_LEN - strlen(s)) / 2; *s; )  // center
2092     legacytitle[i++] = *s++;
2093 
2094   const char *u = __DATE__;
2095   for (i = 0; i < 11; i++)
2096     legacytitle[i + 1] = u[i];
2097 
2098   u = __TIME__;
2099   for (i = 0; i < 8; i++)
2100     legacytitle[i + 71] = u[i];
2101 
2102   legacytitle[MAX_TITLE_LEN] = '\0';
2103 }
2104 
2105 
2106 // Check Legacy.wad
D_CheckWadVersion()2107 void D_CheckWadVersion()
2108 {
2109     int wadversion = 0;
2110     char hs[128];
2111     int wv2, hlen;
2112     lumpnum_t ver_lumpnum;
2113 /* BP: disabled since this should work fine now...
2114     // check main iwad using demo1 version
2115     ver_lumpnum = W_CheckNumForNameFirst("demo1");
2116     // well no demo1, this is not a main wad file
2117     if( ! VALID_LUMP(ver_lumpnum) )
2118         I_Error("%s is not a Main wad file (IWAD)\n"
2119                 "try with Doom.wad or Doom2.wad\n"
2120                 "\n"
2121                 "Use -nocheckwadversion to remove this check,\n"
2122                 "but this can cause Legacy to hang\n",wadfiles[0]->filename);
2123     W_ReadLumpHeader (lump,&wadversion,1);
2124     if( wadversion<109 )
2125         I_Error("Your %s file is version %d.%d\n"
2126                 "Doom Legacy need version 1.9\n"
2127                 "Upgrade your version to 1.9 using IdSofware patch\n"
2128                 "\n"
2129                 "Use -nocheckwadversion to remove this check,\n"
2130                 "but this can cause Legacy to hang\n",wadfiles[0]->filename,wadversion/100,wadversion%100);
2131 */
2132     // check version, of legacy.wad using version lump
2133     ver_lumpnum = W_CheckNumForName("version");
2134     if( ! VALID_LUMP(ver_lumpnum) )
2135     {
2136         I_SoftError("No legacy.wad file.\n");
2137         fatal_error = 1;
2138         return;
2139     }
2140     hlen = W_ReadLumpHeader(ver_lumpnum, &hs, 128);
2141     if (hlen < 128)
2142     {
2143         hs[hlen] = '\0';
2144         if (sscanf(hs, "Doom Legacy WAD V%d.%d", &wv2, &wadversion) == 2)
2145           wadversion += wv2 * 100;
2146     }
2147     if (wadversion != cur_wadversion)
2148     {
2149         I_SoftError("Your legacy.wad file is version %d.%d, you need version %d.%d\n"
2150                 "Use the legacy.wad that came in the same archive as this executable.\n"
2151                 "\n"
2152                 "Use -nocheckwadversion to remove this check,\n"
2153                 "but this can cause Legacy to crash.\n",
2154                 (wadversion / 100), (wadversion % 100),
2155                 (int)(cur_wadversion / 100), (int)(cur_wadversion % 100) );
2156         if( wadversion < min_wadversion )
2157             fatal_error = 1;
2158     }
2159 }
2160 
2161 //
2162 // D_DoomMain
2163 //
2164 // Called from port main program to processes setup.
2165 // Returns before game starts.
D_DoomMain()2166 void D_DoomMain()
2167 {
2168     int p, wdi;
2169 #ifdef DEVPARM_LOADING
2170     char fbuf[FILENAME_SIZE];
2171 #endif
2172     char dirbuf[_MAX_PATH ];
2173     char cfgbuf[_MAX_PATH ];
2174 
2175     int startepisode;
2176     int startmap;
2177     boolean autostart;
2178 
2179 #ifdef FRENCH_INLINE
2180     french_early_text();
2181 #endif
2182 
2183     // print version banner just once here, use it anywhere
2184     sprintf(VERSION_BANNER, "Doom Legacy %d.%d.%d %s", VERSION/100, VERSION%100, REVISION, VERSIONSTRING);
2185     demoversion = VERSION;
2186 
2187     D_Make_legacytitle();
2188 
2189     memset( startupwadfiles, 0, sizeof(startupwadfiles) );
2190 
2191     CON_Init_Setup();  // vid, zone independent
2192     EOUT_flags |= EOUT_con;  // all msgs to CON buffer
2193     use_font1 = 1;  // until PLAYPAL and fonts loaded
2194     vid.draw_ready = 0;  // disable print reaching console
2195 
2196     //added:18-02-98:keep error messages until the final flush(stderr)
2197     if (setvbuf(stderr, NULL, _IOFBF, 1000))
2198         GenPrintf(EMSG_warn,"setvbuf didnt work\n");
2199     setbuf(stdout, NULL);       // non-buffered output
2200 
2201     // get parameters from a response file (eg: doom3 @parms.txt)
2202     M_FindResponseFile();
2203 
2204     // some basic commandline options
2205     if (M_CheckParm("--version"))
2206     {
2207       printf("%s\n", legacytitle);
2208       exit(0);
2209     }
2210 
2211     if (M_CheckParm("-v"))
2212     {
2213       verbose = 1;
2214     }
2215     if (M_CheckParm("-v2"))
2216     {
2217       verbose = 2;
2218     }
2219 
2220     if (M_CheckParm("--help") || M_CheckParm("-h"))
2221     {
2222       printf("%s\n", legacytitle);
2223       Help();
2224       exit(0);
2225     }
2226 
2227     GenPrintf( EMSG_info|EMSG_all, "%s\n", legacytitle);
2228 
2229     // Find or make a default dir that is not root dir
2230     // get the current directory (possible problem on NT with "." as current dir)
2231     if (getcwd(dirbuf, _MAX_PATH) != NULL)
2232     {
2233         // Need a working default dir, to prevent "" leading to root files.
2234         if( (strlen(dirbuf) > 4)
2235             || (strcmp( dirbuf, "." ) == 0) )   // systems that pass "."
2236         {
2237             defdir = strdup( dirbuf );
2238             if( verbose )
2239                 GenPrintf(EMSG_ver, "Current directory: %s\n", defdir);
2240 
2241             if( access( defdir, X_OK ) == 0 )
2242                 defdir_stat = 1;
2243         }
2244     }
2245 
2246     // [WDJ] When I_Get_Prog_Dir fails, progdir will be NULL.
2247     // Protect all uses of progdir and progdir_wads accordingly.
2248     if( I_Get_Prog_Dir( defdir, /*OUT*/ dirbuf ) )
2249     {
2250         // At worst, dirbuf may be an empty string.  OS dependent.
2251         progdir = strdup( dirbuf );
2252         if( verbose )
2253           GenPrintf(EMSG_ver, "Program directory: %s\n", progdir);
2254 
2255         // Set the directories that are relative to the program directory.
2256         if( access( progdir, X_OK ) == 0 )
2257         {
2258             cat_filename(dirbuf, progdir, "wads");
2259             progdir_wads = strdup(dirbuf);
2260         }
2261     }
2262 
2263     memset( doomwaddir, 0, sizeof(doomwaddir) );
2264     doomwaddir[0] = getenv("DOOMWADDIR");  // ptr to environment string
2265 
2266     // CDROM overrides doomwaddir (when valid)
2267     if (M_CheckParm("-cdrom"))
2268     {
2269         GenPrintf(EMSG_hud, D_CDROM);
2270         // [WDJ] Execute DoomLegacy off CDROM ??
2271         // DoomLegacy already has separate doomwaddir and legacyhome.
2272         // Legacy is not compatible with other port config and savegames,
2273         // so do not put such in old doom "c:\\doomdata".
2274         // Substitute CDROM for doomwaddir, but not legacyhome.
2275         if( defdir )
2276             doomwaddir[0] = ""; // wads from cur dir
2277         defdir_stat = 0; // do not let legacyhome use current dir
2278     }
2279 
2280 #if 0
2281 //[WDJ] disabled in 143beta_macosx
2282 // was test on MACOS_DI but could exclude or include __MACH__ ??
2283 //[segabor]
2284 #if defined( __APPLE__ ) && ! defined( __MACH__ )
2285     // cwd is always "/" when app is dbl-clicked
2286     if (!strcasecmp(doomwaddir, "/"))
2287     {
2288         // doomwaddir maybe malloc string, maybe not
2289         doomwaddir[0] = I_GetWadDir();
2290     }
2291 #endif
2292 #endif
2293 
2294     EOUT_flags = EOUT_text | EOUT_log | EOUT_con;
2295 
2296     CONS_Printf(text[Z_INIT_NUM]);
2297     // Cannot Init nor register cv_ vars until after Z_Init and some
2298     // other systems are init first.
2299     // -mb cannot be changed by Launcher, use Response File instead
2300     Z_Init();
2301 
2302     // Init once
2303     COM_Init(); // command buffer
2304     // Can now call CV_RegisterVar, and COM_AddCommand
2305 
2306     // may have some command line dependent init, like joystick
2307     I_SysInit();
2308 
2309     dedicated = M_CheckParm("-dedicated") != 0;
2310 
2311     //---------------------------------------------------- START DISPLAY
2312     //--- Display Error Messages
2313     CONS_Printf("StartupGraphics...\n");
2314     // setup loading screen with dedicated=0 and vid=800,600
2315     V_Init_VideoControl();  // before I_StartupGraphics
2316 
2317     if( ! dedicated )
2318     {
2319         I_StartupGraphics();    // window
2320         SCR_Startup();
2321     }
2322 
2323 #ifdef PARANOID
2324     SCR_Set_dummy_draw();
2325 #endif
2326 
2327     if( verbose > 1 )
2328         CONS_Printf("Init DEH, cht, menu\n");
2329 
2330     P_clear_state_ext();  // init state_ext
2331     // save Doom, Heretic, Chex strings for DEH
2332     DEH_Init();  // Init DEH before files and lumps loaded
2333     cht_Init();	 // init iwad independent cheats info, needed by Responder
2334 
2335     M_Init();    // init menu
2336     R_Init_rdata();
2337 
2338     if( verbose > 1 )
2339         CONS_Printf( "Register\n" );
2340 
2341     // Any cv_ with CV_SAVE need to be registered here, before reading the config.
2342     // Some of these are dependent upon the dedicated command line switch.
2343     CON_Register();
2344     D_Register_ClientCommands(); //Hurdler: be sure that this is called before D_Setup_NetGame
2345     D_Register_MiscCommands();	//[WDJ] more than just DeathMatch
2346 
2347     M_Register_Menu_Controls();
2348     HU_Register_Commands();
2349     ST_Register_Commands();
2350 
2351     T_Register_Commands();    // fragglescript
2352     B_Register_Commands();    //added by AC for acbot
2353     R_Register_EngineStuff();
2354     S_Register_SoundStuff();
2355 
2356     P_Register_Info_Commands();
2357 
2358 #ifdef LAUNCHER
2359     CV_RegisterVar(&cv_home);
2360     CV_RegisterVar(&cv_doomwaddir);
2361     CV_RegisterVar(&cv_iwad);
2362     CV_Set( &cv_doomwaddir, doomwaddir[0] ? doomwaddir[0] : "" );
2363     cv_doomwaddir.state &= ~CS_MODIFIED;
2364 #endif
2365 
2366     //Fab:29-04-98: do some dirty chatmacros strings initialisation
2367     HU_Init_Chatmacros();
2368 
2369     // load default control
2370     G_Controldefault();
2371 
2372 #ifdef ZIPWAD
2373     WZ_available();  // check for zip lib
2374 #endif
2375 
2376     // Before this line are initializations that are run only one time.
2377     //----------------------------------------------------
2378     // After this line is code that deals with configuration,
2379     // game and wad selection, and finding files and directories.
2380     // It may retry some actions and may execute functions multiple times.
2381 
2382 #ifdef LAUNCHER
2383     //---------------------------------------------------- LAUNCHER restarts here
2384 restart_command:
2385 
2386     fatal_error = 0;
2387     verbose = 0;  // verbose may be changed by launcher
2388     if (M_CheckParm("-v"))
2389     {
2390       verbose = 1;
2391     }
2392     if (M_CheckParm("-v2"))
2393     {
2394       verbose = 2;
2395     }
2396 
2397     dedicated = M_CheckParm("-dedicated") != 0;
2398 
2399     if( legacyhome )
2400        free( legacyhome );  // from previous
2401 #endif
2402 
2403     V_SetupFont( 1, NULL, 0 );  // Startup font size
2404     EOUT_flags = EOUT_text | EOUT_log | EOUT_con;
2405 
2406     //---------------------------------------------------- FIND FILES
2407     // -devgame is handled later, by IdentifyVersion
2408     devparm = M_CheckParm("-devparm");  // -devparm
2409     if (devparm)
2410     {
2411       devparm += verbose;  // levels of devparm
2412       CONS_Printf(D_DEVSTR);
2413     }
2414 
2415     if( verbose > 1 )
2416         CONS_Printf("Find HOME\n");
2417     // userhome section
2418     {
2419         const char * userhome = NULL;
2420 #ifdef LAUNCHER
2421         byte   userhome_parm = 0;
2422 #endif
2423         if (M_CheckParm("-home"))
2424         {
2425             userhome = M_GetNextParm();
2426             if( userhome == NULL )
2427             {
2428                 I_SoftError( "Switch  -home <directory>\n" );
2429                 userhome = "";
2430                 fatal_error = 1;
2431             }
2432 #ifdef LAUNCHER
2433             userhome_parm = 1;
2434 #endif
2435         }
2436         else
2437         {
2438             userhome = getenv("HOME");
2439             if( userhome && verbose > 1 )
2440                 CONS_Printf("HOME = %s\n", userhome);
2441 #ifdef WIN32
2442             if( userhome && strstr( userhome, "MSYS" ) )
2443             {
2444                 // Ignore MSYS HOME, it is not the one wanted.
2445                 if( verbose > 1 )
2446                     CONS_Printf("Ignore MYS HOME = %s\n", userhome);
2447                 userhome = NULL;
2448             }
2449             // Windows XP,
2450             if( !userhome )
2451             {
2452                  userhome = getenv("UserProfile");
2453                  if( userhome && verbose > 1 )
2454                      CONS_Printf("UserProfile = %s\n", userhome);
2455             }
2456 #endif
2457         }
2458 
2459         if (!userhome)
2460         {
2461             if(verbose)
2462                 GenPrintf(EMSG_ver, "Please set $HOME to your home directory, or use -home switch\n");
2463 
2464 #if 0
2465             // [WDJ] Using current directory just lead to the user losing
2466             // the config and savegames.
2467 
2468             // Try to use current directory and defaults
2469             // Make an absolute default directory, not root.
2470             if( defdir_stat )
2471             {
2472                 // have working default dir
2473                 // userhome cannot be "", because save games can end up in root directory
2474                 cat_filename( dirbuf, defdir, defhome );
2475                 userhome = strdup(dirbuf);  // malloc
2476                 if(verbose)
2477                     GenPrintf(EMSG_ver, " Using userhome= %s\n", userhome );
2478             }
2479             else
2480             {
2481                 GenPrintf(EMSG_warn, " No home dir, and no defdir.\n" );
2482             }
2483 #endif
2484         }
2485 
2486 #ifdef LAUNCHER
2487         if( (! userhome_parm || init_sequence == 0)
2488           && userhome )
2489         {
2490             // Save the input userhome for the Launcher, unless it came from -home.
2491             CV_Set( &cv_home, userhome );
2492             cv_home.state &= ~CS_MODIFIED;
2493         }
2494 #endif
2495 
2496 #if defined(__APPLE__) && defined(__MACH__) && defined( EXT_MAC_DIR_SPEC )
2497         //[segabor] ... ([WDJ] MAC port has vars handy)
2498 //        sprintf(configfile, "%s/DooMLegacy.cfg", mac_user_home);
2499         cat_filename( cfgbuf, mac_user_home, "DooMLegacy.cfg" );
2500         M_Set_configfile_main( cfgbuf );
2501         sprintf(savegamename, "%s/Saved games/Game %%d.doomSaveGame", mac_user_home);
2502         if ( ! userhome)
2503             userhome = mac_user_home;
2504         // legacyhome = strdup( mac_user_home );
2505         // Needs slash
2506         legacyhome = (char*) malloc( strlen(userhome) + 3 );
2507         // example: "/home/user/"
2508         sprintf(legacyhome, "%s/", userhome);
2509 #else
2510         // Find the legacyhome directory
2511         if (userhome)
2512         {
2513             // [WDJ] find directory, .doomlegacy, or .legacy
2514             char dirpath[ MAX_WADPATH ];
2515 
2516             // form directory filename, with slash (for savegamename)
2517             cat_filename( dirpath, userhome, DEFAULTDIR1 SLASH );
2518             // if it exists then use it
2519             if( access(dirpath, R_OK) < 0 )  // not found
2520             {
2521                 // not there, try 2nd choice
2522                 cat_filename( dirpath, userhome, DEFAULTDIR2 SLASH );
2523                 if( access(dirpath, R_OK) < 0 )  // not found
2524                 {
2525                     // not there either, then make primary default dir
2526                     cat_filename( dirpath, userhome, DEFAULTDIR1 SLASH );
2527                 }
2528             }
2529             // make subdirectory in userhome
2530             // example: "/home/user/.doomlegacy/"
2531             legacyhome = strdup( dirpath );  // malloc
2532         }
2533         else
2534         {
2535             // Check for an existing DEFHOME in current directory.
2536             // Only if user has made it.
2537             if( access(DEFHOME, R_OK) == 0 )  // legacy home found
2538             {
2539                 legacyhome = strdup( DEFHOME );  // malloc, will be free.
2540             }
2541         }
2542 
2543         if( ! legacyhome )
2544         {
2545             // Make a default legacy home in the program directory.
2546             // default absolute path, do not set to ""
2547             cat_filename( dirbuf, progdir, DEFHOME );
2548             legacyhome = strdup( dirbuf );  // malloc, will be free.
2549             if( verbose )
2550                 GenPrintf(EMSG_ver, "Default legacyhome= %s\n", legacyhome );
2551         }
2552 
2553         // Make the legacyhome directory
2554         if( access(legacyhome, R_OK) < 0 )  // not found
2555         {
2556             if( verbose )
2557                 GenPrintf(EMSG_ver, "MKDIR legacyhome= %s\n", legacyhome );
2558             I_mkdir( legacyhome, 0700);
2559         }
2560         legacyhome_len = strlen(legacyhome);
2561 
2562         // [WDJ] configfile must be set whereever legacyhome is on DOS or WIN32
2563         {
2564             const char * cfgstr;
2565             // user specific config file
2566 #ifdef DEVPARM_LOADING
2567             if( devparm )
2568             {
2569                 // example: /home/user/.legacy/devdataconfig.cfg
2570                 cfgstr = DEVDATA CONFIGFILENAME;
2571             }
2572             else
2573 #endif
2574             {
2575                 // example: /home/user/.legacy/config.cfg
2576                 cfgstr = CONFIGFILENAME;
2577             }
2578             cat_filename( cfgbuf, legacyhome, cfgstr );
2579             M_Set_configfile_main( cfgbuf );
2580         }
2581 
2582 #ifdef SAVEGAMEDIR
2583         // default savegame file name, example: "/home/user/.legacy/%s/doomsav%i.dsg"
2584 //        sprintf(savegamename, "%s%%s" SLASH "%s", legacyhome, text[NORM_SAVEI_NUM]);
2585         snprintf(savegamename, MAX_WADPATH-1, "%s%%s" SLASH "%s%s", legacyhome, SAVEGAMENAME, "%d.dsg");
2586         // so can extract legacyhome from savegamename later
2587 #else
2588         // default savegame file name, example: "/home/user/.legacy/doomsav%i.dsg"
2589 //        sprintf(savegamename, "%s%s", legacyhome, text[NORM_SAVEI_NUM]);
2590         snprintf(savegamename, MAX_WADPATH-1, "%s%s%s", legacyhome, SAVEGAMENAME, "%d.dsg");
2591 #endif
2592         savegamename[MAX_WADPATH-1] = '\0';
2593 #endif
2594 
2595         // [WDJ] Would have a doomwaddir in the config file too, but LoadConfig
2596         // is done way too late for that.
2597         for( wdi=0; wdi<(sizeof(dirlist)/sizeof(char*)); wdi++ )
2598         {
2599             char ** dwp = & doomwaddir[wdi+DOOMWADDIR_DIRLIST]; // where it goes in doomwaddir
2600             if( *dwp && (*dwp != dirlist[wdi]) )
2601                 free( *dwp );  // was malloc
2602             // Default searches
2603             if( dirlist[wdi] && dirlist[wdi][0] == '~' )
2604             {
2605                 // Relative to user home.
2606                 cat_filename( dirbuf, (userhome? userhome : "" ), &dirlist[wdi][2]);
2607                 *dwp = strdup( dirbuf );  // (malloc)
2608             }
2609             else
2610             {
2611                 *dwp = dirlist[wdi];  // (ref)
2612             }
2613         }
2614     }
2615 
2616 #if 0
2617     Print_search_directories( EMSG_debug, 0x0F );
2618 #endif
2619 
2620     if( verbose )
2621     {
2622         GenPrintf(EMSG_ver, "Config: %s\n", configfile_main );
2623         GenPrintf(EMSG_ver, "Savegames: %s\n", savegamename );
2624     }
2625 
2626     // identify the main IWAD file to use
2627     IdentifyVersion();  // game, iwad
2628     modifiedgame = false;
2629 
2630     // Title page
2631     const char *gametitle = gamedesc.startup_title;  // set by IdentifyVersion
2632     if( gametitle == NULL )   gametitle = gamedesc.gname;
2633     if( gametitle )
2634       GenPrintf(EMSG_info, "%s\n", gametitle);
2635 
2636 #ifdef DEVPARM_LOADING
2637     // convenience hack to allow -wart e m to add a wad file
2638     p = M_CheckParm("-wart");
2639     if (p)
2640     {
2641         // big hack, change to -warp so a later CheckParm does the warp.
2642         myargv[p][4] = 'p';
2643 
2644         // Map name handling.  Form wad name from map/episode numbers.
2645 #ifdef WADFILE_RELOAD
2646         // prepend a tilde to the filename so wadfile will be reloadable
2647 #endif
2648         switch (gamemode)
2649         {
2650             case doom_shareware:
2651             case ultdoom_retail:
2652             case doom_registered:
2653 #ifdef WADFILE_RELOAD
2654                 sprintf(fbuf, "~" DEVMAPS "E%cM%c.wad", myargv[p + 1][0], myargv[p + 2][0]);
2655 #else
2656                 sprintf(fbuf,  DEVMAPS "E%cM%c.wad", myargv[p + 1][0], myargv[p + 2][0]);
2657 #endif
2658                 GenPrintf(EMSG_info, "Warping to Episode %s, Map %s.\n", myargv[p + 1], myargv[p + 2]);
2659                 break;
2660 
2661             case doom2_commercial:
2662             default:
2663                 p = atoi(myargv[p + 1]);
2664                 if (p < 10)
2665 #ifdef WADFILE_RELOAD
2666                     sprintf(fbuf, "~" DEVMAPS "cdata/map0%i.wad", p);
2667 #else
2668                     sprintf(fbuf, DEVMAPS "cdata/map0%i.wad", p);
2669 #endif
2670                 else
2671 #ifdef WADFILE_RELOAD
2672                     sprintf(fbuf, "~" DEVMAPS "cdata/map%i.wad", p);
2673 #else
2674                     sprintf(fbuf, DEVMAPS "cdata/map%i.wad", p);
2675 #endif
2676                 break;
2677         }
2678         D_AddFile(fbuf);
2679         // continue and execute -warp
2680     }
2681 #endif
2682 
2683     // Add any files specified on the command line with -file <wadfile>
2684     // to the wad list
2685     if (M_CheckParm("-file"))
2686     {
2687         // the parms after p are wadfile/lump names,
2688         // until end of parms or another - preceded parm
2689         modifiedgame = true;    // homebrew levels
2690         while (M_IsNextParm())
2691             D_AddFile( M_GetNextParm() );
2692     }
2693 
2694     // load dehacked file
2695     p = M_CheckParm("-dehacked");
2696     if (!p)
2697         p = M_CheckParm("-deh");        //Fab:02-08-98:like Boom & DosDoom
2698     if (p != 0)
2699     {
2700         while (M_IsNextParm())
2701             D_AddFile( M_GetNextParm() );
2702     }
2703 
2704 #ifdef FRENCH_INLINE
2705     french_text();
2706 #endif
2707 
2708 #ifdef BEX_LANGUAGE
2709     if ( M_CheckParm("-lang") )
2710     {
2711         // will check for NULL parameter
2712         BEX_load_language( M_GetNextParm(), 2 );  // language name
2713     }
2714 #ifdef BEX_LANG_AUTO_LOAD
2715     else
2716     {
2717         BEX_load_language( NULL, 2 );  // default language name
2718     }
2719 #endif
2720 #endif
2721 
2722     // Load wad, including the main wad file.
2723     // This will read DEH and BEX files.  Need devparm and verbose.
2724     if( W_Init_MultipleFiles(startupwadfiles) == 0 )
2725     {
2726        // Some wad failed to load.
2727        if( !M_CheckParm( "-noloadfail" ) )
2728          fatal_error = true;
2729     }
2730 
2731     if ( !M_CheckParm("-nocheckwadversion") )
2732         D_CheckWadVersion();
2733 
2734 
2735     //Hurdler: someone wants to keep those lines?
2736     //BP: i agree with you why should be registered to play someone wads ?
2737     //    unfortunately most additional wad have more texture and monsters
2738     //    that shareware wad do, so there will miss resource :(
2739 
2740     if ( gamedesc.gameflags & GD_idwad )
2741     {
2742       // [WDJ] These warnings only apply to id iwad files, and should not
2743       // appear when only using FreeDoom or third party iwads.
2744 
2745       // Check for -file in shareware
2746       if (modifiedgame)
2747       {
2748         // These are the lumps that will be checked in IWAD,
2749         // if any one is not present, execution will be aborted.
2750         char name[23][8] = {
2751             "e2m1", "e2m2", "e2m3", "e2m4", "e2m5", "e2m6", "e2m7", "e2m8", "e2m9",
2752             "e3m1", "e3m3", "e3m3", "e3m4", "e3m5", "e3m6", "e3m7", "e3m8", "e3m9",
2753             "dphoof", "bfgga0", "heada1", "cybra1", "spida1d1"
2754         };
2755 
2756         if (gamemode == doom_shareware)
2757             CONS_Printf("\nYou shouldn't use -file with the shareware version. Register!\n");
2758 
2759         // Check for fake IWAD with right name,
2760         // but w/o all the lumps of the registered version.
2761         if (gamemode == doom_registered)
2762         {
2763             int i;
2764             for (i = 0; i < 23; i++)
2765             {
2766                 if( ! VALID_LUMP( W_CheckNumForName(name[i]) ) )
2767                     CONS_Printf("\nThis is not the registered version.");
2768             }
2769         }
2770       }
2771 
2772       // If additonal PWAD files are used, print modified banner
2773       if (modifiedgame)
2774           CONS_Printf(text[MODIFIED_NUM]);
2775 
2776       // Check and print which version is executed.
2777       switch (gamemode)
2778       {
2779         case doom_shareware:
2780         case indetermined:
2781             CONS_Printf(text[SHAREWARE_NUM]);
2782             break;
2783         case doom_registered:
2784         case ultdoom_retail:
2785         case doom2_commercial:
2786             CONS_Printf(text[COMERCIAL_NUM]);
2787             break;
2788         default:
2789             // Ouch.
2790             break;
2791       }
2792     }
2793 
2794     EOUT_flags = EOUT_text | EOUT_log | EOUT_con;
2795 
2796 
2797 #ifdef LAUNCHER
2798     //---------------------------------------------------- LAUNCHER display
2799     if ( fatal_error || init_sequence == 1 || (init_sequence == 0 && myargc < 2 ))
2800     {
2801         // [WDJ] Invoke built-in launcher command line
2802         if ( fatal_error )
2803         {
2804             CONS_Printf("Fatal error display: (press ESC to continue).\n");
2805             con_destlines = BASEVIDHEIGHT;
2806             do
2807             {
2808                 CON_Draw_Console();
2809                 I_OsPolling();
2810                 D_Process_Events ();  // menu and console responder
2811                 CON_Ticker ();
2812                 I_UpdateNoBlit();
2813                 I_FinishUpdate();       // page flip or blit buffer
2814             } while( con_destlines>0 );
2815         }
2816         init_sequence = 1;
2817         M_LaunchMenu();  // changes init_sequence > 1 to exit restart loop
2818 
2819         // restart
2820         Clear_SoftError();
2821         D_Clear_Files();
2822         con_Printf( "Launcher restart:\n" );
2823         goto restart_command;
2824     }
2825 #endif
2826 
2827     //--------------------------------------------------------- LAUNCHED
2828     // After this line, commit to the initial game and video port selected.
2829     // Use I_Error.
2830 
2831     if( ! VALID_LUMP( W_CheckNumForName ( "PLAYPAL" ) ) )
2832     {
2833         //Hurdler: I'm tired of that question ;)
2834         I_Error (
2835         "The main IWAD file is not found, or does not have PLAYPAL lump.\n"
2836         "The IWAD can be either doom.wad, doom1.wad, doom2.wad, tnt.wad\n"
2837         "plutonia.wad, heretic.wad, or heretic1.wad from any shareware\n"
2838         "or commercial version of Doom or Heretic, or some other IWAD!\n"
2839         "Cannot use legacy.wad, nor a PWAD, for an IWAD.\n" );
2840     }
2841 
2842     if( fatal_error )
2843     {
2844         I_Error ( "Shutdown due to fatal error.\n" );
2845     }
2846 
2847     //---------------------------------------------------- LOAD CONFIG
2848 
2849     // The drawmode and video settings are now part of the config.
2850     // It needs to be loaded before the full graphics.
2851 
2852     // check for an alternative config file
2853     p = M_CheckParm ("-config");
2854     if (p && p<myargc-1)
2855     {
2856         // substitute config file
2857         strncpy (cfgbuf, myargv[p+1], MAX_WADPATH-1);
2858         cfgbuf[MAX_WADPATH-1] = '\0';
2859         M_Set_configfile_main( cfgbuf );
2860         CONS_Printf ("config file: %s\n", configfile_main);
2861     }
2862     // This config will load the config drawmode setting.
2863     M_ClearConfig( CFG_main );  // due to launcher loop
2864     M_LoadConfig( CFG_main, configfile_main );        // WARNING : this do a "COM_BufExecute()"
2865 
2866 
2867     //---------------------------------------------------- READY SCREEN
2868 #ifdef HWRENDER
2869     // Init the rendermode patch storage.
2870     HWR_patchstore = 0;
2871     EN_HWR_flashpalette = 0;  // software and default
2872 #endif
2873 
2874     // we need to check for dedicated before initialization of some subsystems
2875     dedicated = M_CheckParm("-dedicated") != 0;
2876     if( dedicated )
2877     {
2878         nodrawers = true;
2879         vid.draw_ready = 0;
2880         drawmode_recalc = false;
2881         I_ShutdownGraphics();
2882         EOUT_flags = EOUT_log;
2883     }
2884     else
2885     {
2886         //--------------------------------------------------------- GRAPHICS SETTINGS
2887         set_drawmode = cv_drawmode.EV;
2888         req_bitpp = 0;  // because of launcher looping
2889         req_alt_bitpp = 0;
2890 
2891         if( M_CheckParm("-highcolor") )
2892         {
2893             set_drawmode = DRM_explicit_bpp;  // 15 or 16 bpp
2894             req_bitpp = 16;
2895             req_alt_bitpp = 15;
2896         }
2897         if( M_CheckParm("-truecolor") )
2898         {
2899             set_drawmode = DRM_explicit_bpp;  // 24 or 32 bpp
2900             req_bitpp = 32;
2901             req_alt_bitpp = 24;
2902         }
2903         if( M_CheckParm("-native") )
2904         {
2905             set_drawmode = DRM_native;  // bpp of the default screen
2906         }
2907         p = M_CheckParm("-bpp");  // specific bit per pixel color
2908         if( p )
2909         {
2910             // binding, should fail if cannot find a mode
2911             req_bitpp = atoi(myargv[p + 1]);
2912             if( ! V_CanDraw( req_bitpp ) )
2913             {
2914 #ifdef LAUNCHER
2915               I_SoftError( "-bpp invalid\n");
2916               goto restart_command;
2917 #else
2918               I_Error( "-bpp invalid\n");
2919 #endif
2920             }
2921             set_drawmode = DRM_explicit_bpp;
2922         }
2923 
2924         // Allow a config file for opengl to overload the config settings.
2925         // It may be edited to set only what settings should be specific to opengl.
2926         // May be a problem if opengl cannot really be started.
2927 
2928         if( M_CheckParm("-opengl") )
2929         {
2930             set_drawmode = DRM_opengl; // opengl temporary
2931         }
2932 #ifdef SMIF_WIN_NATIVE
2933 #ifdef HWRENDER
2934         else if( M_CheckParm ("-3dfx") || M_CheckParm ("-glide") )
2935         {
2936             set_drawmode = DRM_glide; // glide temporary
2937         }
2938         else if( M_CheckParm ("-minigl") ) // MiniGL is considered to be opengl
2939         {
2940             set_drawmode = DRM_minigl; // opengl temporary
2941         }
2942         else if( M_CheckParm ("-d3d") )
2943         {
2944             set_drawmode = DRM_d3d; // D3D temporary
2945         }
2946 #endif
2947 #endif
2948 
2949         M_ClearConfig( CFG_drawmode );  // due to launcher loop
2950         // Load the config file for this drawmode.
2951         // example: /home/user/.legacy/config32.cfg
2952         M_Set_configfile_drawmode( set_drawmode );
2953         // Conditional on name defined and file existing.
2954         // This cannot change the drawmode, but can load screen sizes.
2955         M_LoadConfig( CFG_drawmode, configfile_drawmode );        // WARNING : this do a "COM_BufExecute()"
2956 
2957 
2958         // 0 means not set at the cmd-line
2959         req_width = 0;
2960         req_height = 0;
2961 
2962         p = M_CheckParm("-width");
2963         if (p && p < myargc-1)
2964             req_width = atoi(myargv[p+1]);
2965 
2966         p = M_CheckParm("-height");
2967         if (p && p < myargc-1)
2968             req_height = atoi(myargv[p+1]);
2969 
2970         req_command_video_settings = ((req_width > 0) && (req_height > 0));
2971 
2972         //--------------------------------------------------------- FULL GRAPHICS
2973         // setup loading screen
2974         // Still using font1 during this init, up into CON_Init_Video.
2975         CONS_Printf("RequestFullGraphics...\n");
2976         // Allow VID_QueryModelist to detect fullscreen capability.
2977         allow_fullscreen = ! M_CheckParm("-window");
2978         cv_fullscreen.EV = cv_fullscreen.value && allow_fullscreen;
2979 
2980         // Initial setup of rendermode
2981         V_switch_drawmode( set_drawmode, 0 );  // command line, do not change config files
2982 
2983         // set user default mode or mode set at cmdline
2984         SCR_apply_video_settings();  // command line settings, or config file settings.
2985 
2986         // Full graphics.
2987         // param: req_drawmode, req_bitpp, req_alt_bitpp, req_width, req_height.
2988         // If fails for one fullscreen mode, does not mean fails for all fullscreen modes.
2989         // Calls  I_Rendermode_setup, V_Setup_VideoDraw, HWR_Startup_Render.
2990         // Calls  V_SetPalette, HWR_SetPalette.
2991         drawmode_recalc = true;
2992         rendermode_recalc = true;
2993         SCR_SetMode( 0 );
2994 
2995         SCR_Recalc();
2996         V_Clear_Display();
2997 
2998         drawmode_recalc = false;
2999         rendermode_recalc = false;
3000 
3001         //--------------------------------------------------------- CONSOLE
3002         // Need console fonts, colormaps, before can stop using font1.
3003         CONS_Printf(text[HU_INIT_NUM]);
3004 #ifdef PARANOID
3005         // Console fonts were loaded by SCR_SetMode.
3006         if( hu_fonts_loaded < 2 )
3007         {
3008             // Do not reload without unloading fonts first.
3009             if( hu_fonts_loaded == 0 )
3010             {
3011                 GenPrintf( EMSG_warn, "Console fonts not loaded\n" );
3012                 // Load console fonts, setting hu_fonts_loaded.
3013                 HU_Load_Graphics();  // dependent upon dedicated and game
3014             }
3015             else
3016             {
3017                 GenPrintf( EMSG_warn, "Console fonts not complete\n" );
3018             }
3019         }
3020 #endif
3021         // Load console colormaps, and size console.
3022         CON_Init_Video();  // dependent upon vid, hu_font
3023 
3024         if( (hu_fonts_loaded == 2) && whitemap )
3025             use_font1 = 0;  // Now use console fonts, no longer using font1.
3026 
3027         EOUT_flags = EOUT_log | EOUT_con;
3028     }
3029 
3030 #if 0
3031     p = M_CheckParm ("-cfgmod");
3032     if (p && p<myargc-1)
3033     {
3034         // Add mod config file
3035         strncpy (cfgbuf, myargv[p+1], MAX_WADPATH-1);
3036         cfgbuf[MAX_WADPATH-1] = '\0';
3037         // not saveable, so do not need to save name
3038         CONS_Printf ("add config file: %s\n", cfgbuf);
3039         // This cannot change the drawmode.
3040         M_LoadConfig( CFG_other, cfgbuf );        // WARNING : this do a "COM_BufExecute()"
3041     }
3042 #endif
3043 
3044     //--------------------------------------------------------- GAME SETTINGS
3045     // get skill / episode / map from parms
3046     gameskill = sk_medium;
3047     startepisode = 1;
3048     startmap = 1;
3049     autostart = false;
3050 
3051     p = M_CheckParm("-skill");
3052     if (p && p < myargc - 1)
3053     {
3054         gameskill = myargv[p + 1][0] - '1';
3055         autostart = true;
3056     }
3057 
3058     p = M_CheckParm("-episode");
3059     if (p && p < myargc - 1)
3060     {
3061         startepisode = myargv[p + 1][0] - '0';
3062         startmap = 1;
3063         autostart = true;
3064     }
3065 
3066     p = M_CheckParm("-warp");
3067     if (p && p < myargc - 1)
3068     {
3069         if (gamemode == doom2_commercial)
3070             startmap = atoi(myargv[p + 1]);
3071         else
3072         {
3073             startepisode = myargv[p + 1][0] - '0';
3074             if (p < myargc - 2 && myargv[p + 2][0] >= '0' && myargv[p + 2][0] <= '9')
3075                 startmap = myargv[p + 2][0] - '0';
3076             else
3077                 startmap = 1;
3078         }
3079         autostart = true;
3080     }
3081 
3082     // [WDJ] This triggers the first draw to the screen,
3083     // debug it here instead of waiting for CONS_Printf in BloodTime_OnChange
3084     CONS_Printf( "Init after ...\n" );
3085 
3086     CONS_Printf(text[W_INIT_NUM]);
3087     // adapt tables to legacy needs
3088     P_PatchInfoTables();
3089 
3090     if (gamemode == heretic)
3091     {
3092         Heretic_PatchEngine();
3093 #ifdef FRENCH_INLINE
3094         french_heretic();
3095 #endif
3096     }
3097 
3098     if(gamemode == chexquest1)
3099     {
3100         Chex1_PatchEngine();
3101 #ifdef FRENCH_INLINE
3102         french_chexquest();
3103 #endif
3104     }
3105 
3106     B_Init_Bots();       //added by AC for acbot
3107 
3108     wipegamestate = gamestate;
3109 
3110     //------------------------------------------------ COMMAND LINE PARAMS
3111 
3112 #ifdef CDMUS
3113     // Initialize CD-Audio, no music on a dedicated server
3114     if (!M_CheckParm("-nocd") && ! dedicated )
3115       I_InitCD();
3116 #endif
3117 
3118     if (M_CheckParm("-splitscreen"))
3119         CV_SetParam(&cv_splitscreen, 1);
3120 
3121     // Affects only the game started at the command line.
3122     // CV_NETVAR are sent by string, CV_SAVE are saved by string.
3123     nomonsters = M_CheckParm("-nomonsters");
3124 
3125     if (M_CheckParm("-respawn"))
3126       CV_SetParam( &cv_respawnmonsters, 1 );  // NETVAR
3127 //      COM_BufAddText("respawnmonsters 1\n");
3128     if (M_CheckParm("-coopmonsters"))
3129       CV_SetParam( &cv_monbehavior, 1 );  // NETVAR, SAVE
3130 //      COM_BufAddText("monsterbehavior 1\n");
3131     if (M_CheckParm("-infight"))
3132       CV_SetParam( &cv_monbehavior, 2 );  // NETVAR, SAVE
3133 //      COM_BufAddText("monsterbehavior 2\n");
3134     if (M_CheckParm("-teamplay"))
3135       CV_SetParam( &cv_teamplay, 1 );  // NETVAR
3136 //        COM_BufAddText("teamplay 1\n");
3137     if (M_CheckParm("-teamskin"))
3138       CV_SetParam( &cv_teamplay, 2 );  // NETVAR
3139 //        COM_BufAddText("teamplay 2\n");
3140     // Setting deathmatch also sets cv_itemrespawn (d_netcmd).
3141     if (M_CheckParm("-altdeath"))
3142       CV_SetParam( &cv_deathmatch, 2 );  // NETVAR
3143 //      COM_BufAddText("deathmatch 2\n");
3144     else if (M_CheckParm("-deathmatch"))
3145       CV_SetParam( &cv_deathmatch, 1 );  // NETVAR
3146 //        COM_BufAddText("deathmatch 1\n");
3147     if (M_CheckParm("-fast"))
3148       CV_SetParam( &cv_fastmonsters, 1 );  // NETVAR
3149 //        COM_BufAddText("fastmonsters 1\n");
3150     //added by AC
3151     if (M_CheckParm("-predicting"))
3152       CV_SetParam( &cv_predictingmonsters, 1 );  // NETVAR
3153 //        COM_BufAddText("predictingmonsters 1\n");
3154 
3155     if (M_CheckParm("-timer"))
3156     {
3157         char *s = M_GetNextParm();
3158         if( s == NULL )
3159         {
3160             I_SoftError( "Switch  -timer <seconds>\n" );
3161         }
3162         else
3163         {
3164             // May be larger than EV, so cannot use CV_SetParam.
3165             CV_Set( &cv_timelimit, s );  // NETVAR
3166 //            COM_BufAddText(va("timelimit %s\n", s));
3167         }
3168     }
3169 
3170     if (M_CheckParm("-avg"))
3171     {
3172         // May be larger than EV, so cannot use CV_SetParam.
3173         CV_SetValue( &cv_timelimit, 20 );  // NETVAR
3174 //        COM_BufAddText("timelimit 20\n");
3175         CONS_Printf(text[AUSTIN_NUM]);
3176     }
3177 
3178     // turbo option, is not meant to be saved in config, still
3179     // supported at cmd-line for compatibility
3180     if (M_CheckParm("-turbo"))
3181     {
3182         if( M_IsNextParm() )
3183         {
3184             COM_BufAddText(va("turbo %s\n", M_GetNextParm()));
3185         }
3186         else
3187         {
3188             I_SoftError( "Switch  -turbo <10-255>\n" );
3189         }
3190     }
3191 
3192     // push all "+" parameter at the command buffer
3193     M_PushSpecialParameters();
3194 
3195     CONS_Printf(text[M_INIT_NUM]);
3196     M_Configure();
3197 
3198     CONS_Printf(text[R_INIT_NUM]);
3199     R_Init();
3200 
3201     //
3202     // setting up sound
3203     //
3204     CONS_Printf(text[S_SETSOUND_NUM]);
3205     nosoundfx = M_CheckParm("-nosound");
3206     nomusic = M_CheckParm("-nomusic");
3207     if( dedicated )
3208     {
3209         nosoundfx = 1;
3210         nomusic = 1;
3211         // allow sound driver to disable self
3212     }
3213     // Music init is in I_StartupSound
3214     I_StartupSound();
3215     S_Init(cv_soundvolume.value, cv_musicvolume.value);
3216 
3217     CONS_Printf(text[ST_INIT_NUM]);
3218     ST_Init();
3219 
3220     // SoM: Init FraggleScript
3221     T_Init_FS();
3222 
3223     // init all NETWORK
3224     CONS_Printf(text[D_CHECKNET_NUM]);
3225     if (D_Startup_NetGame())
3226         autostart = true;
3227 
3228     // check for a driver that wants intermission stats
3229     p = M_CheckParm("-statcopy");
3230     if (p && p < myargc - 1)
3231     {
3232         I_SoftError("Sorry but statcopy isn't supported at this time\n");
3233         /*
3234            // for statistics driver
3235            extern  void*   statcopy;
3236 
3237            statcopy = (void*)atoi(myargv[p+1]);
3238            CONS_Printf (text[STATREG_NUM]);
3239          */
3240     }
3241 
3242     // start the apropriate game based on parms
3243     p = M_CheckParm("-record");
3244     if (p && p < myargc - 1)
3245     {
3246         G_RecordDemo(myargv[p + 1]);
3247         autostart = true;
3248     }
3249 
3250     // demo doesn't need anymore to be added with D_AddFile()
3251     p = M_CheckParm("-playdemo");
3252     if( !p && M_CheckParm("-timedemo") )
3253       p = 2500;  // indicate timedemo
3254     if (p)
3255     {
3256       if( ! M_IsNextParm() )
3257       {
3258         I_SoftError( "Switch  -playdemo <name>  or  -timedemo <name> \n" );
3259       }
3260       else
3261       {
3262         char demo_name[MAX_WADPATH];  // filename
3263         // add .lmp to identify the EXTERNAL demo file
3264         // it is NOT possible to play an internal demo using -playdemo,
3265         // rather push a playdemo command.. to do.
3266 
3267         strncpy(demo_name, M_GetNextParm(), MAX_WADPATH-1);
3268         demo_name[MAX_WADPATH-1] = '\0';
3269         // get spaced filename or directory
3270         while (M_IsNextParm())
3271         {
3272             // [WDJ] Protect against long demo name on command line
3273             int dn_free = MAX_WADPATH - 2 - strlen(demo_name);
3274             if( dn_free > 1 )
3275             {
3276                 strcat(demo_name, " ");
3277                 strncat(demo_name, M_GetNextParm(), dn_free );
3278                 demo_name[MAX_WADPATH-1] = '\0';
3279             }
3280         }
3281         FIL_DefaultExtension(demo_name, ".lmp");
3282 
3283         CONS_Printf("Playing demo %s.\n", demo_name);
3284 
3285         if( p == 2500 )
3286         {  // timedemo
3287             G_TimeDemo(demo_name);
3288         }
3289         else
3290         {  // playdemo
3291             singledemo = true;  // quit after one demo
3292             G_DeferedPlayDemo(demo_name);
3293         }
3294 
3295         gamestate = wipegamestate = GS_NULL;
3296 
3297         return;
3298       }
3299     }
3300 
3301     p = M_CheckParm("-loadgame");
3302     if (p && p < myargc - 1)
3303     {
3304         G_Load_Game(atoi(myargv[p + 1]));
3305     }
3306     else
3307     {
3308         if (dedicated && server)
3309         {
3310             pagename = "TITLEPIC";
3311             gamestate = GS_WAITINGPLAYERS;
3312         }
3313         else if (autostart || netgame
3314                  || M_CheckParm("+connect") || M_CheckParm("-connect"))
3315         {
3316             //added:27-02-98: reset the current version number
3317             G_setup_VERSION();
3318             gameaction = ga_nothing;
3319             if (server && !M_CheckParm("+map"))
3320                 COM_BufAddText(va("map \"%s\"\n", G_BuildMapName(startepisode, startmap)));
3321         }
3322         else
3323         {
3324             // Cancel commandline, restore cv_ var.
3325             D_StartTitle();     // start up intro loop
3326         }
3327 
3328     }
3329 
3330     drawmode_recalc = false;
3331     Clear_SoftError();
3332     // This leaves commands for the first COM_BufExecute in D_DoomLoop to execute.
3333 }
3334 
3335 
3336 // Print error and continue game [WDJ] 1/19/2009
3337 #define SOFTERROR_LISTSIZE   8
3338 static const char *  SE_msg[SOFTERROR_LISTSIZE];
3339 static uint32_t      SE_val[SOFTERROR_LISTSIZE]; // we only want to compare
3340 static int  SE_msgcnt = 0;
3341 static int  SE_next_msg_slot = 0;
3342 
3343 byte  EOUT_flags = EOUT_text | EOUT_log;  // EOUT_e
3344 
Clear_SoftError(void)3345 static void Clear_SoftError(void)
3346 {
3347    SE_msgcnt = 0;
3348 }
3349 
3350 
3351 // Print out error and continue program.  Maintains list of errors and
3352 // does not repeat error messages in recent history.
I_SoftError(const char * errmsg,...)3353 void I_SoftError (const char *errmsg, ...)
3354 {
3355     va_list     argptr;
3356     int         index;
3357     uint32_t    errval;
3358 
3359     // Message first.
3360     va_start (argptr,errmsg);
3361     errval = va_arg( argptr, uint32_t ) ; // sample it as an int, no matter what
3362     va_end (argptr);
3363 //  debug_Printf("errval=%d\n", errval );   // debug
3364     for( index = 0; index < SE_msgcnt; index ++ ){
3365        if( errmsg == SE_msg[index] ){
3366           if( errval == SE_val[index] ) goto done;	// it is a repeat msg
3367        }
3368     }
3369     // save comparison info
3370     SE_msg[SE_next_msg_slot] = errmsg;
3371     SE_val[SE_next_msg_slot] = errval;
3372     SE_next_msg_slot++;
3373     if( SE_next_msg_slot > SE_msgcnt )
3374        SE_msgcnt = SE_next_msg_slot;  // max
3375     if( SE_next_msg_slot >= SOFTERROR_LISTSIZE )
3376        SE_next_msg_slot = 0;  // wrap
3377     // Error, always prints EMSG_text
3378     fprintf (stderr, "Warn: ");
3379     va_start (argptr,errmsg);
3380     GenPrintf_va( EMSG_error, errmsg, argptr );  // handles EOUT_con
3381     va_end (argptr);
3382 
3383 done:
3384     fflush( stderr );
3385 }
3386 
3387 
3388 // The system independent quit and save config.
D_Quit_Save(quit_severity_e severity)3389 void D_Quit_Save ( quit_severity_e severity )
3390 {
3391     // Prevent recursive I_Quit(), mainly due to situation problems.
3392     static byte quitseq = 0;
3393     uint16_t *  endtext = NULL;
3394 
3395     // If this gets called twice, it cannot just return.
3396     // Some shutdown routine called I_Quit again due to an error and we
3397     // cannot get back to the previous invocation to finish the shutdown.
3398     if( quitseq == 0 )
3399     {
3400         quitseq = 1;
3401         //added:16-02-98: when recording a demo, should exit using 'q' key,
3402         //   but sometimes we forget and use 'F10'.. so save here too.
3403         if (demorecording)
3404            G_CheckDemoStatus();
3405     }
3406     if( quitseq < 2 )
3407     {
3408         quitseq = 2;
3409         D_Quit_NetGame ();
3410     }
3411     if( quitseq < 5 )
3412     {
3413         quitseq = 5;
3414         I_ShutdownSound();
3415     }
3416 #ifdef CDMUS
3417     if( quitseq < 6 )
3418     {
3419         quitseq = 6;
3420         I_ShutdownCD();
3421     }
3422 #endif
3423     if( quitseq < 8 )
3424     {
3425         quitseq = 8;
3426         if( severity == QUIT_normal )
3427         {
3428             M_SaveAllConfig();
3429         }
3430     }
3431     if( quitseq < 10 )
3432     {
3433         quitseq = 10;
3434         if( (severity == QUIT_normal)
3435              && ! M_CheckParm("-noendtext")  // normal spelling, docs
3436              && ! M_CheckParm("-noendtxt")   // previous versions
3437              && cv_textout.EV > 0 )
3438         {
3439             // [WDJ] Check on errors during I_Error shutdown.
3440             // Avoid repeat errors during bad environment shutdown.
3441             lumpnum_t endtxt_num = W_CheckNumForName("ENDOOM");
3442             if( VALID_LUMP(endtxt_num) )
3443                 endtext = W_CacheLumpNum( endtxt_num, PU_STATIC );
3444             // If there are any more errors, then do not show the end text.
3445         }
3446     }
3447     if( quitseq < 11 )
3448     {
3449         quitseq = 11;
3450         // Close open wad files.
3451         // Neccesity for a Mac.  Open files hang devices.
3452         W_Shutdown();
3453     }
3454     if( quitseq < 15 )
3455     {
3456         quitseq = 15;
3457         I_Shutdown_IO();
3458     }
3459     if( quitseq < 20 )
3460     {
3461         quitseq = 20;
3462         if( severity != QUIT_normal )
3463             I_Sleep( 3000 );  // to see some messages
3464         vid.draw_ready = 0;
3465         I_ShutdownGraphics();
3466         HU_Release_Graphics();
3467 #ifdef HWRENDER
3468         if( HWR_patchstore )
3469         {
3470             HWR_Shutdown_Render();
3471         }
3472 #endif
3473     }
3474     if( quitseq < 22 )
3475     {
3476         quitseq = 22;
3477         I_ShutdownSystem();
3478     }
3479     if( quitseq < 29 )
3480     {
3481         quitseq = 29;
3482         if( (severity == QUIT_normal)
3483           && endtext )
3484         {
3485             // Show the ENDOOM text
3486             printf("\r");
3487             I_Show_EndText( endtext );
3488         }
3489     }
3490 }
3491 
3492 // I_Quit exits with (exit 0).
3493 // No return
I_Quit(void)3494 void I_Quit (void)
3495 {
3496     D_Quit_Save( QUIT_normal );
3497     I_Quit_System();  // No Return
3498 }
3499 
3500 
Help(void)3501 static void Help( void )
3502 {
3503   char * np = M_GetNextParm();
3504 
3505   if( np == NULL )
3506   {
3507     printf
3508        ("Usage: doomlegacy [-opengl] [-iwad xxx.wad] [-file pwad.wad ...]\n"
3509         "--version   Print Doom Legacy version\n"
3510         "-h     Help\n"
3511         "-h g   Help game and wads\n"
3512         "-h m   Help multiplayer\n"
3513         "-h c   Help config\n"
3514         "-h s   Help server\n"
3515         "-h d   Help demo\n"
3516         "-h D   Help Devmode\n"
3517         );
3518      return;
3519   }
3520   switch( np[0] )
3521   {
3522    case 'g': // game
3523      printf
3524        (
3525         "-game name      doomu, doom2, tnt, plutonia, freedoom, heretic, chex1, etc.\n"
3526         "-iwad file      The game wad\n"
3527         "-file file      Load DEH and PWAD files (one or more)\n"
3528         "-deh  file      Load DEH files (one or more)\n"
3529         "-loadgame num   Load savegame num\n"
3530         "-episode 2      Goto episode 2, level 1\n"
3531         "-skill 3        Skill 1 to 5\n"
3532         "-warp 13        Goto map13\n"
3533         "-warp 1 3       Goto episode 1 level 3\n"
3534         "-nomonsters     No monsters\n"
3535         "-respawn        Monsters respawn after killed\n"
3536         "-coopmonsters   Monsters cooperate\n"
3537         "-infight        Monsters fight each other\n"
3538         "-fast           Monsters are fast\n"
3539         "-predicting     Monsters aim better\n"
3540         "-turbo num      Player speed %%, 10 to 255\n"
3541         );
3542      break;
3543    case 'm': // multiplayer
3544      printf
3545        (
3546         "-teamplay       Play with teams by color\n"
3547         "-teamskin       Play with teams using skins\n"
3548         "-splitscreen    Two players on this screen\n"
3549         "-deathmatch     Deathmatch, weapons respawn\n"
3550         "-altdeath       Deathmatch, items respawn\n"
3551         "-timer num      Timelimit in minutes\n"
3552         "-avg            Austin 20 min rounds\n"
3553         );
3554      break;
3555    case 'c': // config
3556      printf
3557        (
3558         "-v   -v2        Verbose\n"
3559         "-home name      Config and savegame directory\n"
3560         "-config file    Config file\n"
3561         "-opengl         OpenGL hardware renderer\n"
3562         "-nosound        No sound effects\n"
3563 #ifdef CDMUS
3564         "-nocd           No CD music\n"
3565 #endif
3566         "-nomusic        No music\n"
3567         "-precachesound  Preload sound effects\n"
3568         "-mb num         Pre-allocate num MiB of memory\n"
3569         "-window         No fullscreen\n"
3570         "-width num      Video mode width\n"
3571         "-height num     Video mode height\n"
3572         "-highcolor      Request 15bpp or 16bpp\n"
3573         "-truecolor      Request 24bpp or 32bpp\n"
3574         "-native         Video mode in native bpp\n"
3575         "-bpp num        Video mode in (8,15,16,24,32) bpp\n"
3576         "-nocheckwadversion   Ignore legacy.wad version\n"
3577 #ifdef BEX_LANGUAGE
3578         "-lang name      Load BEX language file name.bex\n"
3579 #endif
3580         );
3581      break;
3582    case 's': // server
3583      printf
3584        (
3585         "-server         Start as game server\n"
3586         "-dedicated      Dedicated server, no player\n"
3587         "-connect name   Connect to server name\n"
3588         "-bandwidth bps  Net bandwidth in bytes/sec\n"
3589         "-packetsize num Net packetsize\n"
3590         "-nodownload     No download from server\n"
3591         "-nofiles        Download all from server\n"
3592         "-clientport x   Use port x for client\n"
3593         "-udpport x      Use udp port x for server (and client)\n"
3594 #ifdef USE_IPX
3595         "-ipx            Use IPX\n"
3596 #endif
3597         "-extratic x     Send redundant player movement\n"
3598         "-debugfile file Log to debug file\n"
3599         "-left           Left slaved view\n"
3600         "-right          Right slaved view\n"
3601         "-screendeg x    Slaved view at x degrees\n"
3602         );
3603      break;
3604    case 'd': // demo
3605      printf
3606        (
3607         "-record file    Record demo to file\n"
3608         "-maxdemo num    Limit record demo size, in KiB\n"
3609         "-playdemo file  Play demo from file\n"
3610         );
3611      break;
3612    case 'D': // devmode
3613      printf
3614        (
3615         "-devparm        Develop mode\n"
3616 #ifdef DEVPARM_LOADING
3617         "-devgame gamename  Develop mode, and specify game\n"
3618         "-wart 3 1       Load file devmaps/E3M1.wad, then warp to it\n"
3619         "-wart 13        Load file devmaps/cdata/map13.wad, then warp to it\n"
3620 #endif
3621         "-timedemo file  Timedemo from file\n"
3622         "-nodraw         Timedemo without draw\n"
3623         "-noblit         Timedemo without blit\n"
3624         );
3625      break;
3626   }
3627 }
3628