// Emacs style mode select   -*- C++ -*-
//-----------------------------------------------------------------------------
//
// $Id: d_main.c,v 1.54 1998/10/04 13:20:32 thldrmn Exp $
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This source is available for distribution and/or modification
// only under the terms of the DOOM Source Code License as
// published by id Software. All rights reserved.
//
// The source is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
// for more details.
//
//
// DESCRIPTION:
//  DOOM main program (D_DoomMain) and game loop (D_DoomLoop),
//  plus functions to determine game mode (shareware, registered),
//  parse command line parameters, configure game parameters (turbo),
//  and call the startup functions.
//
//-----------------------------------------------------------------------------

static const char rcsid[] = "$Id: d_main.c,v 1.54 1998/10/04 13:20:32 thldrmn Exp $";

#ifdef _MSC_VER //proff
#include <direct.h>
#include <io.h>
#define    F_OK    0    /* Check for file existence */
#define    W_OK    2    /* Check for write permission */
#define    R_OK    4    /* Check for read permission */
#define S_ISDIR(x) (((sbuf.st_mode & S_IFDIR)==S_IFDIR)?1:0)
#define FALSE false
#define TRUE true
#else //_MSC_VER
#include <unistd.h>
// proff 11/01/98: This is for mingw32 and cygwin32
#if defined (__MINGW32__) || defined (__CYGWIN32__)
#define FALSE 0
#define TRUE !FALSE
#endif
#endif //_MSC_VER
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "doomdef.h"
#include "doomstat.h"
#include "dstrings.h"
#include "sounds.h"
#include "z_zone.h"
#include "w_wad.h"
#include "s_sound.h"
#include "v_video.h"
#include "f_finale.h"
#include "f_wipe.h"
#include "m_argv.h"
#include "m_misc.h"
#include "m_menu.h"
#include "i_system.h"
#include "i_sound.h"
#include "i_video.h"
#include "g_game.h"
#include "hu_stuff.h"
#include "wi_stuff.h"
#include "st_stuff.h"
#include "am_map.h"
#include "p_setup.h"
#include "r_draw.h"
#include "r_main.h"
#include "d_main.h"
#include "d_deh.h"  // Ty 04/08/98 - Externalizations
#include "lprintf.h"  // jff 08/03/98 - declaration of lprintf

// DEHacked support - Ty 03/09/97
void ProcessDehFile(char *filename, char *outfilename);

void GetFirstMap(int *ep, int *map); // Ty 08/29/98 - add "-warp x" functionality

char *wadfiles[MAXWADFILES];
int wadfilesource[MAXWADFILES];  // Ty 08/29/98 - add source of lumps/files

boolean devparm;        // started game with -devparm

// jff 1/24/98 add new versions of these variables to remember command line
boolean clnomonsters;   // checkparm of -nomonsters
boolean clrespawnparm;  // checkparm of -respawn
boolean clfastparm;     // checkparm of -fast
// jff 1/24/98 end definition of command line version of play mode switches

boolean nomonsters;     // working -nomonsters
boolean respawnparm;    // working -respawn
boolean fastparm;       // working -fast

boolean singletics = false; // debug flag to cancel adaptiveness

//jff 1/22/98 parms for disabling music and sound
boolean nosfxparm;
boolean nomusicparm;

//jff 4/18/98
extern boolean inhelpscreens;

skill_t startskill;
int     startepisode;
int     startmap;
boolean autostart;
FILE    *debugfile;

boolean advancedemo;

extern boolean timingdemo, singledemo, demoplayback, fastdemo; // killough

char    wadfile[PATH_MAX+1];       // primary wad file
char    mapdir[PATH_MAX+1];        // directory of development maps
char    basedefault[PATH_MAX+1];   // default file
char    baseiwad[PATH_MAX+1];      // jff 3/23/98: iwad directory
char    basesavegame[PATH_MAX+1];  // killough 2/16/98: savegame directory

//jff 4/19/98 list of standard IWAD names
const char *const standard_iwads[]=
{
  "/doom2f.wad",
  "/doom2.wad",
  "/plutonia.wad",
  "/tnt.wad",
  "/doom.wad",
  "/doom1.wad",
};
static const int nstandard_iwads = sizeof standard_iwads/sizeof*standard_iwads;

void D_CheckNetGame (void);
void D_ProcessEvents (void);
void G_BuildTiccmd (ticcmd_t* cmd);
void D_DoAdvanceDemo (void);

//
// EVENT HANDLING
//
// Events are asynchronous inputs generally generated by the game user.
// Events can be discarded if no responder claims them
//

event_t events[MAXEVENTS];
int eventhead, eventtail;

//
// D_PostEvent
// Called by the I/O functions when input is detected
//
void D_PostEvent(event_t *ev)
{
  events[eventhead++] = *ev;
  eventhead &= MAXEVENTS-1;
}

//
// D_ProcessEvents
// Send all the events of the given timestamp down the responder chain
//

void D_ProcessEvents (void)
{
  // IF STORE DEMO, DO NOT ACCEPT INPUT
  if (gamemode != commercial || W_CheckNumForName("map01") >= 0)
    for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1))
      if (!M_Responder(events+eventtail))
        G_Responder(events+eventtail);
}

//
// D_Display
//  draw current display, possibly wiping it from the previous
//

// wipegamestate can be set to -1 to force a wipe on the next draw
gamestate_t    wipegamestate = GS_DEMOSCREEN;
extern boolean setsizeneeded;
extern int     showMessages;
void           R_ExecuteSetViewSize(void);

