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


#include "doom_config.h"

#if defined(DOOM_WIN32)


#define X_OK 0
#define W_OK 2
#define R_OK 4
#define RW_OK 6
#elif defined(DOOM_APPLE)


#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 "d_main.h"


#define MAXARGVS 100


char* wadfiles[MAXWADFILES];

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

doom_boolean drone;

doom_boolean singletics = true; // debug flag to cancel adaptiveness

doom_boolean is_wiping_screen = false;

skill_t startskill;
int startepisode;
int startmap;
doom_boolean autostart;

void* debugfile = 0;

doom_boolean advancedemo;

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;
int eventtail;

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

// print title for every printed line
char title[128];


extern  doom_boolean inhelpscreens;
extern doom_boolean setsizeneeded;
extern int showMessages;
extern doom_boolean demorecording;


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


//
// D_PostEvent
// Called by the I/O functions when input is detected
//
void D_PostEvent(event_t* ev)
{
    events[eventhead] = *ev;
    eventhead++;
    eventhead = (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; )
    {
        ev = &events[eventtail];
        if (!M_Responder(ev))
            G_Responder(ev);
        // else menu ate the event
        eventtail++;
        eventtail = (eventtail) & (MAXEVENTS - 1);
    }
}


//
// D_Display
//  draw current display, possibly wiping it from the previous
//
void D_Display(void)
{
    static doom_boolean viewactivestate = false;
    static doom_boolean menuactivestate = false;
    static doom_boolean inhelpscreensstate = false;
    static doom_boolean fullscreen = false;
    static gamestate_t oldgamestate = -1;
    static int borderdrawcount;
    int wipestart;
    int y;
    doom_boolean wipe;
    doom_boolean redrawsbar;

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

    redrawsbar = false;

    // 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);
    }
    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 || (viewheight != 200 && fullscreen))
                redrawsbar = true;
            if (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
    }

    // see if the border needs to be updated to the screen
    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)
    {
        if (automapactive)
            y = 4;
        else
            y = viewwindowy + 4;
        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
    is_wiping_screen = wipe;
    if (!wipe)
    {
        I_FinishUpdate(); // page flip or blit buffer
        return;
    }

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

    wipestart = I_GetTime() - 1;

#if 0 // [pd] Moved to D_UpdateWipe
    do
    {
        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);
#endif
}


//
//  D_DoomLoop
//
void D_UpdateWipe(void)
{
    if (wipe_ScreenWipe(wipe_Melt, 0, 0, SCREENWIDTH, SCREENHEIGHT, 1))
        is_wiping_screen = false;
}


void D_DoomLoop(void)
{
#if 0 // [pd] Moved to D_DoomMain()
    if (demorecording)
        G_BeginRecording();

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

    I_InitGraphics();
#endif

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

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

#if 0 // [pd] Sound is queried by the application's audio thread.
        // Sound mixing for the buffer is snychronous.
        I_UpdateSound();

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


//
//  DEMO LOOP
//
int demosequence;
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(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 = 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();
}


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

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

    newfile = doom_malloc(doom_strlen(file) + 1);
    doom_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).
