/* Emacs style mode select   -*- C++ -*- */
/*-----------------------------------------------------------------------------*/

/* $Id:$*/

/* 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.*/

/* $Log:$*/

/* 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.8 1997/02/03 22:45:09 b1 Exp $";

#define	BGCOLOR		7
#define	FGCOLOR		8


#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#ifdef __riscos__
#include "ROsupport.h"
#include "GameSupp.h"
#else
#define accessWAD(file) access(file, R_OK)
#endif

#ifdef NORMALUNIX
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#endif


#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_local.h"
#include "r_context.h"

#include "d_main.h"


/* 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*/

static void D_DoomLoop (void);


const char*	wadfiles[MAXWADFILES];


boolean		devparm;	/* started game with -devparm*/
boolean         nomonsters;	/* checkparm of -nomonsters*/
boolean         respawnparm;	/* checkparm of -respawn*/
boolean         fastparm;	/* checkparm of -fast*/

/*boolean         drone;*/

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



skill_t		startskill;
int             startepisode;
int		startmap;
boolean		autostart;

FILE*		debugfile;
/* New file, will hold all output after the start screen has appeared */
FILE*		logfile;

boolean		advancedemo;

/* For calculating fps */
static int	fps_every=-1;
static int	fps_lasttic;
static int	fps_counter;
static char	fps_message[32];

int has_dehack;



/*static char	wadfile[1024];*/        /* primary wad file*/
/*char		mapdir[1024];*/         /* directory of development maps*/
char		basedefault[1024];      /* default file*/




/* 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 = 0;
int 		eventtail = 0;



/* D_PostEvent*/
/* Called by the I/O functions when input is detected*/

void D_PostEvent (const 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)
{
    event_t*	ev;

    /* IF STORE DEMO, DO NOT ACCEPT INPUT*/
    if ( ( gamemode == commercial )
	 && (W_CheckNumForName("map01")<0) )
      return;

    for ( ; eventtail != eventhead ; eventtail++, eventtail &= (MAXEVENTS-1) )
    {
	ev = &events[eventtail];
	if (M_Responder (ev))
	    continue;               /* menu ate the event*/
	G_Responder (ev);
    }
}





/* 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;

static 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				nowtime;
    int				tics;
    int				wipestart;
    int				y;
    int				colspertic;
    boolean			done;
    boolean			wipe;
    boolean			redrawsbar;

#ifdef DEBUG
    fprintf(logfile, "["); fflush(logfile);
#endif

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

    redrawsbar = false;

    /* We may have to clear the screen if we change from level to intermission/etc */
    if ((gamestate != GS_LEVEL) && (oldgamestate == GS_LEVEL))
        I_ClearFrameBuffers();

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

    /* save the current screen if about to wipe*/
    if (gamestate != wipegamestate)
    {
	wipe = true;
	wipe_StartScreen(0, 0, SCREENWIDTH, SCREENHEIGHT);
	memset(screens[0], 0, SCREENWIDTH * SCREENHEIGHT * sizeof(pixel_t));
    }
    else
	wipe = false;

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

    /* do buffered drawing*/
    switch (gamestate)
    {
      case GS_LEVEL:
	if (!gametic)
	    break;
	if (automapactive)
	    AM_Drawer ();
	if (wipe || (d_ctx.viewheight != SCREENHEIGHT && fullscreen) )
	    redrawsbar = true;
	if (inhelpscreensstate && !inhelpscreens)
	    redrawsbar = true;              /* just put away the help screen*/

	ST_Drawer (d_ctx.viewheight == SCREENHEIGHT, redrawsbar );
	fullscreen = d_ctx.viewheight == SCREENHEIGHT;
	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 (mixmap)
	    AM_Drawer ();
    }

    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*/

	/*
	 *  If we're not using frame buffers we have to make sure the
	 *  sides of the status bar are filled with the back pattern
	 *  as well.
	 */
#ifndef __riscos__
#ifdef STATIC_RESOLUTION
#if (SCREENWIDTH > 320)
	ST_Drawer(d_ctx.viewheight == SCREENHEIGHT, true);
#endif
#else
	if (SCREENWIDTH > 320)
	{
	    ST_Drawer(d_ctx.viewheight == SCREENHEIGHT, true);
	}