void D_Display (void)
{
  static boolean viewactivestate = false;
  static boolean menuactivestate = false;
  static boolean inhelpscreensstate = false;
  static boolean fullscreen = false;
  static gamestate_t oldgamestate = -1;
  static int borderdrawcount;
  int wipestart;
  boolean done, wipe, redrawsbar;

  if (nodrawers)                    // for comparative timing / profiling
    return;

  redrawsbar = false;

  if (setsizeneeded)                // change the view size if needed
    {
      R_ExecuteSetViewSize();
      oldgamestate = -1;            // force background redraw
      borderdrawcount = 3;
    }

  // save the current screen if about to wipe
  if ((wipe = gamestate != wipegamestate))
    wipe_StartScreen(0, 0, SCREENWIDTH, SCREENHEIGHT);

  if (gamestate == GS_LEVEL && gametic)
    HU_Erase();

  switch (gamestate)                // do buffered drawing
    {
    case GS_LEVEL:
      if (!gametic)
        break;
      if (automapactive)
        AM_Drawer();
// proff 08/18/98: Changed for high-res
      if (wipe || (viewheight != SCREENHEIGHT && fullscreen)
          || (inhelpscreensstate && !inhelpscreens))
	    { 
		    redrawsbar = true;  
		    R_DrawViewBorder(); // proff/nicolas 09/09/98 -- force redraw pattern
	    }                     // around status bar for high-res mode
      ST_Drawer(viewheight == SCREENHEIGHT, redrawsbar );
      fullscreen = viewheight == SCREENHEIGHT;
//      if (wipe || (viewheight != 200 && fullscreen)
//          || (inhelpscreensstate && !inhelpscreens))
//        redrawsbar = true;              // just put away the help screen
//      ST_Drawer(viewheight == 200, redrawsbar );
//      fullscreen = viewheight == 200;
      break;
    case GS_INTERMISSION:
      WI_Drawer();
      break;
    case GS_FINALE:
      F_Drawer();
      break;
    case GS_DEMOSCREEN:
      D_PageDrawer();
      break;
    }

  // draw buffered stuff to screen
  I_UpdateNoBlit();

  // draw the view directly
  if (gamestate == GS_LEVEL && !automapactive && gametic)
    R_RenderPlayerView (&players[displayplayer]);

  if (gamestate == GS_LEVEL && gametic)
    HU_Drawer ();

    // clean up border stuff
  if (gamestate != oldgamestate && gamestate != GS_LEVEL)
    I_SetPalette (W_CacheLumpName ("PLAYPAL",PU_CACHE));

  // see if the border needs to be initially drawn
  if (gamestate == GS_LEVEL && oldgamestate != GS_LEVEL)
    {
      viewactivestate = false;        // view was not active
      R_FillBackScreen ();    // draw the pattern into the back screen
	    // proff/nicolas 09/04/98 -- make sure that pattern
	    // around the statusbar is drawn in high-res mode at
	    // startup when statusbar width is shorter then SCREENWIDTH
      R_DrawViewBorder();
    }

  // see if the border needs to be updated to the screen
// proff 08/17/98: Changed for high-res
  if (gamestate == GS_LEVEL && !automapactive && scaledviewwidth != SCREENWIDTH)
//  if (gamestate == GS_LEVEL && !automapactive && scaledviewwidth != 320)
    {
      if (menuactive || menuactivestate || !viewactivestate)
        borderdrawcount = 3;
      if (borderdrawcount)
        {
          R_DrawViewBorder ();    // erase old menu stuff
          borderdrawcount--;
        }
    }

  menuactivestate = menuactive;
  viewactivestate = viewactive;
  inhelpscreensstate = inhelpscreens;
  oldgamestate = wipegamestate = gamestate;

  // draw pause pic
  if (paused)
    {
      int y = 4;
      if (!automapactive)
        y += viewwindowy;
	    // proff/nicolas 09/20/98 -- changed for hi-res
      V_DrawPatchStretched(viewwindowx+((SCREENWIDTH-320)/2)+(scaledviewwidth-68)/2,
                        (y*200)/SCREENHEIGHT,0,W_CacheLumpName ("M_PAUSE", PU_CACHE));
//      V_DrawPatchDirect(viewwindowx+(scaledviewwidth-68)/2,
//                        y,0,W_CacheLumpName ("M_PAUSE", PU_CACHE));
    }

  // menus go directly to the screen
  M_Drawer();          // menu is drawn even on top of everything
  NetUpdate();         // send out any new accumulation

  // normal update
  if (!wipe)
    {
      I_FinishUpdate ();              // page flip or blit buffer
      return;
    }

  // wipe update
  wipe_EndScreen(0, 0, SCREENWIDTH, SCREENHEIGHT);

  wipestart = I_GetTime () - 1;

  do
    {
      int nowtime, tics;
      do
        {
          nowtime = I_GetTime();
          tics = nowtime - wipestart;
        }
      while (!tics);
      wipestart = nowtime;
      done = wipe_ScreenWipe(wipe_Melt,0,0,SCREENWIDTH,SCREENHEIGHT,tics);
      I_UpdateNoBlit();
      M_Drawer();                   // menu is drawn even on top of wipes
      I_FinishUpdate();             // page flip or blit buffer
    }
  while (!done);
}

//
//  D_DoomLoop()
//
// Not a globally visible function,
//  just included for source reference,
//  called by D_DoomMain, never exits.
// Manages timing and IO,
//  calls all ?_Responder, ?_Ticker, and ?_Drawer,
//  calls I_GetTime, I_StartFrame, and I_StartTic
//

extern boolean demorecording;

static void D_DoomLoop(void)
{
  if (demorecording)
    G_BeginRecording ();

  if (M_CheckParm ("-debugfile"))
    {
      char    filename[20];
      sprintf(filename,"debug%i.txt",consoleplayer);
      //jff 9/3/98 use logical output routine
      lprintf(LO_DEBUG,"debug output to: %s\n",filename);
      debugfile = fopen(filename,"w");
    }

  I_InitGraphics ();

  atexit(D_QuitNetGame);       // killough

  for (;;)
    {
      // frame syncronous IO operations
      I_StartFrame ();

      // process one or more tics
      if (singletics)
        {
          I_StartTic ();
          D_ProcessEvents ();
          G_BuildTiccmd (&netcmds[consoleplayer][maketic%BACKUPTICS]);
          if (advancedemo)
            D_DoAdvanceDemo ();
          M_Ticker ();
          G_Ticker ();
          gametic++;
          maketic++;
        }
      else
        TryRunTics (); // will run at least one tic

      // killough 3/16/98: change consoleplayer to displayplayer
      S_UpdateSounds(players[displayplayer].mo);// move positional sounds

      // Update display, next frame, with current state.
      D_Display();

#ifndef SNDSERV
      // Sound mixing for the buffer is snychronous.
      I_UpdateSound();
#endif

#ifndef SNDINTR
      // Synchronous sound output is explicitly called.
      // Update sound output.
      I_SubmitSound();
#endif
    }
}

//
//  DEMO LOOP
//

static int  demosequence;         // killough 5/2/98: made static
static int  pagetic;
static char *pagename;

//
// D_PageTicker
// Handles timing for warped projection
//
void D_PageTicker(void)
{
  if (--pagetic < 0)
    D_AdvanceDemo();
}

//
// D_PageDrawer
//
void D_PageDrawer(void)
{
// proff/nicolas 09/14/98 -- now stretchs bitmaps to fullscreen!
  V_DrawPatchStretched (0, 0, 0, W_CacheLumpName(pagename, PU_CACHE));
//  V_DrawPatch (0, 0, 0, W_CacheLumpName(pagename, PU_CACHE));
}

//
// D_AdvanceDemo
// Called after each demo or intro demosequence finishes
//
void D_AdvanceDemo (void)
{
  advancedemo = true;
}

//
// This cycles through the demo sequences.
// FIXME - version dependend demo numbers?
//
void D_DoAdvanceDemo (void)
{
  players[consoleplayer].playerstate = PST_LIVE;  // not reborn
  advancedemo = false;
  usergame = false;               // no save / end game here
  paused = false;
  gameaction = ga_nothing;

  if ( gamemode == retail )
    demosequence = (demosequence+1)%7;
  else
    demosequence = (demosequence+1)%6;

    switch (demosequence)
      {
      case 0:
        if ( gamemode == commercial )
          pagetic = TICRATE * 11;        // killough 2/22/98: use TICRATE
        else
          pagetic = (TICRATE*170)/35;    // killough 2/22/98: use TICRATE
        gamestate = GS_DEMOSCREEN;
        pagename = "TITLEPIC";
        if ( gamemode == commercial )
          S_StartMusic(mus_dm2ttl);
        else
          S_StartMusic(mus_intro);
        break;
      case 1:
        G_DeferedPlayDemo("demo1");
        break;
      case 2:
        pagetic = (TICRATE * 200)/35;    // killough 2/22/98: use TICRATE
        gamestate = GS_DEMOSCREEN;
        pagename = "CREDIT";
        break;
      case 3:
        G_DeferedPlayDemo("demo2");
        break;
      case 4:
        gamestate = GS_DEMOSCREEN;
        if (gamemode == commercial)
          {
            pagetic = TICRATE * 11;
            pagename = "TITLEPIC";
            S_StartMusic(mus_dm2ttl);
          }
        else
          {
            pagetic = (TICRATE*200)/35;   // killough 2/22/98: use TICRATE
            pagename = gamemode == retail ? "CREDIT" : "HELP2";
          }
        break;
      case 5:
        G_DeferedPlayDemo ("demo3");
        break;
        // THE DEFINITIVE DOOM Special Edition demo
      case 6:
        G_DeferedPlayDemo ("demo4");
        break;
      }
}

//
// D_StartTitle
//
void D_StartTitle (void)
{
  gameaction = ga_nothing;
  demosequence = -1;
  D_AdvanceDemo();
}

// print title for every printed line
#ifdef _WIN32 // proff: I need this for the windowtitle
char title[128];
#else //_WIN32
static char title[128];
#endif //_WIN32

//
// D_AddFile
//
// Rewritten by Lee Killough
//
// Ty 08/29/98 - add source parm to indicate where this came from