//
void IdentifyVersion(void)
{
    char* doom1wad;
    char* doomwad;
    char* doomuwad;
    char* doom2wad;

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

    char* home;
    char* doomwaddir;
    doomwaddir = doom_getenv("DOOMWADDIR");

    if (!doomwaddir)
        doomwaddir = ".";

    // Commercial.
    doom2wad = doom_malloc(doom_strlen(doomwaddir) + 1 + 9 + 1);
    //doom_sprintf(doom2wad, "%s/doom2.wad", doomwaddir);
    doom_strcpy(doom2wad, doomwaddir);
    doom_concat(doom2wad, "/doom2.wad");

    // Retail.
    doomuwad = doom_malloc(doom_strlen(doomwaddir) + 1 + 8 + 1);
    //doom_sprintf(doomuwad, "%s/doomu.wad", doomwaddir);
    doom_strcpy(doomuwad, doomwaddir);
    doom_concat(doomuwad, "/doomu.wad");

    // Registered.
    doomwad = doom_malloc(doom_strlen(doomwaddir) + 1 + 8 + 1);
    //doom_sprintf(doomwad, "%s/doom.wad", doomwaddir);
    doom_strcpy(doomwad, doomwaddir);
    doom_concat(doomwad, "/doom.wad");

    // Shareware.
    doom1wad = doom_malloc(doom_strlen(doomwaddir) + 1 + 9 + 1);
    //doom_sprintf(doom1wad, "%s/doom1.wad", doomwaddir);
    doom_strcpy(doom1wad, doomwaddir);
    doom_concat(doom1wad, "/doom1.wad");

    // Bug, dear Shawn.
   // Insufficient malloc, caused spurious realloc errors.
    plutoniawad = doom_malloc(doom_strlen(doomwaddir) + 1 +/*9*/12 + 1);
    //doom_sprintf(plutoniawad, "%s/plutonia.wad", doomwaddir);
    doom_strcpy(plutoniawad, doomwaddir);
    doom_concat(plutoniawad, "/plutonia.wad");

    tntwad = doom_malloc(doom_strlen(doomwaddir) + 1 + 9 + 1);
    //doom_sprintf(tntwad, "%s/tnt.wad", doomwaddir);
    doom_strcpy(tntwad, doomwaddir);
    doom_concat(tntwad, "/tnt.wad");

    // French stuff.
    doom2fwad = doom_malloc(doom_strlen(doomwaddir) + 1 + 10 + 1);
    //doom_sprintf(doom2fwad, "%s/doom2f.wad", doomwaddir);
    doom_strcpy(doom2fwad, doomwaddir);
    doom_concat(doom2fwad, "/doom2f.wad");

#if !defined(DOOM_WIN32)
    home = doom_getenv("HOME");
    if (!home)
        I_Error("Error: Please set $HOME to your home directory");
#else
    home = ".";
#endif
    //doom_sprintf(basedefault, "%s/.doomrc", home);
    doom_strcpy(basedefault, home);
    doom_concat(basedefault, "/.doomrc");

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

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

    if (M_CheckParm("-comdev"))
    {
        gamemode = commercial;
        devparm = true;
        /* I don't bother
        if(plutonia)
            D_AddFile (DEVDATA"plutonia.wad");
        else if(tnt)
            D_AddFile (DEVDATA"tnt.wad");
        else*/
        D_AddFile(DEVDATA"doom2.wad");

        D_AddFile(DEVMAPS"cdata/texture1.lmp");
        D_AddFile(DEVMAPS"cdata/pnames.lmp");
        doom_strcpy(basedefault, DEVDATA"default.cfg");
        return;
    }

    void* f;
    if (f = doom_open(doom2fwad, "rb"))
    {
        doom_close(f);
        gamemode = commercial;
        // C'est ridicule!
        // Let's handle languages in config files, okay?
        language = french;
        doom_print("French version\n");
        D_AddFile(doom2fwad);
        return;
    }

    if (f = doom_open(doom2wad, "rb"))
    {
        doom_close(f);
        gamemode = commercial;
        D_AddFile(doom2wad);
        return;
    }

    if (f = doom_open(plutoniawad, "rb"))
    {
        doom_close(f);
        gamemode = commercial;
        D_AddFile(plutoniawad);
        return;
    }

    if (f = doom_open(tntwad, "rb"))
    {
        doom_close(f);
        gamemode = commercial;
        D_AddFile(tntwad);
        return;
    }

    if (f = doom_open(doomuwad, "rb"))
    {
        doom_close(f);
        gamemode = retail;
        D_AddFile(doomuwad);
        return;
    }

    if (f = doom_open(doomwad, "rb"))
    {
        doom_close(f);
        gamemode = registered;
        D_AddFile(doomwad);
        return;
    }

    if (f = doom_open(doom1wad, "rb"))
    {
        doom_close(f);
        gamemode = shareware;
        D_AddFile(doom1wad);
        return;
    }

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


//
// Find a Response File
//
void FindResponseFile(void)
{
    int i;

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

            // READ THE RESPONSE FILE INTO MEMORY
            handle = doom_open(&myargv[i][1], "rb");
            if (!handle)
            {
                doom_print("\nNo such response file!");
                doom_exit(1);
            }
            doom_print("Found response file %s!\n");
            doom_print(&myargv[i][1]);
            doom_print("!\n");
            doom_seek(handle, 0, DOOM_SEEK_END);
            size = doom_tell(handle);
            doom_seek(handle, 0, DOOM_SEEK_SET);
            file = doom_malloc(size);
            doom_read(handle, file, size * 1);
            doom_close(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 = doom_malloc(sizeof(char*) * MAXARGVS);
            doom_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
            doom_print(doom_itoa(myargc, 10));
            doom_print(" command-line args:\n");
            for (k = 1; k < myargc; k++)
            {
                //doom_print("%s\n", myargv[k]);
                doom_print(myargv[k]);
                doom_print("\n");
            }

            break;
        }
}


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

    FindResponseFile();

    IdentifyVersion();

    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:
            //doom_sprintf(title,
            //        "                         "
            //        "The Ultimate DOOM Startup v%i.%i"
            //        "                           ",
            //        VERSION / 100, VERSION % 100);
            doom_strcpy(title, "                         " "The Ultimate DOOM Startup v");
            doom_concat(title, doom_itoa(VERSION / 100, 10));
            doom_concat(title, ".");
            doom_concat(title, doom_itoa(VERSION % 100, 10));
            doom_concat(title, "                           ");
            break;
        case shareware:
            //doom_sprintf(title,
            //        "                            "
            //        "DOOM Shareware Startup v%i.%i"
            //        "                           ",
            //        VERSION / 100, VERSION % 100);
            doom_strcpy(title, "                            " "DOOM Shareware Startup v");
            doom_concat(title, doom_itoa(VERSION / 100, 10));
            doom_concat(title, ".");
            doom_concat(title, doom_itoa(VERSION % 100, 10));
            doom_concat(title, "                           ");
            break;
        case registered:
            //doom_sprintf(title,
            //        "                            "
            //        "DOOM Registered Startup v%i.%i"
            //        "                           ",
            //        VERSION / 100, VERSION % 100);
            doom_strcpy(title, "                            " "DOOM Registered Startup v");
            doom_concat(title, doom_itoa(VERSION / 100, 10));
            doom_concat(title, ".");
            doom_concat(title, doom_itoa(VERSION % 100, 10));
            doom_concat(title, "                           ");
            break;
        case commercial:
            //doom_sprintf(title,
            //        "                         "
            //        "DOOM 2: Hell on Earth v%i.%i"
            //        "                           ",
            //        VERSION / 100, VERSION % 100);
            doom_strcpy(title, "                         " "DOOM 2: Hell on Earth v");
            doom_concat(title, doom_itoa(VERSION / 100, 10));
            doom_concat(title, ".");
            doom_concat(title, doom_itoa(VERSION % 100, 10));
            doom_concat(title, "                           ");
            break;
            /*FIXME
                   case pack_plut:
                    sprintf (title,
                             "                   "
                             "DOOM 2: Plutonia Experiment v%i.%i"
                             "                           ",
                             VERSION/100,VERSION%100);
                    break;
                  case pack_tnt:
                    sprintf (title,
                             "                     "
                             "DOOM 2: TNT - Evilution v%i.%i"
                             "                           ",
                             VERSION/100,VERSION%100);
                    break;
            */
        default:
            //doom_sprintf(title,
            //        "                     "
            //        "Public DOOM - v%i.%i"
            //        "                           ",
            //        VERSION / 100, VERSION % 100);
            doom_strcpy(title, "                     " "Public DOOM - v");
            doom_concat(title, doom_itoa(VERSION / 100, 10));
            doom_concat(title, ".");
            doom_concat(title, doom_itoa(VERSION % 100, 10));
            doom_concat(title, "                           ");
            break;
    }

    //doom_print("%s\n", title);
    doom_print(title);
    doom_print("\n");

    if (devparm)
        doom_print(D_DEVSTR);