#endif
#endif
    }

    /* see if the border needs to be updated to the screen*/
    if (gamestate == GS_LEVEL && !automapactive && d_ctx.scaledviewwidth != SCREENWIDTH)
    {
	if (menuactive || menuactivestate || !viewactivestate)
	    borderdrawcount = 3;

	if (borderdrawcount)
	{
	    R_DrawViewBorder ();    /* erase old menu stuff*/
	    borderdrawcount--;
#ifdef __riscos__
            I_InvalidateArea(INVAL_BACKGRND);
#endif
	}

    }

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

    /* draw pause pic*/
    if (paused)
    {
	if (automapactive)
	    y = 4;
	else
	    y = viewwindowy+4;
	V_DrawPatchDirect(viewwindowx+(d_ctx.scaledviewwidth-68)/2,
			  ((SCREENHEIGHT-200)>>1)+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;

    colspertic = (SCREENWIDTH * SCREENHEIGHT + 320*200-1) / (320*200);

    do
    {
	do
	{
	    nowtime = I_GetTime ();
	    tics = nowtime - wipestart;
	} while (!tics);
	wipestart = nowtime;

	y = colspertic;
	do
	{
	    done = wipe_ScreenWipe(wipe_Melt, 0, 0, SCREENWIDTH, SCREENHEIGHT, tics);
	    y--;
	} while ((y > 0) && (!done));

	I_UpdateNoBlit ();
	M_Drawer ();                            /* menu is drawn even on top of wipes*/
	I_FinishUpdate ();                      /* page flip or blit buffer*/
    } while (!done);
#ifdef __riscos__
    if (BufferedKeyboard)
        GameSupp_FlushKeyPress();
#endif

#ifdef DEBUG
    fprintf(logfile, "]"); fflush(logfile);
#endif
}




/*  D_DoomLoop*/

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

    if (M_CheckParm ("-debugfile"))
    {
	char    filename[20];
	sprintf (filename,"debug%i.txt",consoleplayer);
	printf ("debug output to: %s\n",filename);
	debugfile = fopen (filename,"w");
    }

    I_InitGraphics ();

    fps_lasttic = I_GetTime(); fps_counter = 0;

    while (1)
    {
	/* 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*/
	}

	S_UpdateSounds (players[consoleplayer].mo);/* move positional sounds*/

	/* Calculate fps? */
	if (fps_every >= 0)
	{
	    if (++fps_counter >= fps_every)
	    {
		int newTic;
		newTic = I_GetTime();
		if (newTic != fps_lasttic)
		{
		    int major, minor;
		    minor = (fps_counter * 100 * TICRATE) / (newTic - fps_lasttic);
		    major = minor / 100; minor -= 100*major;
		    if (minor < 10)
			sprintf(fps_message, "%d.0%d fps", major, minor);
		    else
			sprintf(fps_message, "%d.%d fps", major, minor);
		    players[consoleplayer].message = fps_message;
		    fps_counter = 0; fps_lasttic = newTic;
		}
	    }
	}

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

#ifdef __riscos__
	sound_ready = 1;
#else
#ifndef SNDINTR
#ifndef SNDSERV
	/* Sound mixing for the buffer is snychronous.*/
	I_UpdateSound();
#endif
	/* Update sound output.*/
	I_SubmitSound();
#endif	/* SNDINTR */
#endif	/* __riscos__ */
    }
}




/*  DEMO LOOP*/

static int	demosequence;
static int	pagetic;
char*		pagename;



/* D_PageTicker*/
/* Handles timing for warped projection*/

void D_PageTicker (void)
{
    if (--pagetic < 0)
	D_AdvanceDemo ();
}




/* D_PageDrawer*/