void D_AddFile (char *file, int source)
{
  static int numwadfiles;

  if (numwadfiles >= MAXWADFILES)
    I_Error("Error: Number of WAD files exceeds limit of %d", MAXWADFILES);

  wadfiles[numwadfiles++] =
    AddDefaultExtension(strcpy(malloc(strlen(file)+5), file), ".wad");
  wadfilesource[numwadfiles-1] = source; // Ty 08/29/98

}

// Return the path where the executable lies -- Lee Killough
char *D_DoomExeDir(void)
{
  static char *base;
  if (!base)        // cache multiple requests
    {
      size_t len = strlen(*myargv);
      char *p = (base = malloc(len+1)) + len;
      strcpy(base,*myargv);
      while (p > base && *p!='/' && *p!='\\')
        *p--=0;
    }
  return base;
}

//
// CheckIWAD
//
// Verify a file is indeed tagged as an IWAD
// Scan its lumps for levelnames and return gamemode as indicated
// Detect missing wolf levels in DOOM II
//
// The filename to check is passed in iwadname, the gamemode detected is
// returned in gmode, hassec returns the presence of secret levels
//
// jff 4/19/98 Add routine to test IWAD for validity and determine
// the gamemode from it. Also note if DOOM II, whether secret levels exist
//
void CheckIWAD(char *iwadname, GameMode_t *gmode, boolean *hassec)
{
  char temp[320]; // buffer for error messages

  if ( !access (iwadname,R_OK) )
  {
    int ud=0,rg=0,sw=0,cm=0,sc=0;
    int handle;

    // Identify IWAD correctly
    if ( (handle = open (iwadname,O_RDONLY | O_BINARY)) != -1)
    {
      wadinfo_t header;

      // read IWAD header
      read (handle, &header, sizeof(header));
      if (!strncmp(header.identification,"IWAD",4))
      {
        size_t length;
        filelump_t *fileinfo;

        // read IWAD directory
        header.numlumps = LONG(header.numlumps);
        header.infotableofs = LONG(header.infotableofs);
        length = header.numlumps;
        fileinfo = malloc(length*sizeof(filelump_t));
        lseek (handle, header.infotableofs, SEEK_SET);
        read (handle, fileinfo, length*sizeof(filelump_t));
        close(handle);

        // scan directory for levelname lumps
        while (length--)
          if (fileinfo[length].name[0] == 'E' &&
              fileinfo[length].name[2] == 'M' &&
              fileinfo[length].name[4] == 0)
          {
            if (fileinfo[length].name[1] == '4')
              ++ud;
            else if (fileinfo[length].name[1] == '3')
              ++rg;
            else if (fileinfo[length].name[1] == '2')
              ++rg;
            else if (fileinfo[length].name[1] == '1')
              ++sw;
          }
          else if (fileinfo[length].name[0] == 'M' &&
                    fileinfo[length].name[1] == 'A' &&
                    fileinfo[length].name[2] == 'P' &&
                    fileinfo[length].name[5] == 0)
          {
            ++cm;
            if (fileinfo[length].name[3] == '3')
              if (fileinfo[length].name[4] == '1' ||
                  fileinfo[length].name[4] == '2')
                ++sc;
          }

        free(fileinfo);
      }
      else // missing IWAD tag in header
      {
        sprintf(temp,"IWAD tag not present: %s\n",iwadname);
        I_Error(temp);
      }
    }
    else // error from open call
    {
      sprintf(temp,"Can't open IWAD: %s\n",iwadname);
      I_Error(temp);
    }

    // Determine game mode from levels present
    // Must be a full set for whichever mode is present
    // Lack of wolf-3d levels also detected here

    *gmode = indetermined;
    *hassec = false;
    if (cm>=30)
    {
      *gmode = commercial;
      *hassec = sc>=2;
    }
    else if (ud>=9)
      *gmode = retail;
    else if (rg>=18)
      *gmode = registered;
    else if (sw>=9)
      *gmode = shareware;
  }
  else // error from access call
  {
    sprintf(temp,"IWAD not readable: %s\n",iwadname);
    I_Error(temp);
  }
}

//
// NormalizeSlashes
//
// Remove trailing slashes, translate backslashes to slashes
// The string to normalize is passed and returned in str
//
// jff 4/19/98 Make killoughs slash fixer a subroutine
//
void NormalizeSlashes(char *str)
{
  int l;

  // killough 1/18/98: Neater / \ handling.
  // Remove trailing / or \ to prevent // /\ \/ \\, and change \ to /

  if (!str || !(l = strlen(str)))
    return;
  if (str[--l]=='/' || str[l]=='\\')     // killough 1/18/98
    str[l]=0;
  while (l--)
    if (str[l]=='\\')
      str[l]='/';
}

// jff 4/19/98 Add routine to check a pathname for existence as
// a file or directory. If neither append .wad and check if it
// exists as a file then. Else return non-existent.

boolean WadFileStatus(char *filename,boolean *isdir)
{
  struct stat sbuf;
  int i;

  *isdir = false;                 //default is directory to false
  if (!filename || !*filename)    //if path NULL or empty, doesn't exist
    return false;

  if (!stat(filename,&sbuf))      //check for existence
  {
    *isdir=S_ISDIR(sbuf.st_mode); //if it does, set whether a dir or not
    return true;                  //return does exist
  }

  i = strlen(filename);           //get length of path
  if (i>=4)
    if(!strnicmp(filename+i-4,".wad",4))
      return false;               //if already ends in .wad, not found

  strcat(filename,".wad");        //try it with .wad added
  if (!stat(filename,&sbuf))      //if it exists then
  {
    if (S_ISDIR(sbuf.st_mode))    //but is a dir, then say we didn't find it
      return false;
    return true;                  //otherwise return file found, w/ .wad added
  }
  filename[i]=0;                  //remove .wad
  return false;                   //and report doesn't exist
}