#if 0 // [pd] Ignore cdrom
    if (M_CheckParm("-cdrom"))
    {
        doom_print(D_CDROM);
        mkdir("c:\\doomdata", 0);
        doom_strcpy(basedefault, "c:/doomdata/default.cfg");
    }
#endif

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

        if (p < myargc - 1)
            scale = doom_atoi(myargv[p + 1]);
        if (scale < 10)
            scale = 10;
        if (scale > 400)
            scale = 400;
        //doom_print("turbo scale: %i%%\n", scale);
        doom_print("turbo scale: ");
        doom_print(doom_itoa(scale, 10));
        doom_print("%%\n");
        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:
                //doom_sprintf(file, "~"DEVMAPS"E%cM%c.wad", myargv[p + 1][0], myargv[p + 2][0]);
                doom_strcpy(file, "~"DEVMAPS"E");
                doom_concat(file, doom_ctoa(myargv[p + 1][0]));
                doom_concat(file, "M");
                doom_concat(file, doom_ctoa(myargv[p + 2][0]));
                doom_concat(file, ".wad");

                //doom_print("Warping to Episode %s, Map %s.\n", myargv[p + 1], myargv[p + 2]);
                doom_print("Warping to Episode ");
                doom_print(myargv[p + 1]);
                doom_print(", Map ");
                doom_print(myargv[p + 2]);
                doom_print(".\n");
                break;

            case commercial:
            default:
                p = doom_atoi(myargv[p + 1]);
                if (p < 10)
                {
                    //doom_sprintf(file, "~"DEVMAPS"cdata/map0%i.wad", p);
                    doom_strcpy(file, "~"DEVMAPS"cdata/map0");
                    doom_concat(file, doom_itoa(p, 10));
                    doom_concat(file, ".wad");
                }
                else
                {
                    //doom_sprintf(file, "~"DEVMAPS"cdata/map%i.wad", p);
                    doom_strcpy(file, "~"DEVMAPS"cdata/map");
                    doom_concat(file, doom_itoa(p, 10));
                    doom_concat(file, ".wad");
                }
                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)
    {
        //doom_sprintf(file, "%s.lmp", myargv[p + 1]);
        doom_strcpy(file, myargv[p + 1]);
        doom_concat(file, ".lmp");
        D_AddFile(file);
        //doom_print("Playing demo %s.lmp.\n", myargv[p + 1]);
        doom_print("Playing demo ");
        doom_print(myargv[p + 1]);
        doom_print(".lmp.\n");
    }

    // 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 = doom_atoi(myargv[p + 1]);
        //doom_print("Levels will end after %d minute", time);
        doom_print("Levels will end after ");
        doom_print(doom_itoa(time, 10));
        doom_print(" minute");
        if (time > 1)
            doom_print("s");
        doom_print(".\n");
    }

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

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

    // init subsystems
    doom_print("V_Init: allocate screens.\n");
    V_Init();

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

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

    doom_print("W_Init: Init WADfiles.\n");
    W_InitMultipleFiles(wadfiles);


    // 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("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)
                    I_Error("Error: \nThis is not the registered version.");
    }

    // Iff additonal PWAD files are used, print modified banner
    if (modifiedgame)
    {
        /*m*/doom_print(
            "===========================================================================\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"
        );
        //getchar();
    }


    // Check and print which version is executed.
    switch (gamemode)
    {
        case shareware:
        case indetermined:
            doom_print(
                "===========================================================================\n"
                "                                Shareware!\n"
                "===========================================================================\n"
            );
            break;
        case registered:
        case retail:
        case commercial:
            doom_print(
                "===========================================================================\n"
                "                 Commercial product - do not distribute!\n"
                "         Please report software piracy to the SPA: 1-800-388-PIR8\n"
                "===========================================================================\n"
            );
            break;

        default:
            // Ouch.
            break;
    }

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

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

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

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

    doom_print("D_CheckNetGame: Checking network game status.\n");
    D_CheckNetGame();

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

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

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

    // check for a driver that wants intermission stats