void D_PageDrawer (void)
{
    V_DrawPatch ((SCREENWIDTH - 320) >> 1, (SCREENHEIGHT - 200) >> 1, 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 = 35 * 11;
	else
	    pagetic = 170;
	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 = 200;
	gamestate = GS_DEMOSCREEN;
	pagename = "CREDIT";
	break;
      case 3:
	G_DeferedPlayDemo ("demo2");
	break;
      case 4:
	gamestate = GS_DEMOSCREEN;
	if ( gamemode == commercial)
	{
	    pagetic = 35 * 11;
	    pagename = "TITLEPIC";
	    S_StartMusic(mus_dm2ttl);
	}
	else
	{
	    pagetic = 200;

	    if ( gamemode == retail )
	      pagename = "CREDIT";
	    else
	      pagename = "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*/
char            title[128];




/* D_AddFile*/

void D_AddFile (const char *file)
{
    int     numwadfiles;
    char    *newfile;

    for (numwadfiles = 0 ; wadfiles[numwadfiles] ; numwadfiles++)
	;

    newfile = (char*) malloc (strlen(file)+1);
    strcpy (newfile, file);

    wadfiles[numwadfiles] = newfile;
}


/* IdentifyVersion*/
/* Checks availability of IWAD files by name,*/
/* to determine whether registered/commercial features*/
/* should be executed (notably loading PWAD's).*/

static void IdentifyVersion (void)
{

    char*	doom1wad;
    char*	doomwad;
    char*	doomuwad;
    char*	doom2wad;

    char*	doom2fwad;
    char*	plutoniawad;
    char*	tntwad;

    int		flength;

    char	*home;
    char	*doomwaddir;

#ifdef NORMALUNIX
    home = getenv("HOME");
    if (!home)
      I_Error("Please set $HOME to your home directory");
    sprintf(basedefault, "%s/.doomrc", home);
#define USEWADEXT	".wad"
#endif

#ifdef __riscos__
    home = getenv("Doom$Path");
    if (!home)
      I_Error("Set Doom$Path first!");
    /* The dereferenced path already ends in a '.' */
    sprintf(basedefault, "%sdoomrc", home);
#define USEWADEXT
#endif

    doomwaddir = getenv("DOOMWADDIR");
    if (!doomwaddir)
        doomwaddir = CURRENTDIR;

    flength = strlen(doomwaddir) + 2;

    /* Reserve enough room at the end of the strings for a /wad extension */
    if ((doom2wad = (char*)malloc(flength+9)) != NULL)
	sprintf(doom2wad, "%s"DIRSEPSTR"doom2"USEWADEXT, doomwaddir);

    if ((doomuwad = (char*)malloc(flength+9)) != NULL)
	sprintf(doomuwad, "%s"DIRSEPSTR"doomu"USEWADEXT, doomwaddir);

    if ((doomwad = (char*)malloc(flength+8)) != NULL)
	sprintf(doomwad, "%s"DIRSEPSTR"doom"USEWADEXT, doomwaddir);

    if ((doom1wad = (char*)malloc(flength+9)) != NULL)
	sprintf(doom1wad, "%s"DIRSEPSTR"doom1"USEWADEXT, doomwaddir);

    if ((plutoniawad = (char*)malloc(flength+12)) != NULL)
	sprintf(plutoniawad, "%s"DIRSEPSTR"plutonia"USEWADEXT, doomwaddir);

    if ((tntwad = (char*)malloc(flength+7)) != NULL)
	sprintf(tntwad, "%s"DIRSEPSTR"tnt"USEWADEXT, doomwaddir);

    if ((doom2fwad = (char*)malloc(flength+10)) != NULL)
	sprintf(doom2fwad, "%s"DIRSEPSTR"doom2f"USEWADEXT, doomwaddir);

    if (!doom2wad || !doomuwad || !doomwad || !doom1wad || !plutoniawad ||
	!tntwad || !doom2fwad)
	I_Error("Not enough memory for WAD names");


    if (M_CheckParm ("-shdev"))
    {
	gamemode = shareware;
	devparm = true;
	D_AddFile (DEVDATA DIRSEPSTR"doom1"EXTSEPSTR"wad");
	D_AddFile (DEVMAPS DIRSEPSTR"data_se"DIRSEPSTR"texture1"EXTSEPSTR"lmp");
	D_AddFile (DEVMAPS DIRSEPSTR"data_se"DIRSEPSTR"pnames"EXTSEPSTR"lmp");
	strcpy (basedefault, DEVDATA DIRSEPSTR"default"EXTSEPSTR"cfg");
	return;
    }

    if (M_CheckParm ("-regdev"))
    {
	gamemode = registered;
	devparm = true;
	D_AddFile (DEVDATA DIRSEPSTR"doom"EXTSEPSTR"wad");
	D_AddFile (DEVMAPS DIRSEPSTR"data_se"DIRSEPSTR"texture1"EXTSEPSTR"lmp");
	D_AddFile (DEVMAPS DIRSEPSTR"data_se"DIRSEPSTR"texture2"EXTSEPSTR"lmp");
	D_AddFile (DEVMAPS DIRSEPSTR"data_se"DIRSEPSTR"pnames"EXTSEPSTR"lmp");
	strcpy (basedefault, DEVDATA DIRSEPSTR"default"EXTSEPSTR"cfg");
	return;
    }

    if (M_CheckParm ("-comdev"))
    {
	gamemode = commercial;
	devparm = true;
	D_AddFile (DEVDATA DIRSEPSTR"doom2"EXTSEPSTR"wad");
	D_AddFile (DEVMAPS DIRSEPSTR"cdata"DIRSEPSTR"texture1"EXTSEPSTR"lmp");
	D_AddFile (DEVMAPS DIRSEPSTR"cdata"DIRSEPSTR"pnames"EXTSEPSTR"lmp");
	strcpy (basedefault, DEVDATA DIRSEPSTR"default"EXTSEPSTR"cfg");
	return;
    }

    if ( !accessWAD (doom2fwad) )
    {
	gamemode = commercial;
	gamemission = doom2;
	/* C'est ridicule!*/
	/* Let's handle languages in config files, okay?*/
	language = french;
	printf("French version\n");
	D_AddFile (doom2fwad);
	return;
    }

    if ( !accessWAD (doom2wad) )
    {
	gamemode = commercial;
	gamemission = doom2;
	D_AddFile (doom2wad);
	return;
    }

    if ( !accessWAD (plutoniawad) )
    {
      gamemode = commercial; gamemission = pack_plut;
      D_AddFile (plutoniawad);
      return;
    }

    if ( !accessWAD (tntwad) )
    {
      gamemode = commercial; gamemission = pack_tnt;
      D_AddFile (tntwad);
      return;
    }

    if ( !accessWAD (doomuwad) )
    {
      gamemode = retail;
      D_AddFile (doomuwad);
      return;
    }

    if ( !accessWAD (doomwad) )
    {
      gamemode = registered;
      D_AddFile (doomwad);
      return;
    }

    if ( !accessWAD (doom1wad) )
    {
      gamemode = shareware;
      D_AddFile (doom1wad);
      return;
    }

    printf("Game mode indeterminate.\n");
    gamemode = indetermined;

    /* We don't abort. Let's see what the PWAD contains.*/
    /*exit(1);*/
    /*I_Error ("Game mode indeterminate\n");*/
}


/* IdentifyVersionByName
 * Checks availability of the IWAD identified by the "-iwad" option
 * to determine whether registered/commercial features
 * should be executed (notably loading PWAD's).
 * Returns 0 if successful.
 */

static int IdentifyVersionByName (const char *iwad)
{
    static const struct
    {
	const char *	name;
	GameMode_t	mode;
	GameMission_t	mission;
	Language_t	language;
    } ID[] =
    {
	{ "shareware", shareware, doom },
	{ "doom", registered, doom },
	{ "doomu", retail, doom },
	{ "doom2", commercial, doom2 },
	{ "doom2f", commercial, doom2, french },
	{ "tnt", commercial, pack_tnt },
	{ "plutonia", commercial, pack_plut },
	{ 0 }
    };
    int   i = 0, j = 0;
    char *home;
    char *doomwaddir;
    FILE *wadlist;
#ifdef NORMALUNIX
    char *wadfile;
#endif

    while (ID[i].name)
    {
	if (!strcmp (iwad, ID[i].name))
	    break;
	i++;
    }
    if (!ID[i].name)
        return 1;

#ifdef NORMALUNIX
    doomwaddir = getenv("DOOMWADDIR");
    if (!doomwaddir)
	doomwaddir = ".";

    wadfile = malloc (strlen (doomwaddir) + 10);
    if (!wadfile)
	I_Error ("Not enough memory to read IWAD files list");
    sprintf (wadfile, "%s/WadFiles", doomwaddir);

    wadlist = fopen (wadfile, "r");
    free (wadfile);
#endif

#ifdef __riscos__
    doomwaddir = getenv("DOOMWADDIR");
    if (!doomwaddir)
	doomwaddir = "@";

    wadlist = fopen ("Doom:WadFiles", "r");
#endif

    if (!wadlist)
        return 1; /* failed to open IWAD list file */

    j = i++;
    do
    {
	char *p;
	fgets (basedefault, sizeof (basedefault), wadlist);
	p = strpbrk (basedefault, " \t\n");
	if (p)
	  *p = 0;
	if (basedefault[0] && basedefault[0] != '#')
	    --i;
    } while (i && !feof (wadlist));
    fclose (wadlist);
    if (i)
	return 1;

    if (basedefault[0] == '-' && basedefault[1] == '\0')
	return 1; /* IWAD not present */

    if (accessWAD (basedefault))
	return 1; /* IWAD not found */

    gamemode = ID[j].mode;
    gamemission = ID[j].mission;
    language = ID[j].language;
    D_AddFile (basedefault);

#ifdef NORMALUNIX
    home = getenv("HOME");
    if (!home)
	I_Error("Please set $HOME to your home directory");
    sprintf(basedefault, "%s/.doomrc", home);
#endif

#ifdef __riscos__
    home = getenv("Doom$Path");
    if (!home)
	I_Error("Set Doom$Path first!");
    /* The dereferenced path already ends in a '.' */
    sprintf(basedefault, "%sdoomrc", home);
#endif

    return 0;
}


/* Find a Response File*/

static void FindResponseFile (void)
{
    int             i;
#define MAXARGVS        100

    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)
	    {
		printf ("\nNo such response file!");
		exit(1);
	    }
	    printf("Found response file %s!\n",&myargv[i][1]);
	    fseek (handle,0,SEEK_END);
	    size = ftell(handle);
	    fseek (handle,0,SEEK_SET);
	    if ((file = (char*)malloc (size)) == NULL)
		I_Error("Not enough memory for file");

	    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];
	    if ((myargv = (char**)malloc(sizeof(char *)*MAXARGVS)) == NULL)
		I_Error("Not enough memory for argv");

	    memset(myargv,0,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*/
	    printf("%d command-line args:\n",myargc);
	    for (k=1;k<myargc;k++)
		printf("%s\n",myargv[k]);

	    break;
	}
}



/*
 *  Put all strings here so they can be easily modified using DeHackEd
 */

char retail_message[] = 	"                         "
				"The Ultimate DOOM Startup v%i.%i"
				"                           ";

char shareware_message[] =	"                            "
				"DOOM Shareware Startup v%i.%i"
				"                           ";

char registered_message[] =	"                            "
				"DOOM Registered Startup v%i.%i"
				"                           ";

char commercial_message[] =	"                         "
				"DOOM 2: Hell on Earth v%i.%i"
				"                           ";

char pack_plut_message[] =	"                   "
				"DOOM 2: Plutonia Experiment v%i.%i"
				"                           ";

char pack_tnt_message[] =	"                     "
				"DOOM 2: TNT - Evilution v%i.%i"
				"                           ";

char public_message[] =		"                     "
				"Public DOOM - v%i.%i"
				"                           ";

char modified_banner[] =
	"===========================================================================\n"
	"ATTENTION:  This version of DOOM has been modified.  If you would like to\n"
	"get a copy of the original game, call 1-800-IDGAMES or see the readme file.\n"
	"        You will not receive technical support for modified games.\n"
	"                      press enter to continue\n"
	"===========================================================================\n";

char shareware_banner[] =
	"===========================================================================\n"
	"                                Shareware!\n"
	"===========================================================================\n";

char commercial_banner[] =
	"===========================================================================\n"
	"                 Commercial product - do not distribute!\n"
	"         Please report software piracy to the SPA: 1-800-388-PIR8\n"
	"===========================================================================\n";


#ifdef __riscos__
static void D_RemoveExitGuard(void)
{
  GameSupp_RemoveExitHandler();
}
#endif


/*
 *  If this is zero then sound and sprite names are not emulated. Those are _very_
 *  machine dependent and tend to go wrong. Use -dehack for level 0, -dehack1 for
 *  level 1.
 */
int DeHackEmulationLevel = 0;

/* D_DoomMain*/

void D_DoomMain (void)
{
    int			p;
    char		file[256];
    char*		message;

#ifdef __riscos__
#ifndef STATIC_RESOLUTION
    screen_desc		screenDesc;
#endif
#endif

    memset(&d_ctx, 0, sizeof(draw_context_t));

    /* Open the log file */
    if ((logfile = fopen(DOOMLOGFILE,"w")) == NULL)
	logfile = stderr;

    M_InitMenuData();
    M_InitMiscData();

    printf("[DIY ");
#ifdef DIYBOOM
    printf("Boom");
#endif

#ifdef INBUILT_DEHACKED
    printf(", DHE");
    /* First check if we have a level 1 emulation */
    p = M_CheckParm("-dehack1");
    if (p)
    {
    	DeHackEmulationLevel = 1;
    }
    else p = M_CheckParm("-dehack");

    has_dehack = p;

    /* if either switch is present we read in the DeHackEd file */
    if (p)
    {
	printf("(on)");
        while (++p < myargc)
        {
            /*
             *  All arguments between -dehack and the next switch are interpreted as
             *  DeHackEd patch files.
             */
            if (myargv[p][0] == '-') break;
            ApplyDehackPatch(myargv[p]);
        }
    }
#endif

    printf(", %dbpp", (1<<LD_PIXEL_DEPTH));

#ifndef STATIC_RESOLUTION
    /*
     *  Determine resolution at run-time. On RISC OS read first the actual screen-
     *  resolution. On all systems check the command line for resolution switches.
     */
#ifdef __riscos__
    ReadScreenInfo(&screenDesc);
    SCREENWIDTH = screenDesc.resx; SCREENHEIGHT = screenDesc.resy;
#else
    SCREENWIDTH = 320; SCREENHEIGHT = 200;
#endif
    /* Check command line */
    p = M_CheckParm ("-resx");
    if (p && p < myargc-1)
        SCREENWIDTH = atoi(myargv[p+1]);
    p = M_CheckParm ("-resy");
    if (p && p < myargc-1)
        SCREENHEIGHT = atoi(myargv[p+1]);

    printf(", DYN %d*%d (%d*%d)", SCREENWIDTH, SCREENHEIGHT, MAXSCREENWIDTH, MAXSCREENHEIGHT);
    if ((SCREENWIDTH > MAXSCREENWIDTH) || (SCREENHEIGHT > MAXSCREENHEIGHT))
    {
        printf("]\nUnsupported resolution!\n");
        exit(-1);
    }
    INV_ASPECT_RATIO = ((float)SCREENHEIGHT) / SCREENWIDTH;
    M_initForResolution();
    F_initForResolution();
    AM_initForResolution();
    ST_initForResolution();
    WI_initForResolution();
    R_DrawInitForResolution();
    R_PlaneInitForResolution();
#else
    printf(", STAT %d*%d", SCREENWIDTH, SCREENHEIGHT);
#endif

#if (defined(DIYTRANSPARENCY) && (LD_PIXEL_DEPTH > 3))
    I_InitTransparencyStates();
    printf(", TRANS");
#endif

    printf("]\n");

    p = M_CheckParm("-fps");
    if (p && (p <myargc-1))
    {
	if ((myargv[p+1][0] >= '0') && (myargv[p+1][0] <= '9'))
	{
	    fps_every = atoi(myargv[p+1]);
	}
    }

    FindResponseFile ();

    p = M_CheckParm ("-iwad");
    if (p == 0 || myargv[p+1] == 0 || IdentifyVersionByName (myargv[p+1]))
	IdentifyVersion ();

    setbuf (stdout, NULL);
    modifiedgame = false;

    nomonsters = M_CheckParm ("-nomonsters");
    respawnparm = M_CheckParm ("-respawn");
    fastparm = M_CheckParm ("-fast");
    devparm = M_CheckParm ("-devparm");
    if (M_CheckParm ("-altdeath"))
	deathmatch = 2;
    else if (M_CheckParm ("-deathmatch"))
	deathmatch = 1;

    switch ( gamemode )
    {
      case retail:
        message = retail_message; break;
      case shareware:
        message = shareware_message; break;
      case registered:
        message = registered_message; break;
      case commercial:
        if (gamemission == pack_plut) message = pack_plut_message;
        else if (gamemission == pack_tnt) message = pack_tnt_message;
        else message = commercial_message;
        break;
      default:
        message = public_message; break;
    }

    sprintf(title, message, VERSION/100, VERSION%100);

    printf ("%s\n",title);

    if (devparm)
	printf(D_DEVSTR);

    if (M_CheckParm("-cdrom"))
    {
	printf(D_CDROM);
	sprintf(basedefault, "%s"DIRSEPSTR"default"EXTSEPSTR"cfg", I_GetCDSaveDir());
    }

    /* turbo option*/
    if ( (p=M_CheckParm ("-turbo")) )
    {
	int     scale = 200;

	if (p<myargc-1)
	    scale = atoi (myargv[p+1]);
	if (scale < 10)
	    scale = 10;
	if (scale > 400)
	    scale = 400;
	printf ("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*/

    /* convenience hack to allow -wart e m to add a wad file*/
    /* prepend a tilde to the filename so wadfile will be reloadable*/
    p = M_CheckParm ("-wart");
    if (p)
    {
	myargv[p][4] = 'p';     /* big hack, change to -warp*/

	/* Map name handling.*/
	switch (gamemode )
	{
	  case shareware:
	  case retail:
	  case registered:
	    sprintf (file,"~"DEVMAPS DIRSEPSTR"E%cM%c"EXTSEPSTR"wad",
		     myargv[p+1][0], myargv[p+2][0]);
	    printf("Warping to Episode %s, Map %s.\n",
		   myargv[p+1],myargv[p+2]);
	    break;

	  case commercial:
	  default:
	    p = atoi (myargv[p+1]);
	    if (p<10)
	      sprintf (file,"~"DEVMAPS DIRSEPSTR"cdata"DIRSEPSTR"map0%i"EXTSEPSTR"wad", p);
	    else
	      sprintf (file,"~"DEVMAPS DIRSEPSTR"cdata"DIRSEPSTR"map%i"EXTSEPSTR"wad", p);
	    break;
	}
	D_AddFile (file);
    }

    p = M_CheckParm ("-file");
    if (p)
    {
	/* 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][0] != '-')
	    D_AddFile (myargv[p]);
    }

    p = M_CheckParm ("-playdemo");

    if (!p)
	p = M_CheckParm ("-timedemo");

    if (p && p < myargc-1)
    {
#ifdef __riscos__
        const char *b = myargv[p+1];

        /* Relative or absolute path? */
        while (*b != '\0')
        {
            if ((*b == ':') || (*b == '$')) break;
            b++;
        }
        if (*b == '\0')
            sprintf (file,"Doom:Demos.%s/lmp", myargv[p+1]);
        else
            sprintf (file,"%s/lmp", myargv[p+1]);
#else
	sprintf (file,"%s"EXTSEPSTR"lmp", myargv[p+1]);
#endif
	D_AddFile (file);
	printf("Playing demo %s.\n",file);
    }

    /* get skill / episode / map from parms*/
    startskill = sk_medium;
    startepisode = 1;
    startmap = 1;
    autostart = false;


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

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

    p = M_CheckParm ("-timer");
    if (p && p < myargc-1 && deathmatch)
    {
	int     time;
	time = atoi(myargv[p+1]);
	printf("Levels will end after %d minute",time);
	if (time>1)
	    printf("s");
	printf(".\n");
    }

    p = M_CheckParm ("-avg");
    if (p && p < myargc-1 && deathmatch)
	printf("Austin Virtual Gaming: Levels will end after 20 minutes\n");

    p = M_CheckParm ("-warp");
    if (p && p < myargc-1)
    {
	if (gamemode == commercial)
	    startmap = atoi (myargv[p+1]);
	else
	{
	    startepisode = myargv[p+1][0]-'0';
	    startmap = myargv[p+2][0]-'0';
	}
	autostart = true;
    }

    /* init subsystems*/
    V_Init ();

    printf ("M_LoadDefaults: Load system defaults.\n");
    M_LoadDefaults ();              /* load before initing other systems*/

#ifdef __riscos__
    if ((InstHandlerFlags & IHFlag_Exit) != 0)
    {
      _kernel_oserror *err = GameSupp_InstallExitHandler(NULL, NULL);

      if (err == NULL)
      {
        atexit(D_RemoveExitGuard);
      }
      else
      {
        fprintf(logfile, "Exit handler: %s\n", err->errmess);
      }
    }
#endif

    printf ("Z_Init: Init zone memory allocation daemon. \n");
    Z_Init ();

    printf ("W_Init: Init WADfiles.\n");
    W_InitMultipleFiles ((const char**)wadfiles);

/* We don't really give a fuck about registering... */
#if 0
    /* 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.*/
	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_CheckNumForName2(name[i],ns_sprites)<0)
		    I_Error("\nThis is not the registered version.");
    }
#endif

    /* Iff additonal PWAD files are used, print modified banner*/
    if (modifiedgame)
    {
	printf (modified_banner);
#if 0
	/* The text now again asks for a keypress (for dehacked consistency), but we just */
	/* ignore that. */
	getchar ();
#endif
    }


    /* Check and print which version is executed.*/
    switch ( gamemode )
    {
      case shareware:
      case indetermined:
	printf (shareware_banner);
	break;
      case registered:
      case retail:
      case commercial:
	printf (commercial_banner);
	break;

      default:
	/* Ouch.*/
	break;
    }

    printf ("M_Init: Init miscellaneous info.\n");
    M_Init ();

    printf ("R_Init: Init DOOM refresh daemon - ");
    R_Init ();

    printf ("\nP_Init: Init Playloop state.\n");
    P_Init ();

    printf ("I_Init: Setting up machine state.\n");
    I_Init ();

    printf ("D_CheckNetGame: Checking network game status.\n");
    D_CheckNetGame ();
#if (defined(INBUILT_DEHACKED) && !defined(FULL_NEW_FEATURES))
    /* No homing missiles or jumping in net games (might have been switched on by DeHackEd)! */
    if (netgame)
    {
	P_MakeHomingMissiles(0);
	jump_cheat = false;
    }
#endif

    printf ("S_Init: Setting up sound.\n");
    S_Init (snd_SfxVolume /* *8 */, snd_MusicVolume /* *8*/ );

    printf ("HU_Init: Setting up heads up display.\n");
    HU_Init ();

    printf ("ST_Init: Init status bar.\n");
    ST_Init ();

    /* check for a driver that wants intermission stats*/
    p = M_CheckParm ("-statcopy");
    if (p && p<myargc-1)
    {
	/* for statistics driver*/
	statcopy = (void*)atoi(myargv[p+1]);
	printf ("External statistics registered.\n");
    }

    /* start the apropriate game based on parms*/
    p = M_CheckParm ("-record");

    if (p && p < myargc-1)
    {
	G_RecordDemo (myargv[p+1]);
	autostart = true;
    }

    p = M_CheckParm ("-playdemo");
    if (p && p < myargc-1)
    {
        char *demolumpname;

        demolumpname = (char*)malloc(12);
        memset(demolumpname, 0, 12);
	singledemo = true;              /* quit after one demo*/
	W_ExtractFileBase(myargv[p+1], demolumpname);
	G_DeferedPlayDemo (demolumpname);
	D_DoomLoop ();  /* never returns*/
    }

    p = M_CheckParm ("-timedemo");
    if (p && p < myargc-1)
    {
        char *demolumpname;

        demolumpname = (char*)malloc(12);
        memset(demolumpname, 0, 12);
	singledemo = true;              /* quit after one demo*/
	W_ExtractFileBase(myargv[p+1], demolumpname);
	G_TimeDemo (demolumpname);
	D_DoomLoop ();  /* never returns*/
    }

    p = M_CheckParm ("-loadgame");
    if (p && p < myargc-1)
    {
        const char *gamepath = getenv(GamePathVar);

	if (gamepath == NULL)
	{
	    if (M_CheckParm("-cdrom"))
        	sprintf(file, "%s"DIRSEPSTR SAVEGAMENAME"%c"EXTSEPSTR SAVEGAMEEXT, I_GetCDSaveDir(), myargv[p+1][0]);
            else
        	sprintf(file, "%s"SAVEGAMENAME"%c"EXTSEPSTR SAVEGAMEEXT, DefaultGamePath, myargv[p+1][0]);
	}
	else
	{
	    sprintf(file, "%s"SAVEGAMENAME"%c"EXTSEPSTR SAVEGAMEEXT, gamepath, myargv[p+1][0]);
	}

	G_LoadGame (file);
    }

#ifdef DIYBOOM
    p = M_CheckParm("-compatibility");
    if (p)
    {
        compatibility = 1; demo_compatibility = 1;
    }
#endif

    if ( gameaction != ga_loadgame )
    {
	if (autostart || netgame)
	    G_InitNew (startskill, startepisode, startmap);
	else
	    D_StartTitle ();                /* start up intro loop*/

    }

    D_DoomLoop ();  /* never returns*/
}