//
// FindIWADFIle
//
// Search in all the usual places until an IWAD is found.
//
// The global baseiwad contains either a full IWAD file specification
// or a directory to look for an IWAD in, or the name of the IWAD desired.
//
// The global standard_iwads lists the standard IWAD names
//
// The result of search is returned in baseiwad, or set blank if none found
//
// IWAD search algorithm:
//
// Set customiwad blank
// If -iwad present set baseiwad to normalized path from -iwad parameter
//  If baseiwad is an existing file, thats it
//  If baseiwad is an existing dir, try appending all standard iwads
//  If haven't found it, and no : or / is in baseiwad,
//   append .wad if missing and set customiwad to baseiwad
//
// Look in . for customiwad if set, else all standard iwads
//
// Look in DoomExeDir. for customiwad if set, else all standard iwads
//
// If $DOOMWADDIR is an existing file
//  If customiwad is not set, thats it
//  else replace filename with customiwad, if exists thats it
// If $DOOMWADDIR is existing dir, try customiwad if set, else standard iwads
//
// If $HOME is an existing file
//  If customiwad is not set, thats it
//  else replace filename with customiwad, if exists thats it
// If $HOME is an existing dir, try customiwad if set, else standard iwads
//
// IWAD not found
//
// jff 4/19/98 Add routine to search for a standard or custom IWAD in one
// of the standard places. Returns a blank string if not found.
//
char *FindIWADFile()
{
  static char iwad[256];
  static char customiwad[256];
  boolean     isdir=false;
  int         i;
  char        *p;

  *iwad = 0;       // default return filename to empty
  *customiwad = 0; // customiwad is blank

  i = M_CheckParm("-iwad"); //jff 3/24/98 get -iwad parm if specified else use .
  if (i && i<myargc-1)
  {
    sprintf(baseiwad,"%s",myargv[i+1]);
    NormalizeSlashes(baseiwad);

    strcpy(iwad,baseiwad);
    if (WadFileStatus(iwad,&isdir))
    {
      if (!isdir)
        return iwad;
      else
      {
        for (i=0;i<nstandard_iwads;i++)
        {
          int n = strlen(iwad);
          strcat(iwad,standard_iwads[i]);
          if (WadFileStatus(iwad,&isdir) && !isdir)
            return iwad;
          iwad[n] = 0; // reset iwad length to former
        }
      }
    }
    else
    {
      if (!strchr(iwad,':') && !strchr(iwad,'/'))
        AddDefaultExtension(strcat(strcpy(customiwad, "/"), iwad), ".wad");
    }
  }

  strcpy(iwad,".");
  //jff 9/3/98 use logical output routine
  lprintf(LO_INFO,"Looking in %s\n",iwad);
  if (*customiwad)
  {
    strcat(iwad,customiwad);
    if (WadFileStatus(iwad,&isdir) && !isdir)
      return iwad;
  }
  else
  {
    for (i=0;i<nstandard_iwads;i++)
    {
      int n = strlen(iwad);
      strcat(iwad,standard_iwads[i]);
      if (WadFileStatus(iwad,&isdir) && !isdir)
        return iwad;
      iwad[n] = 0; // reset iwad length to former
    }
  }

  strcpy(iwad,D_DoomExeDir());
  NormalizeSlashes(iwad);
  //jff 9/3/98 use logical output routine
  lprintf(LO_INFO,"Looking in %s\n",iwad);
  if (*customiwad)
  {
    strcat(iwad,customiwad);
    if (WadFileStatus(iwad,&isdir) && !isdir)
      return iwad;
  }
  else
  {
    for (i=0;i<nstandard_iwads;i++)
    {
      int n = strlen(iwad);
      strcat(iwad,standard_iwads[i]);
      if (WadFileStatus(iwad,&isdir) && !isdir)
        return iwad;
      iwad[n] = 0; // reset iwad length to former
    }
  }

  p = getenv("DOOMWADDIR");
  if (p)
  {
    strcpy(iwad,p);
    NormalizeSlashes(iwad);
    if (WadFileStatus(iwad,&isdir))
    {
      if (!isdir)
      {
        if (!*customiwad)
        {
          //jff 9/3/98 use logical output routine
          lprintf(LO_INFO,"Looking for %s\n",iwad);
          return iwad;
        }
        else
        {
          p = strrchr(iwad,'/');
          if (p)
          {
            *p=0;
            strcat(iwad,customiwad);
            //jff 9/3/98 use logical output routine
            lprintf(LO_INFO,"Looking for %s\n",iwad);
            if (WadFileStatus(iwad,&isdir) && !isdir)
              return iwad;
          }
        }
      }
      else
      {
        //jff 9/3/98 use logical output routine
        lprintf(LO_INFO,"Looking in %s\n",iwad);
        if (*customiwad)
        {
          strcat(iwad,customiwad);
          if (WadFileStatus(iwad,&isdir) && !isdir)
            return iwad;
        }
        else
        {
          for (i=0;i<nstandard_iwads;i++)
          {
            int n = strlen(iwad);
            strcat(iwad,standard_iwads[i]);
            if (WadFileStatus(iwad,&isdir) && !isdir)
              return iwad;
            iwad[n] = 0; // reset iwad length to former
          }
        }
      }
    }
  }

  p = getenv("HOME");
  if (p)
  {
    strcpy(iwad,p);
    NormalizeSlashes(iwad);
    //jff 9/3/98 use logical output routine
    lprintf(LO_INFO,"Looking in %s\n",iwad);
    if (WadFileStatus(iwad,&isdir))
    {
      if (!isdir)
      {
        if (!*customiwad)
        {
          //jff 9/3/98 use logical output routine
          lprintf(LO_INFO,"Looking for %s\n",iwad);
          return iwad;
        }
        else
        {
          p = strrchr(iwad,'/');
          if (p)
          {
            *p=0;
            strcat(iwad,customiwad);
            //jff 9/3/98 use logical output routine
            lprintf(LO_INFO,"Looking for %s\n",iwad);
            if (WadFileStatus(iwad,&isdir) && !isdir)
              return iwad;
          }
        }
      }
      else
      {
        //jff 9/3/98 use logical output routine
        lprintf(LO_INFO,"Looking in %s\n",iwad);
        if (*customiwad)
        {
          strcat(iwad,customiwad);
          if (WadFileStatus(iwad,&isdir) && !isdir)
            return iwad;
        }
        else
        {
          for (i=0;i<nstandard_iwads;i++)
          {
            int n = strlen(iwad);
            strcat(iwad,standard_iwads[i]);
            if (WadFileStatus(iwad,&isdir) && !isdir)
              return iwad;
            iwad[n] = 0; // reset iwad length to former
          }
        }
      }
    }
  }

  *iwad = 0;
  return iwad;
}

//
// IdentifyVersion
//
// Set the location of the defaults file and the savegame root
// Locate and validate an IWAD file
// Determine gamemode from the IWAD
// 
// supports IWADs with custom names. Also allows the -iwad parameter to
// specify which iwad is being searched for if several exist in one dir.
// The -iwad parm may specify:
//
// 1) a specific pathname, which must exist (.wad optional)
// 2) or a directory, which must contain a standard IWAD,
// 3) or a filename, which must be found in one of the standard places:
//   a) current dir,
//   b) exe dir
//   c) $DOOMWADDIR
//   d) or $HOME
//
// jff 4/19/98 rewritten to use a more advanced search algorithm

void IdentifyVersion (void)
{
  int         i;    //jff 3/24/98 index of args on commandline
  struct stat sbuf; //jff 3/24/98 used to test save path for existence
  char *iwad;

  // get config file from same directory as executable

// proff 07/04/98: Changed from _MSC_VER to _WIN32 for compatibility to CYGWIN32
#ifdef _WIN32
  sprintf(basedefault,"%sprboom.cfg", D_DoomExeDir());  // proff
#else //_MSC_VER
  sprintf(basedefault,"%s/boom.cfg", D_DoomExeDir());  // killough
#endif //_MSC_VER

  // set save path to -save parm or current dir

  strcpy(basesavegame,".");       //jff 3/27/98 default to current dir
  if ((i=M_CheckParm("-save")) && i<myargc-1) //jff 3/24/98 if -save present
  {
    if (!stat(myargv[i+1],&sbuf) && S_ISDIR(sbuf.st_mode)) // and is a dir
    {
      strcpy(basesavegame,myargv[i+1]);  //jff 3/24/98 use that for savegame
      NormalizeSlashes(basesavegame);    //jff 9/22/98 fix c:\ not working
    }
    //jff 9/3/98 use logical output routine
    else lprintf(LO_ERROR,"Error: -save path does not exist, using current dir\n");
  }

  // locate the IWAD and determine game mode from it

  iwad = FindIWADFile();

  if (iwad && *iwad)
  {
    //jff 9/3/98 use logical output routine
    lprintf(LO_CONFIRM,"IWAD found: %s\n",iwad); //jff 4/20/98 print only if found
    CheckIWAD(iwad,&gamemode,&haswolflevels);

    //jff 8/23/98 set gamemission global appropriately in all cases
    switch(gamemode)
    {
      case retail:
        //jff 9/3/98 use logical output routine
        lprintf(LO_CONFIRM,"Ultimate DOOM version\n");
        gamemission = doom;
        break;
      case registered:
        //jff 9/3/98 use logical output routine
        lprintf(LO_CONFIRM,"DOOM Registered version\n");
        gamemission = doom;
        break;
      case shareware:
        //jff 9/3/98 use logical output routine
        lprintf(LO_CONFIRM,"DOOM Shareware version\n");
        gamemission = doom;
        break;
      case commercial:
        i = strlen(iwad);
        gamemission = doom2;
        if (i>=10 && !strnicmp(iwad+i-10,"doom2f.wad",10))
        {
          language=french;
          //jff 9/3/98 use logical output routine
          lprintf(LO_CONFIRM,"DOOM II version, french language\n");
        } 
        else if (i>=7 && !strnicmp(iwad+i-7,"tnt.wad",7))
        {
          gamemission = pack_tnt;
          //jff 9/3/98 use logical output routine
          lprintf(LO_CONFIRM,"TNT version\n");
        }
        else if (i>=12 && !strnicmp(iwad+i-12,"plutonia.wad",12))
        {
          gamemission = pack_plut;
          //jff 9/3/98 use logical output routine
          lprintf(LO_CONFIRM,"PLUTONIA version\n");
        }
        else
          //jff 9/3/98 use logical output routine
          lprintf(LO_CONFIRM,"DOOM II version%s\n",haswolflevels? "" : ", german edition, no wolf levels");
        break;
      default:
        gamemission = none;
        break;
    }
    if (gamemode == indetermined)
      //jff 9/3/98 use logical output routine
      lprintf(LO_WARN,"Unknown Game Version, may not work\n");
    D_AddFile(iwad,source_iwad);
  }
  else
    I_Error("IWAD not found\n");
}