#if 0 // [pd] Unsure how to test this
    p = M_CheckParm("-statcopy");
    if (p && p < myargc - 1)
    {
        // for statistics driver
        extern  void* statcopy;

        statcopy = (void*)atoll(myargv[p + 1]);
        doom_print("External statistics registered.\n");
    }
#endif

    // 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)
    {
        singledemo = true;              // quit after one demo
        G_DeferedPlayDemo(myargv[p + 1]);
        D_DoomLoop();  // never returns
    }

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

    p = M_CheckParm("-loadgame");
    if (p && p < myargc - 1)
    {
#if 0 // [pd] We don't support the cdrom flag
        if (M_CheckParm("-cdrom"))
        {
            //doom_sprintf(file, "c:\\doomdata\\"SAVEGAMENAME"%c.dsg", myargv[p + 1][0]);
        }
        else
#endif
        {
            //doom_sprintf(file, SAVEGAMENAME"%c.dsg", myargv[p + 1][0]);
            doom_strcpy(file, SAVEGAMENAME);
            doom_concat(file, doom_ctoa(myargv[p + 1][0]));
            doom_concat(file, ".dsg");
        }
        G_LoadGame(file);
    }


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

    }

    // D_DoomLoop (); // never returns [ddps] Called by app

    if (demorecording)
        G_BeginRecording();

    if (M_CheckParm("-debugfile"))
    {
        char    filename[20];
        //doom_sprintf(filename, "debug%i.txt", consoleplayer);
        doom_strcpy(filename, "debug");
        doom_concat(filename, doom_itoa(consoleplayer, 10));
        doom_concat(filename, ".txt");
        //doom_print("debug output to: %s\n", filename);
        doom_print("debug output to: ");
        doom_print(filename);
        doom_print("\n");
        debugfile = doom_open(filename, "w");
    }

    I_InitGraphics();
}