// killough 5/3/98: old code removed

//
// Find a Response File
//

#define MAXARGVS 100

void FindResponseFile (void)
{
  int i;

  for (i = 1;i < myargc;i++)
    if (myargv[i][0] == '@')
      {
        FILE *handle;
        int  size;
        int  k;
        int  index;
        int  indexinfile;
        char *infile;
        char *file;
        char *moreargs[20];
        char *firstargv;

        // READ THE RESPONSE FILE INTO MEMORY
        handle = fopen (&myargv[i][1],"rb");
        if (!handle)
          {
            //jff 9/3/98 use logical output routine
            lprintf(LO_FATAL,"\nNo such response file!\n");
            exit(1);
          }
        //jff 9/3/98 use logical output routine
        lprintf(LO_CONFIRM,"Found response file %s!\n",&myargv[i][1]);
        fseek(handle,0,SEEK_END);
        size = ftell(handle);
        fseek(handle,0,SEEK_SET);
        file = malloc (size);
        fread(file,size,1,handle);
        fclose(handle);

        // KEEP ALL CMDLINE ARGS FOLLOWING @RESPONSEFILE ARG
        for (index = 0,k = i+1; k < myargc; k++)
          moreargs[index++] = myargv[k];

        firstargv = myargv[0];
        myargv = calloc(sizeof(char *),MAXARGVS);
        myargv[0] = firstargv;

        infile = file;
        indexinfile = k = 0;
        indexinfile++;  // SKIP PAST ARGV[0] (KEEP IT)
        do
          {
            myargv[indexinfile++] = infile+k;
            while(k < size &&
                  ((*(infile+k)>= ' '+1) && (*(infile+k)<='z')))
              k++;
            *(infile+k) = 0;
            while(k < size &&
                  ((*(infile+k)<= ' ') || (*(infile+k)>'z')))
              k++;
          }
        while(k < size);

        for (k = 0;k < index;k++)
          myargv[indexinfile++] = moreargs[k];
        myargc = indexinfile;

        // DISPLAY ARGS
        //jff 9/3/98 use logical output routine
        lprintf(LO_CONFIRM,"%d command-line args:\n",myargc);
        for (k=1;k<myargc;k++)
          //jff 9/3/98 use logical output routine
          lprintf(LO_CONFIRM,"%s\n",myargv[k]);
        break;
      }
}

//
// DoLooseFiles
//
// Take any file names on the command line before the first switch parm
// and insert the appropriate -file, -deh or -playdemo switch in front
// of them.
//
// Note that more than one -file, etc. entry on the command line won't
// work, so we have to go get all the valid ones if any that show up
// after the loose ones.  This means that boom fred.wad -file wilma
// will still load fred.wad and wilma.wad, in that order.
// The response file code kludges up its own version of myargv[] and 
// unfortunately we have to do the same here because that kludge only
// happens if there _is_ a response file.  Truth is, it's more likely
// that there will be a need to do one or the other so it probably 
// isn't important.  We'll point off to the original argv[], or the 
// area allocated in FindResponseFile, or our own areas from strdups.
//

void DoLooseFiles(void)
{
  char *wads[MAXARGVS];  // store the respective loose filenames
  char *lmps[MAXARGVS];
  char *dehs[MAXARGVS];
  int wadcount = 0;      // count the loose filenames
  int lmpcount = 0;
  int dehcount = 0;
  int i,j,p;
  char **tmyargv;  // use these to recreate the argv array
  int tmyargc;

  for (i=1;i<myargc;i++)
  {
    if (*myargv[i] == '-') break;  // quit at first switch 

    // so now we must have a loose file.  Find out what kind and store it.
    j = strlen(myargv[i]);
    if (!stricmp(&myargv[i][j-4],".wad")) 
      wads[wadcount++] = strdup(myargv[i]);
    if (!stricmp(&myargv[i][j-4],".lmp")) 
      lmps[lmpcount++] = strdup(myargv[i]);
    if (!stricmp(&myargv[i][j-4],".deh")) 
      dehs[dehcount++] = strdup(myargv[i]);
    if (!stricmp(&myargv[i][j-4],".bex")) 
      dehs[dehcount++] = strdup(myargv[i]);
    if (myargv[i][j-4] != '.')  // assume wad if no extension
      wads[wadcount++] = strdup(myargv[i]);
    *myargv[i] = '\0'; // nuke that entry so it won't repeat later
  }

  // Now, if we didn't find any loose files, we can just leave.
  if (wadcount+lmpcount+dehcount == 0) return;  // ******* early return ****

  if ((p = M_CheckParm ("-file")))
  {
    *myargv[p] = '\0';    // nuke the entry
    while (++p != myargc && *myargv[p] != '-')
    {
      wads[wadcount++] = strdup(myargv[p]);
      *myargv[p] = '\0';  // null any we find and save
    }
  }

  if ((p = M_CheckParm ("-deh")))
  {
    *myargv[p] = '\0';    // nuke the entry
    while (++p != myargc && *myargv[p] != '-')
    {
      dehs[dehcount++] = strdup(myargv[p]);
      *myargv[p] = '\0';  // null any we find and save
    }
  }

  if ((p = M_CheckParm ("-playdemo")))
  {
    *myargv[p] = '\0';    // nuke the entry
    while (++p != myargc && *myargv[p] != '-')
    {
      lmps[lmpcount++] = strdup(myargv[p]);
      *myargv[p] = '\0';  // null any we find and save
    }
  }

  // Now go back and redo the whole myargv array with our stuff in it.
  // First, create a new myargv array to copy into
  tmyargv = calloc(sizeof(char *),MAXARGVS);
  tmyargv[0] = myargv[0]; // invocation 
  tmyargc = 1;

  // put our stuff into it
  if (wadcount > 0)
  {
    tmyargv[tmyargc++] = strdup("-file"); // put the switch in
    for (i=0;i<wadcount;)
      tmyargv[tmyargc++] = wads[i++]; // allocated by strdup above
  }

  // for -deh
  if (dehcount > 0)
  {
    tmyargv[tmyargc++] = strdup("-deh"); 
    for (i=0;i<dehcount;)
      tmyargv[tmyargc++] = dehs[i++]; 
  }

  // for -playdemo
  if (lmpcount > 0)
  {
    tmyargv[tmyargc++] = strdup("-playdemo"); 
    for (i=0;i<lmpcount;)
      tmyargv[tmyargc++] = lmps[i++]; 
  }

  // then copy everything that's there now
  for (i=1;i<myargc;i++)
  {
    if (*myargv[i])  // skip any null entries
      tmyargv[tmyargc++] = myargv[i];  // pointers are still valid
  }
  // now make the global variables point to our array
  myargv = tmyargv;
  myargc = tmyargc;
}

//
// D_DoomMain
//

void D_DoomMain(void)
{
  int p,i;
  char file[PATH_MAX+1];      // killough 3/22/98
  char *cena="ICWEFDA",*pos;  //jff 9/3/98 use this for parsing console masks

  //jff 9/3/98 get mask for console output filter
  if ((p = M_CheckParm ("-cout")))
    if (++p != myargc && *myargv[p] != '-')
      for (i=0,cons_output_mask=0;i<strlen(myargv[p]);i++)
        if ((pos = strchr(cena,toupper(myargv[p][i]))))
          cons_output_mask |= (1<<(pos-cena));

  //jff 9/3/98 get mask for redirected console error filter
  if ((p = M_CheckParm ("-cerr")))
    if (++p != myargc && *myargv[p] != '-')
      for (i=0,cons_error_mask=0;i<strlen(myargv[p]);i++)
        if ((pos = strchr(cena,toupper(myargv[p][i]))))
          cons_error_mask |= (1<<(pos-cena));

  setbuf(stdout,NULL);

  FindResponseFile();
  DoLooseFiles();  // Ty 08/29/98 - handle "loose" files on command line
  IdentifyVersion();

  modifiedgame = false;

  // ty 03/09/98 do dehacked stuff
  // Note: do this before any other since it is expected by
  // the deh patch author that this is actually part of the EXE itself
  // Using -deh in BOOM, others use -dehacked.
  // Ty 03/18/98 also allow .bex extension.  .bex overrides if both exist.

  p = M_CheckParm ("-deh");
  if (p)
    {
      // the parms after p are deh/bex file names,
      // until end of parms or another - preceded parm
      // Ty 04/11/98 - Allow multiple -deh files in a row

      while (++p != myargc && *myargv[p] != '-')
        {
          AddDefaultExtension(strcpy(file, myargv[p]), ".bex");
          if (access(file, F_OK))  // nope
            {
              AddDefaultExtension(strcpy(file, myargv[p]), ".deh");
              if (access(file, F_OK))  // still nope
                I_Error("Cannot find .deh or .bex file named %s",myargv[p]);
            }
          // during the beta we have debug output to dehout.txt
          ProcessDehFile(file,"dehout.txt");
        }
    }
  // ty 03/09/98 end of do dehacked stuff

  // jff 1/24/98 set both working and command line value of play parms
  nomonsters = clnomonsters = M_CheckParm ("-nomonsters");
  respawnparm = clrespawnparm = M_CheckParm ("-respawn");
  fastparm = clfastparm = M_CheckParm ("-fast");
  // jff 1/24/98 end of set to both working and command line value

  devparm = M_CheckParm ("-devparm");

  if (M_CheckParm ("-altdeath"))
    deathmatch = 2;
  else
    if (M_CheckParm ("-deathmatch"))
      deathmatch = 1;

  switch ( gamemode )
    {
    case retail:
      sprintf (title,
               "                         "
               "The Ultimate DOOM Startup v%i.%02i"
               "                           ",
               VERSION/100,VERSION%100);
      break;
    case shareware:
      sprintf (title,
               "                            "
               "DOOM Shareware Startup v%i.%02i"
               "                           ",
               VERSION/100,VERSION%100);
      break;
    case registered:
      sprintf (title,
               "                            "
               "DOOM Registered Startup v%i.%02i"
               "                           ",
               VERSION/100,VERSION%100);
      break;
    case commercial:  // Ty 08/27/98 - fixed gamemode vs gamemission
      switch (gamemission)
      {
        case pack_plut:
          sprintf (title,
              "                   "
              "DOOM 2: Plutonia Experiment v%i.%02i"
              "                           ",
              VERSION/100,VERSION%100);
          break;
        case pack_tnt:
          sprintf (title,
              "                     "
              "DOOM 2: TNT - Evilution v%i.%02i"
              "                           ",
              VERSION/100,VERSION%100);
          break;
        default:
          sprintf (title,
               "                         "
               "DOOM 2: Hell on Earth v%i.%02i"
               "                           ",
               VERSION/100,VERSION%100);
          break;
      }
      break;
    default:
      sprintf (title,
               "                     "
               "Public DOOM - v%i.%i"
               "                           ",
               VERSION/100,VERSION%100);
      break;
    }

  //jff 9/3/98 use logical output routine
  lprintf (LO_ALWAYS,"%s\nBuilt on %s\n", title, version_date);    // killough 2/1/98

  if (devparm)
    //jff 9/3/98 use logical output routine
    lprintf(LO_CONFIRM,D_DEVSTR);

  if (M_CheckParm("-cdrom"))
    {
      //jff 9/3/98 use logical output routine
      lprintf(LO_CONFIRM,D_CDROM);
// proff: The definition of mkdir is different in Visual C
// proff 07/04/98: Changed for compatibility to MINGW32
#if defined(_MSC_VER) || defined (__MINGW32__)
      mkdir("c:\\doomdata");
#else //_MSC_VER
      mkdir("c:\\doomdata",0);
#endif //_MSC_VER
// proff 07/04/98: Changed from _MSC_VER to _WIN32 for compatibility to CYGWIN32
#ifdef _WIN32
      strcpy(basedefault, "c:/doomdata/prboom.cfg");       // proff
#else
      strcpy(basedefault, "c:/doomdata/boom.cfg");       // killough 3/22/98
#endif
    }

  // turbo option
  if ((p=M_CheckParm ("-turbo")))
    {
      int scale = 200;
      extern int forwardmove[2];
      extern int sidemove[2];

      if (p<myargc-1)
        scale = atoi(myargv[p+1]);
      if (scale < 10)
        scale = 10;
      if (scale > 400)
        scale = 400;
      //jff 9/3/98 use logical output routine
      lprintf (LO_CONFIRM,"turbo scale: %i%%\n",scale);
      forwardmove[0] = forwardmove[0]*scale/100;
      forwardmove[1] = forwardmove[1]*scale/100;
      sidemove[0] = sidemove[0]*scale/100;
      sidemove[1] = sidemove[1]*scale/100;
    }

  // add any files specified on the command line with -file wadfile
  // to the wad list

  // killough 1/31/98, 5/2/98: reload hack removed, -wart same as -warp now.

  if ((p = M_CheckParm ("-file")))
    {
      // the parms after p are wadfile/lump names,
      // until end of parms or another - preceded parm
      modifiedgame = true;            // homebrew levels
      while (++p != myargc && *myargv[p] != '-')
        D_AddFile(myargv[p],source_pwad);
    }

  if (!(p = M_CheckParm("-playdemo")) || p >= myargc-1)    // killough
    if ((p = M_CheckParm ("-fastdemo")) && p < myargc-1)   // killough
      fastdemo = true;             // run at fastest speed possible
    else
      p = M_CheckParm ("-timedemo");

  if (p && p < myargc-1)
    {
      strcpy(file,myargv[p+1]);
      AddDefaultExtension(file,".lmp");     // killough
      D_AddFile (file,source_lmp);
      //jff 9/3/98 use logical output routine
      lprintf(LO_CONFIRM,"Playing demo %s\n",file);
    }

  // get skill / episode / map from parms

  startskill = sk_none; // jff 3/24/98 was sk_medium, just note not picked
  startepisode = 1;
  startmap = 1;
  autostart = false;

  if ((p = M_CheckParm ("-skill")) && p < myargc-1)
    {
      startskill = myargv[p+1][0]-'1';
      autostart = true;
    }

  if ((p = M_CheckParm ("-episode")) && p < myargc-1)
    {
      startepisode = myargv[p+1][0]-'0';
      startmap = 1;
      autostart = true;
    }

  if ((p = M_CheckParm ("-timer")) && p < myargc-1 && deathmatch)
    {
      int time = atoi(myargv[p+1]);
      //jff 9/3/98 use logical output routine
      lprintf(LO_CONFIRM,"Levels will end after %d minute%s.\n", time, time>1 ? "s" : "");
    }

  if ((p = M_CheckParm ("-avg")) && p < myargc-1 && deathmatch)
    //jff 9/3/98 use logical output routine
    lprintf(LO_CONFIRM,"Austin Virtual Gaming: Levels will end after 20 minutes\n");

  if ((p = M_CheckParm ("-warp")) ||      // killough 5/2/98
       (p = M_CheckParm ("-wart"))) 
       // Ty 08/29/98 - moved this check later so we can have -warp alone: && p < myargc-1)
  {
    startmap = 0; // Ty 08/29/98 - allow "-warp x" to go to first map in wad(s)
    autostart = true; // Ty 08/29/98 - move outside the decision tree
    if (gamemode == commercial)
    {
      if (p < myargc-1) 
        startmap = atoi(myargv[p+1]);   // Ty 08/29/98 - add test if last parm
    }
    else    // 1/25/98 killough: fix -warp xxx from crashing Doom 1 / UD
    {
      if (p < myargc-2)
      {
        startepisode = atoi(myargv[++p]);
        startmap = atoi(myargv[p+1]);
      }
    }
  }
  // Ty 08/29/98 - later we'll check for startmap=0 and autostart=true
  // as a special case that -warp * was used.  Actually -warp with any
  // non-numeric will do that but we'll only document "*"

  //jff 1/22/98 add command line parms to disable sound and music
  {
    int nosound = M_CheckParm("-nosound");
    nomusicparm = nosound || M_CheckParm("-nomusic");
    nosfxparm   = nosound || M_CheckParm("-nosfx");
  }
  //jff end of sound/music command line parms

  // killough 3/2/98: allow -nodraw -noblit generally
  nodrawers = M_CheckParm ("-nodraw");
  noblit = M_CheckParm ("-noblit");

  // jff 4/21/98 allow writing predefined lumps out as a wad
  if ((p = M_CheckParm("-dumplumps")) && p < myargc-1)
    WritePredefinedLumpWad(myargv[p+1]);

  // init subsystems
  //jff 9/3/98 use logical output routine
  lprintf(LO_INFO,"V_Init: allocate screens.\n");
  V_Init();

  //jff 9/3/98 use logical output routine
  lprintf(LO_INFO,"M_LoadDefaults: Load system defaults.\n");
  M_LoadDefaults();              // load before initing other systems

  G_ReloadDefaults();    // killough 3/4/98: set defaults just loaded.
  // jff 3/24/98 this sets startskill if it was -1

  // internal translucency set to config file value               // phares
  general_translucency = default_translucency;                    // phares

  pitched_sounds = default_pitched_sounds;    // killough 2/21/98

  // 1/18/98 killough: Z_Init() call moved to i_main.c

  //jff 9/3/98 use logical output routine
  lprintf(LO_INFO,"W_Init: Init WADfiles.\n");
  W_InitMultipleFiles(wadfiles,wadfilesource);

  lprintf(LO_INFO,"\n");     // killough 3/6/98: add a newline, by popular demand :)

  // Check for -file in shareware
  if (modifiedgame)
    {
      // These are the lumps that will be checked in IWAD,
      // if any one is not present, execution will be aborted.
      static const char name[23][8]= {
        "e2m1","e2m2","e2m3","e2m4","e2m5","e2m6","e2m7","e2m8","e2m9",
        "e3m1","e3m3","e3m3","e3m4","e3m5","e3m6","e3m7","e3m8","e3m9",
        "dphoof","bfgga0","heada1","cybra1","spida1d1" };
      int i;

      if (gamemode == shareware)
        I_Error("\nYou cannot -file with the shareware version. Register!");

      // Check for fake IWAD with right name,
      // but w/o all the lumps of the registered version.
      if (gamemode == registered)
        for (i = 0;i < 23; i++)
          if (W_CheckNumForName(name[i])<0 &&
              (W_CheckNumForName)(name[i],ns_sprites)<0) // killough 4/18/98
            I_Error("\nThis is not the registered version.");
    }

  V_InitColorTranslation(); //jff 4/24/98 load color translation lumps

  // killough 2/22/98: copyright / "modified game" / SPA banners removed

  // Ty 04/08/98 - Add 5 lines of misc. data, only if nonblank
  // The expectation is that these will be set in a .bex file
  //jff 9/3/98 use logical output routine
  if (*startup1) lprintf(LO_INFO,startup1);
  if (*startup2) lprintf(LO_INFO,startup2);
  if (*startup3) lprintf(LO_INFO,startup3);
  if (*startup4) lprintf(LO_INFO,startup4);
  if (*startup5) lprintf(LO_INFO,startup5);
  // End new startup strings

  //jff 9/3/98 use logical output routine
  lprintf(LO_INFO,"M_Init: Init miscellaneous info.\n");
  M_Init();

  //jff 9/3/98 use logical output routine
  lprintf(LO_INFO,"R_Init: Init DOOM refresh daemon - ");
  R_Init();

  //jff 9/3/98 use logical output routine
  lprintf(LO_INFO,"\nP_Init: Init Playloop state.\n");
  P_Init();

  //jff 9/3/98 use logical output routine
  lprintf(LO_INFO,"I_Init: Setting up machine state.\n");
  I_Init();

  //jff 9/3/98 use logical output routine
  lprintf(LO_INFO,"D_CheckNetGame: Checking network game status.\n");
  D_CheckNetGame();

  //jff 9/3/98 use logical output routine
  lprintf(LO_INFO,"S_Init: Setting up sound.\n");
  S_Init(snd_SfxVolume /* *8 */, snd_MusicVolume /* *8*/ );

  //jff 9/3/98 use logical output routine
  lprintf(LO_INFO,"HU_Init: Setting up heads up display.\n");
  HU_Init();

  //jff 9/3/98 use logical output routine
  lprintf(LO_INFO,"ST_Init: Init status bar.\n");
  ST_Init();

  idmusnum = -1; //jff 3/17/98 insure idmus number is blank

  // check for a driver that wants intermission stats
  if ((p = M_CheckParm ("-statcopy")) && p<myargc-1)
    {
      // for statistics driver
      extern  void* statcopy;

      // killough 5/2/98: this takes a memory
      // address as an integer on the command line!

      statcopy = (void*) atoi(myargv[p+1]);
      //jff 9/3/98 use logical output routine
      lprintf (LO_CONFIRM,"External statistics registered.\n");
    }

  // start the apropriate game based on parms
  if ((p = M_CheckParm("-record")) && p < myargc-1)
    {
      demo_insurance = default_demo_insurance!=0;       // killough 4/4/98
      G_RecordDemo(myargv[p+1]);
      autostart = true;
    }

  if ((p = M_CheckParm("-playdemo")) && p < myargc-1)
    {
      singledemo = true;              // quit after one demo
      G_DeferedPlayDemo(myargv[p+1]);
      D_DoomLoop();  // never returns
    }

  if ((p = M_CheckParm ("-fastdemo")) && p < myargc-1)
    {                                 // killough
      singledemo = true;              // quit after one demo
      timingdemo = true;              // show stats after quit
      G_DeferedPlayDemo(myargv[p+1]);
      D_DoomLoop();  // never returns
    }

  if ((p = M_CheckParm("-timedemo")) && p < myargc-1)
    {
      G_TimeDemo(myargv[p+1]);
      D_DoomLoop();  // never returns
    }

  if ((p = M_CheckParm ("-loadgame")) && p < myargc-1)
    {
      int slot = atoi(myargv[p+1]);     // killough 3/16/98: add slot info
      G_SaveGameName(file, slot);       // killough 3/22/98
      G_LoadGame(file, slot, true);     // killough 5/15/98: add command flag
    }

  if (gameaction != ga_loadgame)
    if (autostart || netgame)  // Ty 08/29/98 get first map if startmap==0
    {
      GetFirstMap(&startepisode, &startmap);  // sets first map and first episode if unknown
      G_InitNew (startskill, startepisode, startmap);
    }
    else
      D_StartTitle();                // start up intro loop

  D_DoomLoop ();  // never returns
}

//
// GetFirstMap
//
// Ty 08/29/98 - determine first available map from the loaded wads and run it
// 

void GetFirstMap(int *ep, int *map)
{
  int i,j; // used to generate map name
  boolean done = FALSE;  // Ty 09/13/98 - to exit inner loops
  char test[6];  // MAPxx or ExMx plus terminator for testing
  char name[6];  // MAPxx or ExMx plus terminator for display
  boolean newlevel = FALSE;  // Ty 10/04/98 - to test for new level
  int ix;  // index for lookup

  strcpy(name,""); // initialize
  if (*map == 0) // unknown so go search for first changed one
  {
    *ep = 1;
    *map = 1; // default E1M1 or MAP01
    if (gamemode == commercial)
    {
      for (i=1;!done && i<33;i++)  // Ty 09/13/98 - add use of !done
      {
        sprintf(test,"MAP%02d",i);
        ix = W_CheckNumForName(test);
        if (ix != -1)  // Ty 10/04/98 avoid -1 subscript
        {
          if (lumpinfo[ix].source == source_pwad)
          {
            *map = i;
            strcpy(name,test);  // Ty 10/04/98
            done = TRUE;  // Ty 09/13/98
            newlevel = TRUE; // Ty 10/04/98
          }
          else
          {
            if (!*name)  // found one, not pwad.  First default.
               strcpy(name,test);
          }
        }
      }
    }
    else // one of the others
    {
      strcpy(name,"E1M1");  // Ty 10/04/98 - default for display
      for (i=1;!done && i<5;i++)  // Ty 09/13/98 - add use of !done
      {
        for (j=1;!done && j<10;j++)  // Ty 09/13/98 - add use of !done
        {
          sprintf(test,"E%dM%d",i,j);
          ix = W_CheckNumForName(test);
          if (ix != -1)  // Ty 10/04/98 avoid -1 subscript
          {
            if (lumpinfo[ix].source == source_pwad)
            {
              *ep = i;
              *map = j;
              strcpy(name,test); // Ty 10/04/98
              done = TRUE;  // Ty 09/13/98
              newlevel = TRUE; // Ty 10/04/98
            }
            else
            {
              if (!*name)  // found one, not pwad.  First default.
                 strcpy(name,test);
            }
          }
        }
      }
    }   
    //jff 9/3/98 use logical output routine
    lprintf(LO_CONFIRM,"Auto-warping to first %slevel: %s\n",
      newlevel ? "new " : "", name);  // Ty 10/04/98 - new level test
  }
}

//----------------------------------------------------------------------------
//
// $Log: d_main.c,v $
// Revision 1.54  1998/10/04  13:20:32  thldrmn
// Fix autowarp message and subscript bug
//
// Revision 1.53  1998/09/24  19:02:41  jim
// Fixed -save parm for x:/
//
// Revision 1.52  1998/09/14  01:27:38  thldrmn
// Fixed autowarp message for DOOM1/UDOOM
//
// Revision 1.51  1998/09/07  20:18:09  jim
// Added logical output routine
//
// Revision 1.49  1998/08/29  22:57:45  thldrmn
// Updates to -warp and switchless filename handling
//
// Revision 1.48  1998/08/24  20:28:26  jim
// gamemission support in IdentifyVersion
//
// Revision 1.47  1998/05/16  09:16:51  killough
// Make loadgame checksum friendlier
//
// Revision 1.46  1998/05/12  10:32:42  jim
// remove LEESFIXES from d_main
//
// Revision 1.45  1998/05/06  15:15:46  jim
// Documented IWAD routines
//
// Revision 1.44  1998/05/03  22:26:31  killough
// beautification, declarations, headers
//
// Revision 1.43  1998/04/24  08:08:13  jim
// Make text translate tables lumps
//
// Revision 1.42  1998/04/21  23:46:01  jim
// Predefined lump dumper option
//
// Revision 1.39  1998/04/20  11:06:42  jim
// Fixed print of IWAD found
//
// Revision 1.37  1998/04/19  01:12:19  killough
// Fix registered check to work with new lump namespaces
//
// Revision 1.36  1998/04/16  18:12:50  jim
// Fixed leak
//
// Revision 1.35  1998/04/14  08:14:18  killough
// Remove obsolete adaptive_gametics code
//
// Revision 1.34  1998/04/12  22:54:41  phares
// Remaining 3 Setup screens
//
// Revision 1.33  1998/04/11  14:49:15  thldrmn
// Allow multiple deh/bex files
//
// Revision 1.32  1998/04/10  06:31:50  killough
// Add adaptive gametic timer
//
// Revision 1.31  1998/04/09  09:18:17  thldrmn
// Added generic startup strings for BEX use
//
// Revision 1.30  1998/04/06  04:52:29  killough
// Allow demo_insurance=2, fix fps regression wrt redrawsbar
//
// Revision 1.29  1998/03/31  01:08:11  phares
// Initial Setup screens and Extended HELP screens
//
// Revision 1.28  1998/03/28  15:49:37  jim
// Fixed merge glitches in d_main.c and g_game.c
//
// Revision 1.27  1998/03/27  21:26:16  jim
// Default save dir offically . now
//
// Revision 1.26  1998/03/25  18:14:21  jim
// Fixed duplicate IWAD search in .
//
// Revision 1.25  1998/03/24  16:16:00  jim
// Fixed looking for wads message
//
// Revision 1.23  1998/03/24  03:16:51  jim
// added -iwad and -save parms to command line
//
// Revision 1.22  1998/03/23  03:07:44  killough
// Use G_SaveGameName, fix some remaining default.cfg's
//
// Revision 1.21  1998/03/18  23:13:54  jim
// Deh text additions
//
// Revision 1.19  1998/03/16  12:27:44  killough
// Remember savegame slot when loading
//
// Revision 1.18  1998/03/10  07:14:58  jim
// Initial DEH support added, minus text
//
// Revision 1.17  1998/03/09  07:07:45  killough
// print newline after wad files
//
// Revision 1.16  1998/03/04  08:12:05  killough
// Correctly set defaults before recording demos
//
// Revision 1.15  1998/03/02  11:24:25  killough
// make -nodraw -noblit work generally, fix ENDOOM
//
// Revision 1.14  1998/02/23  04:13:55  killough
// My own fix for m_misc.c warning, plus lots more (Rand's can wait)
//
// Revision 1.11  1998/02/20  21:56:41  phares
// Preliminarey sprite translucency
//
// Revision 1.10  1998/02/20  00:09:00  killough
// change iwad search path order
//
// Revision 1.9  1998/02/17  06:09:35  killough
// Cache D_DoomExeDir and support basesavegame
//
// Revision 1.8  1998/02/02  13:20:03  killough
// Ultimate Doom, -fastdemo -nodraw -noblit support, default_compatibility
//
// Revision 1.7  1998/01/30  18:48:15  phares
// Changed textspeed and textwait to functions
//
// Revision 1.6  1998/01/30  16:08:59  phares
// Faster end-mission text display
//
// Revision 1.5  1998/01/26  19:23:04  phares
// First rev with no ^Ms
//
// Revision 1.4  1998/01/26  05:40:12  killough
// Fix Doom 1 crashes on -warp with too few args
//
// Revision 1.3  1998/01/24  21:03:04  jim
// Fixed disappearence of nomonsters, respawn, or fast mode after demo play or IDCLEV
//
// Revision 1.1.1.1  1998/01/19  14:02:53  rand
// Lee's Jan 19 sources
//
//----------------------------------------------------------------------------
