/**********************************************************************************************************************************/
/* File         : EASYWAD2.C                                                                                                      */
/* Executable   : EASYWAD2.EXE                                                                                                    */
/* Helpfile     : EASYWAD2.CFG                                                                                                    */
/* Doc file     : EASYWAD2.DOC                                                                                                    */
/* Version num  : 1.01                                                                                                            */
/* Last changed : 18-02-1995  12:37                                                                                               */
/* Update count : 2                                                                                                               */
/* OS type      : PC (DOS)                                                                                                        */
/* Description  : Menu handler for multiple WAD files for DOOM (Trademark of Id Software)                                         */
/* Compiler     : Microsoft (R) Quick C Compiler Version 2.50                                                                     */
/* Linker       : Microsoft (R) QuickC Linker Version 4.10                                                                        */
/* QCL attribs  : /AC /G2 /Ox /F 2000 /link GRAPHICS.LIB (Compact model, fully optimized 80286 code, Stacksize 8K)                */
/* Other        : WM.BAT    : start file                                                                                          */
/*                START.BAT : the result                                                                                          */
/*                START.OPT : response file                                                                                       */
/* Remarks      : Credits go to Brendon Wyber & Raphael Quinet (Doom Editor Utilities) for WadHeader and WadDirectory structures. */
/*                                                                                                                                */
/*                                   By M. van der Heide of ThunderWare Research Center                                           */
/**********************************************************************************************************************************/

#include   <bios.h>
#include   <ctype.h>
#include   <direct.h>
#include   <dos.h>
#include   <graph.h>
#include   <malloc.h>
#include   <stdarg.h>
#include   <stdio.h>
#include   <stdlib.h>
#include   <string.h>

#pragma    check_stack        (off)
#pragma    check_pointer      (off)

#ifndef    TRUE
typedef    char               boolean;
#define    TRUE               1
#define    FALSE              0
#endif

#define    DBLACK             0                                             /* Define names for the standard (VGA) palette colors */
#define    DBLUE              1
#define    DGREEN             2
#define    DCYAN              3
#define    DRED               4
#define    DMAGENTA           5
#define    DYELLOW            6
#define    DWHITE             7
#define    LBLACK             8
#define    LBLUE              9
#define    LGREEN             10
#define    LCYAN              11
#define    LRED               12
#define    LMAGENTA           13
#define    LYELLOW            14
#define    LWHITE             15

#define    ANYATTR            _A_NORMAL|_A_RDONLY|_A_HIDDEN|_A_SYSTEM|_A_ARCH                                  /* File attributes */
#define    SUBATTR            _A_NORMAL|_A_RDONLY|_A_HIDDEN|_A_SYSTEM|_A_ARCH|_A_SUBDIR                /* Subdirectory attributes */
#define    MAXFNAME           9
#define    MAXWADS            1000                                              /* Max number of WAD files the program can handle */
#define    MAXAUTOINCLUDE     5                                        /* Maximum number of autoinclude-WAD files in a CONFIGFILE */
#define    MAXWADDIRS         400                                  /* Max number of WAD file directories a CONFIGFILE may contain */
#define    PAGE               54                                                             /* Max number of WAD files on screen */
#define    WADHEIGHT          18                                                            /* Max number of filenames vertically */
#define    WADWIDTH           27                                          /* Max positions taken for a filename+info horizontally */
#define    MAXINFOLEN         16                                                           /* Max length of a WAD file info field */
#define    ENTRYLEN           8                                        /* Length of the name of a 'directory' entry in a WAD file */
#define    NOMEM              (void far *)0                                                            /* Memory allocation error */

#define    DEFAULTCONFIGFILE  "EASYWAD2.CFG"
#define    OTHERCONFIGFILE    '+'                             /* Used on the command line: use other config file than EASYWAD.CFG */
#define    COMMENT            '#'                      /* All characters in a line following this one are ignored in a CONFIGFILE */
#define    WADFILE            "*.WAD"
#define    BATFILE            "START.BAT"                                                        /* This file is built at the end */
#define    RESPONSEFILE       "START.OPT"                                                                        /* Response file */
#define    DOSUB1             "/S"                                        /* Used in CONFIGFILE in a WADDIR entry: do subdirs too */
#define    DOSUB2             "-S"
#define    RESCAN1            "/R"                                              /* Used on the command line: rebuild WAD InfoFile */
#define    RESCAN2            "-R"

#define    MAINWAD            "DOOM2.WAD"                                                                        /* Main WAD file */

#define    STARTALONE         "DOOM2"                                                       /* Define the start commands for DOOM */
#define    STARTIPX           "IPXSETUP"
#define    STARTLINK          "SERSETUP"
#define    DMATCH             "-DEATHMATCH"                                             /* Define the command parameters for DOOM */
#define    INCFILE            "-FILE"
#define    GOTOANYTHING       "-WARP"
#define    SKILL              "-SKILL"
#define    NUMPLAYERS         "-NODES"
#define    COMPORT            "-COM"
#define    NOMONSTERS         "-NOMONSTERS"
#define    RESPAWNMONSTERS    "-RESPAWN"
#define    FASTMONSTERS       "-FAST"
#define    DMATCHV2           "-ALTDEATH"
#define    NWSOCKET           "-SOCKET"

#define    NOFIELD            0                                                                /* Number the FIELDs on the screen */
#define    FILEFIELD          1 
#define    DIFFICULTYFIELD    3 
#define    PLAYTYPEFIELD      4 
#define    LEVELFIELD         5 
#define    DEATHMATCHFIELD    6 
#define    PAGERFIELD         7 
#define    RDPREVFIELD        8 
#define    STARTFIELD         9 
#define    AUTOMATICFIELD     10 
#define    DEATHMATCHV2FIELD  11 
#define    RESPAWNFIELD       12 
#define    NOMONSTERSFIELD    13 
#define    FASTMONSTERSFIELD  14 

#define    DEFAULTVERSION     666                                                                   /* This effectively means 1.0 */
#define    DEFAULTDIFFICULTY  3 
#define    DEFAULTPLAYTYPE    1 
#define    DEFAULTLEVEL       1 
#define    DEFAULTDMATCH      FALSE 
#define    DEFAULTDMATCHV2    FALSE 
#define    DEFAULTRESPAWN     FALSE
#define    DEFAULTNOMONSTERS  FALSE
#define    DEFAULTFASTMONST   FALSE
#define    DEFAULTSOCKET      0
#define    DEFAULTNODES       2
#define    DEFAULTCOMPORT     1
#define    DEFAULTWADDIR      "."                                                                            /* Current directory */
#define    DEFAULTINFOFILE    "WADS.DSC"


#define    KEY_LEVELUP        '.'                                                     /* Keyboard equivalents of mouse selections */
#define    KEY_LEVELDOWN      ','
#define    KEY_DIFFICULTY     'S'                                                                                      /* (Skill) */
#define    KEY_PLAYTYPE       'T'
#define    KEY_NODES          'N'
#define    KEY_COMPORT        'C'
#define    KEY_DEATHMATCH     'D'
#define    KEY_DEATHMATCHV2   'V'
#define    KEY_AUTO           'A'
#define    KEY_READPREVIOUS   'R'
#define    KEY_NOMONSTERS     'M'
#define    KEY_RESPAWNMONST   'P'
#define    KEY_FASTMONSTERS   'F'
#define    KEY_ABORT          0x1B                                                                                       /* [ESC] */
#define    KEY_STARTGAME      0x0D                                                                                    /* [RETURN] */
#define    KEY_PAGEUP         0x4900
#define    KEY_PAGEDOWN       0x5100
#define    KEY_DELETEWAD      0x5300                                                                                     /* [DEL] */
#define    KEY_HELP           0x3B00                                                                                      /* [F1] */
#define    KEY_RESCAN         0x3F00                                                                                      /* [F5] */
#define    KEY_TOGGLEFULL     0x4100                                                                                      /* [F7] */
#define    KEY_TOGGLESORT     0x4200                                                                                      /* [F8] */

#define    KEY_CURSLEFT       0x4B00
#define    KEY_CURSRIGHT      0x4D00
#define    KEY_CURSUP         0x4800
#define    KEY_CURSDOWN       0x5000
#define    KEY_SELECTFILE     0x3900                                                                                   /* [SPACE] */

#define    RETURNERROR        1                                                                              /* Define exit codes */
#define    RETURNABORT        1
#define    RETURNSTART        0

#define    NUMLEVEL           99                                                          /* Define the number of possible levels */
#define    NUMDIFFICULTY      5
#define    NUMPLAYTYPE        3

#define    NUMOPTIONS         17                                                       /* Define the number of CONFIGFILE options */

#define    MAXIGNORE          12                                         /* Define number of WAD 'directory' identifiers per type */
#define    MAXLEVELS          1
#define    MAXCOLORS          2
#define    MAXDEMOS           1
#define    MAXSPRITES         92
#define    MAXSOUNDS          2
#define    MAXMUSIC           3
#define    MAXGRAPHS          0
#define    MAXADDSWITCHES     5

#define    NEWCOLORS          0x01                             /* Define bits for the 'NewStuff' field in the 'wadinfo' structure */
#define    NEWDEMOS           0x02
#define    NEWSOUNDS          0x04
#define    NEWMUSIC           0x08
#define    NEWSPRITES         0x10
#define    NEWGRAPHS          0x20

#define    ToNumber(_Drv)    (toupper ((_Drv)) - 'A' + 1)                                   /* Convert drive name to drive number */
#define    ToName(_Drv)      ((_Drv) + 'A' - 1)                                             /* Convert drive number to drive name */

struct     Mouse_s                                                                                           /* Define mouse info */
{
  int      OldXx;                                                                /* Coordinates stored from previous status check */
  int      OldYy;
  int      Xx;                                                                                                          /* 0 - 79 */
  int      Yy;                                                                                                          /* 0 - 29 */
  boolean  CoChange;                                        /* TRUE if coordinates have changed (mouse moved to a next character) */
  boolean  Left;                                                                /* Status of the 3 mouse buttons; TRUE if pressed */
  boolean  Middle;
  boolean  Right;
  boolean  LeftStillPressed;                                 /* TRUE if the left button was also pressed in previous status check */
}          Mouse;

struct     WadDir_s                                                                             /* Define info for a WADDIR entry */
{
  char     Drive;                                                                         /* 1 = A, etc., 0 means: no drive given */
  char     Name[_MAX_DIR];
  boolean  DoSubDirs;                                                    /* TRUE if the subdirectories should be searched as well */
};

struct     WadInfo_s                                         /* Define info for a WADFILE entry (also used for AUTOINCLUDE files) */
{
  char     Drive;                                                                         /* 1 = A, etc., 0 means: no drive given */
  char     Path[_MAX_DIR];
  char     OrigName[MAXFNAME + _MAX_EXT - 1];                                                /* The original name, with extension */
  char     Name[MAXFNAME];                                                  /* Filled out, no extension (as this is always *.WAD) */
  char     Info[MAXINFOLEN + 1];                                                           /* Info as found in a WADINFOFILE file */
  char     NewStuff;                                                 /* Each bit represents an identifier type (demo, sound, etc) */
  unsigned long  NewLevels1;                                                              /* Each bit represents a level: 01 - 32 */
  unsigned long  NewLevels2;                                                                                           /* 33 - 64 */
  unsigned long  NewLevels3;                                                                                           /* 65 - 96 */
  unsigned short NewLevels4;                                                                                           /* 97 - 99 */
  boolean  Selected;                                                                     /* TRUE if the WAD is selected on screen */
};

struct     WadHeader_s                                                                        /* The first 12 bytes of a WAD file */
{
  char     Type[4];                                                          /* "PWAD" for a patch WAD, "IWAD" for an initial WAD */
  long     DirSize;                                                                   /* Number of entries in the WAD 'directory' */
  long     DirStart;                                       /* Pointer to the location (offset) of the 'directory' in the WAD file */
};

struct     WadDirectory_s                                                                  /* A 'directory' entry in the WAD file */
{
  long     Start;                                                            /* Pointer to the data of this entry in the WAD file */
  long     Size;                                                                                   /* Length in bytes of the data */
  char     Name[ENTRYLEN];                                                                     /* Identifier (name) of this entry */
};

struct     ExtCom_s                                                /* The commands that are available for the ADDSWITCHES keyword */
{
  char    *Command;                                                                                     /* The name of the switch */
  boolean  InUse;                                                              /* TRUE if this switch was given in the CONFIGFILE */
};

struct     ConfOp_s                                                                     /* The commands available in a CONFIGFILE */
{
  char    *OptionName;
  enum
  {
    OPT_DOOMDIR,
    OPT_WADDIR,
    OPT_AUTOINC,
    OPT_ADDSWIT,
    OPT_PLAYTYP,
    OPT_SET,
    OPT_NUM,
    OPT_STRING,
    OPT_FILE,
    OPT_SORTFIL
  }        OptionType;
  void far *DataPtr;
};

struct     WadDir_s  far *WadDir[MAXWADDIRS];
struct     WadInfo_s far *WadInfo[MAXWADS];
struct     WadInfo_s far *AutoInc[MAXAUTOINCLUDE];

char       CurPath[_MAX_DIR];                                                           /* Current directory (preceded by drive:) */
char       ConfigFile[_MAX_PATH];                                                                   /* Filename of the CONFIGFILE */
char       InfoFile[_MAX_PATH];                                             /* Filename of WADINFOFILE as found in the CONFIGFILE */
char       DoomDirectory[_MAX_PATH];                                        /* The main DOOM directory as found in the CONFIGFILE */
char       IpxDriver[256];                                                     /* Different IPX driver as found in the CONFIGFILE */
char       SerDriver[256];                                                     /* Different SER driver as found in the CONFIGFILE */
char       S[256];                                                                                          /* All-purpose string */
char       CurrentField;                                                                      /* Current FIELD type (see defines) */
char       SelectionChange;                                                      /* Used in file FIELD; TRUE if selection toggled */
char       CurrentPage;                                                                                /* Current file FIELD page */
char       LastPage;                                                                                      /* Last file FIELD page */
int        CurDrive;                                                                                             /* Current drive */
int        DoomDrive;                                                                             /* Drive of main DOOM directory */
int        TotalWads;                                                                          /* Total number of found WAD files */
int        TotalWadDirs;                                                 /* Total number of read WADDIR entries in the CONFIGFILE */
int        TotalAutoInc;                                              /* Total number of read AUTOINCLUDE entries in a CONFIGFILE */
int        M;
int        N;
int        Dummy;                                                                               /* Used in _dos_setdrive function */
boolean    OtherSerDriver;                                             /* TRUE if a different SERDRIVER was found in a CONFIGFILE */
boolean    OtherIpxDriver;                                             /* TRUE if a different IPXDRIVER was found in a CONFIGFILE */
boolean    UseMouse;                                                                            /* TRUE if a mouse has been found */
boolean    MouseHidden;                                                        /* TRUE if the mouse pointer is temporarely hidden */
boolean    Rescan           = FALSE;                                                /* TRUE if '-R' was given on the command line */
boolean    ConfigChange     = FALSE;                              /* TRUE if a different CONFIGFILE was given on the command line */
boolean    ScreenOpen       = FALSE;
boolean    DoNotSearch      = FALSE;                                              /* TRUE if NOSEARCH was found in the CONFIGFILE */
boolean    SortWadFiles     = FALSE;                                             /* TRUE if SORTFILES was found in the CONFIGFILE */
boolean    SortByName       = TRUE;                                                          /* TRUE for "NAME", FALSE for "INFO" */
boolean    NoFullName       = FALSE;                                            /* TRUE if NOFULLNAME was found in the CONFIGFILE */
boolean    NoAutoReturn     = FALSE;                                          /* TRUE if NOAUTORETURN was found in the CONFIGFILE */

int        CurrentSelected  = 0;                                                           /* Current pointed WAD file, 0 if none */
int        PreviousWad      = 0;                                                          /* Previous pointed WAD file, 0 if none */
int        DifficultyActive = DEFAULTDIFFICULTY;
int        PlayTypeActive   = DEFAULTPLAYTYPE;
int        NumNodesActive   = DEFAULTNODES;
int        NetworkSocket    = DEFAULTSOCKET;
int        CommPortActive   = DEFAULTCOMPORT;
int        CurrentLevel     = DEFAULTLEVEL;
boolean    DeathmatchOn     = DEFAULTDMATCH;
boolean    DeathmatchV2On   = DEFAULTDMATCHV2;
boolean    RespMonstersOn   = DEFAULTRESPAWN;
boolean    NoMonstersOn     = DEFAULTNOMONSTERS;
boolean    FastMonstersOn   = DEFAULTFASTMONST;

char      *Difficulties[]   = {"I'm too young to die ", "Hey, not too rough   ", "Hurt me plenty       ", "Ultra-Violence!      ",
                               "NIGHTMARE            "};
char      *PlayTypes[]      = {"Alone                ", "IPX-compatible       ", "Serial link          "};
char      *NumberNodes      =  "Number of players    ";
char      *CommPort         =  "COM port             ";
char      *Deathmatch       =  "DEATHMATCH!          ";
char      *DeathmatchV2     =  "DEATHMATCH! V2.0     ";
char      *NoMonsters       =  "No Monsters          ";
char      *RespawnMonsters  =  "Respawn monsters     ";
char      *FastMonsters     =  "Fast monsters        ";
char      *Boxes[]          = {"( ) ",                  "(\x07) "};
char      *PreviousPage     =  "<<<";
char      *NextPage         =  ">>>";
char      *PreviousLevel    =  "<<<";
char      *NextLevel        =  ">>>";
char      *StartGame        =  "( START DOOM2 )";
char      *Automatic        =  "( AUTO SELECT )";
char      *ReadPrevious     =  "(READ PREVIOUS)";
char      *NoLevel          =  "-         ";

char      *IdLevelNames[]   = {"     Entryway      ", "    Underhalls     ", "    The Gantlet    ", "     The Focus     ",
                               " The Waste Tunnels ", "    The Crusher    ", "    Dead Simple    ", " Tricks and Traps  ",
                               "      The Pit      ", "  Refueling Base   ", "  Circle of Death  ", "    The Factory    ",
                               "     Downtown      ", "  The Inmost Dens  ", "  Industrial Zone  ", "      Suburbs      ",
                               "     Tenements     ", "   The Courtyard   ", "    The Citadel    ", "      Gotcha!      ",
                               "      Nirvana      ", "   The Catacombs   ", "   Barrels o'Fun   ", "     The Chasm     ",
                               "    Bloodfalls     ", "The Abandoned Mines", "   Monster Condo   ", " The Spirit World  ",
                               "  The Living End   ", "    Icon of Sin    ", "    Wolfenstein    ", "      Grosse       "};

char      *IdLevels[]       = {"MAP", "M"};
char      *IdIgnore[]       = {"THINGS",   "LINEDEFS", "SIDEDEFS", "VERTEXES", "SEGS", "SSECTORS", "NODES", "SECTORS", "REJECT",
                               "BLOCKMAP", "INFOPACK", "PLATFORM"};                           /* Last 2 are NOT from Id Software! */
char      *IdColors[]       = {"PLAYPAL", "COLORMAP", "c", "palette"};
char      *IdDemos[]        = {"DEMO", "d", "demos"};
char      *IdSprites[]      = {"SARG", "TROO", "BOSS", "PLAY", "POSS", "SPOS", "SKUL", "HEAD", "CYBR", "SPID", "CHG",  "SAW",
                               "PIS",  "PBU",  "PSH",  "BAL",  "PUF",  "BLU",  "MIS",  "TFO",  "PUN",  "SHT",  "PLS",  "BFG",
                               "BFS",  "BFE",  "POL",  "CAND", "CBRA", "SHOT", "MGUN", "LAUN", "CSAW", "CLIP", "SHEL", "ROCK",
                               "STIM", "MEDI", "ARM",  "BAR",  "BPAK", "BROK", "AMMO", "SBOX", "ELEC", "BKEY", "YKEY", "RKEY",
                               "SUIT", "PVIS", "BEXP", "PMAP", "PIN",  "BON",  "SOUL", "COL",  "FSKU", "CEYE", "TRE",  "SMI",
                               "BSKU", "RSKU", "YSKU", "PLAS", "BFUG", "CELL", "PSTR", "CELP", "GOR",  "TGRN", "TBLU", "SMRT",
                               "SMBT", "SMGT", "BOS2", "BSPI", "CPOS", "FATB", "FATT", "FBXP", "FIRE", "HDB",  "KEEN", "MEGA",
                               "PAIN", "SGN2", "SHT2", "SKEL", "SSWV", "TLMP", "TLP2", "VILE", "p", "sprites"};
char      *IdSounds[]       = {"DS", "DP", "s", "sounds"};
char      *IdMusic[]        = {"D_", "GENMIDI", "DMXGUSC", "m", "music"};
char      *IdGraphics[]     = {"g", "graphics"};

struct    ConfOp_s ConfOp[] = {{"DOOMDIR",     OPT_DOOMDIR,  NOMEM},
                               {"WADDIR",      OPT_WADDIR,   NOMEM},
                               {"WADINFOFILE", OPT_FILE,     InfoFile},
                               {"SETSKILL",    OPT_NUM,     &DifficultyActive},
                               {"DEATHMATCH",  OPT_SET,     &DeathmatchOn},
                               {"AUTOINCLUDE", OPT_AUTOINC,  NOMEM},
                               {"NOSEARCH",    OPT_SET,     &DoNotSearch},
                               {"SETCOMPORT",  OPT_NUM,     &CommPortActive},
                               {"SETNODES",    OPT_NUM,     &NumNodesActive},
                               {"SETPLAYTYPE", OPT_PLAYTYP,  NOMEM},
                               {"ADDSWITCHES", OPT_ADDSWIT,  NOMEM},
                               {"SORTFILES",   OPT_SORTFIL,  NOMEM},
                               {"SETSOCKET",   OPT_NUM,     &NetworkSocket},
                               {"IPXDRIVER",   OPT_STRING,   IpxDriver},
                               {"SERDRIVER",   OPT_STRING,   SerDriver},
                               {"NOFULLNAME",  OPT_SET,     &NoFullName},
                               {"NOAUTORETURN",OPT_SET,     &NoAutoReturn}};

struct    ExtCom_s ExtCom[] = {{"-NOJOY",   FALSE},
                               {"-NOMOUSE", FALSE},
                               {"-NOMUSIC", FALSE},
                               {"-NOSFX",   FALSE},
                               {"-NOSOUND", FALSE}};

int ResetMouse (void)

/**********************************************************************************************************************************/
/* Pre   : None.                                                                                                                  */
/* Post  : The mouse driver has been reset. If no mouse was found, then 0 is returned. Anything else means success.               */
/* Import: None.                                                                                                                  */
/**********************************************************************************************************************************/

{
  union REGS Regs;

  Regs.x.ax = 0x0000;
  int86 (0x33, &Regs, &Regs);
  return (Regs.x.ax);
}

void ShowMouse (void)

/**********************************************************************************************************************************/
/* Pre   : (global) 'UseMouse' is TRUE if a mouse has been detected.                                                              */
/* Post  : The mouse pointer is made visable.                                                                                     */
/* Import: None.                                                                                                                  */
/**********************************************************************************************************************************/

{
  union REGS Regs;

  if (UseMouse)
  {
    Regs.x.ax = 0x0001;
    int86 (0x33, &Regs, &Regs);
  }
}

void HideMouse (void)

/**********************************************************************************************************************************/
/* Pre   : (global) 'UseMouse' is TRUE if a mouse has been detected.                                                              */
/* Post  : The mouse pointer is made invisable.                                                                                   */
/* Import: None.                                                                                                                  */
/**********************************************************************************************************************************/

{
  union REGS Regs;

  if (UseMouse)
  {
    Regs.x.ax = 0x0002;
    int86 (0x33, &Regs, &Regs);
  }
}

void MouseStatus (void)

/**********************************************************************************************************************************/
/* Pre   : None.                                                                                                                  */
/* Post  : The mouse driver has been read, which returns the status of the buttons and the x and y coordinates of the mouse.      */
/*         All this information is stored in the 'Mouse' structure.                                                               */
/* Import: None.                                                                                                                  */
/**********************************************************************************************************************************/

{
  union REGS Regs;

  Mouse.LeftStillPressed = Mouse.Left;
  Regs.x.ax      = 0x0003;
  int86 (0x33, &Regs, &Regs);
  Mouse.Left     = (Regs.x.bx & 0x0001);                                                 /* Store the status of the mouse buttons */
  Mouse.Right    = (Regs.x.bx & 0x0002);
  Mouse.Middle   = (Regs.x.bx & 0x0004);
  Mouse.Xx       =  Regs.x.cx / 0x0008;                                     /* Convert pixel coordinates to character coordinates */
  Mouse.Yy       =  Regs.x.dx / 0x0010;
  Mouse.CoChange = (Mouse.OldXx != Mouse.Xx || Mouse.OldYy != Mouse.Yy);
  if (!Mouse.Left)
    Mouse.LeftStillPressed = FALSE;
}

void DeAllocateAll (void)

/**********************************************************************************************************************************/
/* Pre   : None.                                                                                                                  */
/* Post  : If any WadDirs were initialized yet, then the memory is deallocated completely.                                        */
/*         If any WadInfos were initialized yet, then the memory is deallocated completely.                                       */
/*         If any AutoIncs were initialized yet, then the memory is deallocated completely.                                       */
/* Import: None.                                                                                                                  */
/**********************************************************************************************************************************/

{
  fcloseall ();
  flushall ();
  while (-- TotalWadDirs >= 0)
    _ffree (WadDir[TotalWadDirs]);
  while (-- TotalWads >= 0)
    _ffree (WadInfo[TotalWads]);
  while (-- TotalAutoInc >= 0)
    _ffree (AutoInc[TotalAutoInc]);
}

void Bye (int ReturnType, char *Message, ...)

/**********************************************************************************************************************************/
/* Pre   : 'ReturnType' holds the exit code, 'Message' holds the error message.                                                   */
/* Post  : The error message has been printed, all memory is freed and the program has been aborted.                              */
/* Import: DeAllocateAll.                                                                                                         */
/**********************************************************************************************************************************/

{
  va_list Args;

  if (ScreenOpen)                                                                                     /* Still in graphics mode ? */
    _setvideomode (_DEFAULTMODE);                                                                        /* Then close the screen */
  va_start (Args, Message);
  vfprintf (stderr, Message, Args);                                                        /* Print the (formatted) error message */
  va_end (Args);
  _dos_setdrive (CurDrive, &Dummy);                                                         /* Return to home drive and directory */
  chdir (CurPath);
  DeAllocateAll ();
  exit (ReturnType);
}

void PrText (short UseBox, short Y0, short X0, unsigned char Color, char *Msg, ...)

/**********************************************************************************************************************************/
/* Pre   : 'Y0' and 'X0' hold the coordinates, 'Color' holds the (VGA) color and 'Msg' holds the message to print.                */
/*         UseBox < 0: no selection box is printed first;                                                                         */
/*         UseBox = 0: an empty selection box is printed first;                                                                   */
/*         UseBox > 0: a filled selection box is printed first;                                                                   */
/*         If Y0 is 0, then the coordinates are not used; the text is written directly after the previous.                        */
/* Post  : If the coordinates were non-zero, then the message is printed at these coordinates in the given color. The coordinates */
/*         are first converted to the pixel coordinates according to the used font.                                               */
/* Import: None.                                                                                                                  */
/**********************************************************************************************************************************/

{
  char     Message[80];
  va_list  Args;

  va_start (Args, Msg);
  vsprintf (Message, Msg, Args);                                                           /* Convert the message into one string */
  va_end (Args);
  if (Y0 != 0)
    _settextposition (Y0, X0);
  _settextcolor (Color);
  if (UseBox == 0)
    _outtext (Boxes[0]);
  if (UseBox > 0)
    _outtext (Boxes[1]);
  _outtext (Message);
}

void InitVideo (void)

/**********************************************************************************************************************************/
/* Pre   : None.                                                                                                                  */
/* Post  : A VGA display of 640 x 480 x 16 colors has been opened and the screen has been cleared.                                */
/* Import: Bye.                                                                                                                   */
/**********************************************************************************************************************************/

{
  if (!_setvideomode (_VRES16COLOR))
    Bye (RETURNERROR, "ERROR - You need a VGA videocard for this utility\n");
  _clearscreen (_GCLEARSCREEN);
}

char *TestName (char *OldName)

/**********************************************************************************************************************************/
/* Pre   : 'OldName' holds the name to be tested.                                                                                 */
/* Post  : The name is tested. The return value is a string of the (adapted) input with the following specifications:             */
/*         - Each part (subdirname) has at most 8 characters, possibly followed by a '.' and at most 3 characters.                */
/*         - Each of the characters is valid to the DOS system.                                                                   */
/*         - Trailing backslashes have been cut, except when it was the only path character.                                      */
/* Import: Bye.                                                                                                                   */
/**********************************************************************************************************************************/

{
  char    Level[_MAX_PATH];
  char    NewName[_MAX_PATH];
  char    Part[MAXFNAME];
  int     LvlCnt;
  int     Ex;
  int     PathLenOldName;
  int     CharCnt;
  int     PartChar;
  boolean PointDone;
  boolean NError          = FALSE;
  boolean DriveFound      = FALSE;
  boolean Ready           = FALSE;

  PathLenOldName = strlen (OldName);
  CharCnt = -1;
  NewName[0] = '\0';
  while (!Ready && !NError)
  {
    LvlCnt = 0;
    while ((++ CharCnt < PathLenOldName) && OldName[CharCnt] != '\\' && OldName[CharCnt] != ':')
      Level[LvlCnt ++] = OldName[CharCnt];
    Level[LvlCnt] = '\0';
    if (OldName[CharCnt] == ':')                                                                         /* Preceded by drive: ?  */
      if (DriveFound)                                                                                   /* Already a drive found! */
        NError = TRUE;
      else
      {
        DriveFound = TRUE;
        strncpy (NewName, OldName, CharCnt + 1);
        NewName[CharCnt + 1] = '\0';
        if (CharCnt != 1 || toupper (OldName[0]) < 'A' || toupper (OldName[0]) > 'Z')                      /* Test drive validity */
          Bye (RETURNERROR, "\nERROR - Invalid drivename %s\n", NewName);
        if (CharCnt == PathLenOldName - 1)
          Ready = TRUE;                                                                               /* Only a driveletter given */
        else
          if (CharCnt == PathLenOldName - 2 && OldName[CharCnt + 1] == '\\')
          {
            Ready = TRUE;                                                                 /* Exceptional case: only drive:\ given */
            strcpy (NewName, OldName);
          }
      }
    else
    {
      if (CharCnt == PathLenOldName)                                                                      /* Handling last part ? */
        Ready = TRUE;
      if (CharCnt == PathLenOldName - 1 && OldName[CharCnt] == '\\')
        Ready = TRUE;                                                                                     /* Ended with backslash */
      PointDone = FALSE;
      for (PartChar = 0 ; PartChar < strlen (Level) ; PartChar ++)
        switch (Level[PartChar])
        {
          case '.'     : if (!PointDone)
                         {
                           strncpy (Part, Level, PartChar < 8 ? PartChar : 8);
                           Part[PartChar < 8 ? PartChar : 8] = '\0';                                         /* Cut >8 characters */
                           strcat (NewName, Part);
                           strcat (NewName, ".");                                                                  /* Add the '.' */
                           Ex = PartChar + 1;
                           PointDone = TRUE;
                         }
                         else
                           if (strcmp (Level, ".."))                                                          /* Exceptional case */
                             NError = TRUE;
                         break;
          case ';'     :
          case ','     :
          case '\''    :
          case '/'     :
          case '('     :
          case ')'     :
          case '['     :
          case ']'     :                                      /* Characters '>', '<', '|' '"' and '\' have already been taken out */
          case '='     : NError = TRUE;                                                                     /* All bad characters */
                         break;
        }
      if (!PointDone)                                                                       /* Finish filenames without extension */
      {
        strncpy (Part, Level, PartChar < 8 ? PartChar : 8);
        Part[PartChar < 8 ? PartChar : 8] = '\0';
        strcat (NewName, Part);
        PointDone = TRUE;
      }
      else                                                                       /* This also deals with the second point in '..' */
      {
        strncpy (Part, Level + Ex, PartChar - Ex < 3 ? PartChar - Ex : 3);                                   /* Cut >3 characters */
        Part[PartChar - Ex < 3 ? PartChar - Ex : 3] = '\0';
        strcat (NewName, Part);
      }
      if (!Ready)
        strcat (NewName, "\\");                                                                       /* Add the subdir character */
    }
  }
  if (NError)                                                                                            /* Report bad characters */
    Bye (RETURNERROR, "\nERROR - Invalid name %s\n", OldName);
  if (!strlen (NewName) && OldName[0] == '\\')                                                              /* Input was root dir */
    return ("\\");                                                                         /* Which should be treated differently */
  else
    return (NewName);                                                                                 /* Return (modified) string */
}

void _fastcall GetWadInfo (int WadNumber, boolean GoThere)

/**********************************************************************************************************************************/
/* Pre   : 'WadNumber' holds the WAD file number in memory that should be checked.                                                */
/*         'GoThere' is TRUE if the routine should first go to the required drive and directory.                                  */
/* Post  : The WAD directory of the file has been found and read out. The structure 'wadinfo' has been updated.                   */
/* Import: Bye.                                                                                                                   */
/**********************************************************************************************************************************/

{
  FILE                  *Fp;
  struct WadDirectory_s  WadDirectory;
  struct WadHeader_s     WadHeader;
  char                   Identifier[9];
  char                   DrivePath[_MAX_DIR];
  int  register          O;
  int  register          P;
  long register          Entries;
  boolean                More;

  if (GoThere)                                                                                 /* Jump to the directory if needed */
  {
    _dos_setdrive (WadInfo[WadNumber]->Drive, &Dummy);
    free (getcwd (DrivePath, _MAX_DIR - 1));                                                         /* Collect current directory */
    chdir (WadInfo[WadNumber]->Path);
  }
  if (!(Fp = fopen (WadInfo[WadNumber]->OrigName, "rb")))
    Bye (RETURNERROR, "\nERROR - Error opening file %s\n", WadInfo[WadNumber]->OrigName);
  if (fread (&WadHeader, 1, sizeof (struct WadHeader_s), Fp) != sizeof (struct WadHeader_s))               /* Read the WAD header */
    Bye (RETURNERROR, "\nERROR - Error reading file %s\n", WadInfo[WadNumber]->OrigName);
  if (strnicmp (WadHeader.Type, "PWAD", 4) && strnicmp (WadHeader.Type, "IWAD", 4))                         /* Is it a WAD file ? */
    Bye (RETURNERROR, "\nERROR - File %s is not a WAD file\n", WadInfo[WadNumber]->OrigName);
  if (fseek (Fp, WadHeader.DirStart, SEEK_SET))                                     /* Go to the WAD 'directory' part of the file */
    Bye (RETURNERROR, "\nERROR - Error reading file %s\n", WadInfo[WadNumber]->OrigName);
  WadInfo[WadNumber]->NewStuff = 0x00;                                                                       /* Clear all entries */
  WadInfo[WadNumber]->NewLevels1 = 0x00000000;
  WadInfo[WadNumber]->NewLevels2 = 0x00000000;
  WadInfo[WadNumber]->NewLevels3 = 0x00000000;
  WadInfo[WadNumber]->NewLevels4 = 0x00;
  Entries = -1;                                                                              /* Count all WAD 'directory' entries */
  while (++ Entries < WadHeader.DirSize)                                     /* The number of entries was found in the WAD header */
  {
    if (fread (&WadDirectory, 1, sizeof (struct WadDirectory_s), Fp) != sizeof (struct WadDirectory_s))          /* Read an entry */
      Bye (RETURNERROR, "\nERROR - Error reading file %s\n", WadInfo[WadNumber]->OrigName);
    for (O = 0 ; O < ENTRYLEN ; O ++)                                                       /* Fill the identifier to 8 positions */
      Identifier[O] = WadDirectory.Name[O];
    Identifier[ENTRYLEN] = '\0';                                                                          /* And make it a string */
    More = TRUE;                                                            /* Now test it against all types and signal successes */
    for (O = 0 ; O < MAXIGNORE && More ; O ++)
      if (!strnicmp (Identifier, IdIgnore[O], strlen (IdIgnore[O])))
        More = FALSE;
    if (More)
      for (O = 0 ; O < MAXCOLORS && More ; O ++)
        if (!strnicmp (Identifier, IdColors[O], strlen (IdColors[O])))
        {
          More = FALSE;
          WadInfo[WadNumber]->NewStuff |= NEWCOLORS;
        }
    if (More)
      for (O = 0 ; O < MAXDEMOS && More ; O ++)
        if (!strnicmp (Identifier, IdDemos[O], strlen (IdDemos[O])))
        {
          More = FALSE;
          WadInfo[WadNumber]->NewStuff |= NEWDEMOS;
        }
    if (More)
      for (O = 0 ; O < MAXLEVELS && More ; O ++)
        if (!strnicmp (Identifier, IdLevels[O], strlen (IdLevels[O])))
        {
          More = FALSE;
          P = atoi (Identifier + strlen (IdLevels[O]));
          if (P <= 32)
            WadInfo[WadNumber]->NewLevels1 |= ((unsigned long)1 << (P - 1));
          else
            if (P <= 64)
              WadInfo[WadNumber]->NewLevels2 |= ((unsigned long)1 << (P - 33));
            else
              if (P <= 96)
                WadInfo[WadNumber]->NewLevels3 |= ((unsigned long)1 << (P - 65));
              else
                WadInfo[WadNumber]->NewLevels4 |= ((unsigned short)1 << (P - 97));
        }
    if (More)
      for (O = 0 ; O < MAXSPRITES && More ; O ++)
        if (!strnicmp (Identifier, IdSprites[O], strlen (IdSprites[O])))
        {
          More = FALSE;
          WadInfo[WadNumber]->NewStuff |= NEWSPRITES;
        }
    if (More)
      for (O = 0 ; O < MAXSOUNDS && More ; O ++)
        if (!strnicmp (Identifier, IdSounds[O], strlen (IdSounds[O])))
        {
          More = FALSE;
          WadInfo[WadNumber]->NewStuff |= NEWSOUNDS;
        }
    if (More)
      for (O = 0 ; O < MAXMUSIC && More ; O ++)
        if (!strnicmp (Identifier, IdMusic[O], strlen (IdMusic[O])))
        {
          More = FALSE;
          WadInfo[WadNumber]->NewStuff |= NEWMUSIC;
        }
    if (More && WadDirectory.Start != 0x00000000 && Identifier[0] != '\0')       /* All other identifiers are counted as graphics */
      WadInfo[WadNumber]->NewStuff |= NEWGRAPHS;
  }
  fclose (Fp);
  if (GoThere)
  {
    chdir (DrivePath);
    _dos_setdrive (CurDrive, &Dummy);                                                                  /* Return to home location */
  }
}

void _fastcall WriteWadInfo (char *FileName)

/**********************************************************************************************************************************/
/* Pre   : 'FileName' holds the name of the file (which is the same as set in 'InfoFile').                                        */
/* Post  : All found WAD files are considered. All files have their fields 'NewStuff' and 'NewLevels' initialized. This info is   */
/*         converted into readable text and written to the file, together with the path information of the WAD file.              */
/* Import: Bye.                                                                                                                   */
/**********************************************************************************************************************************/

{
  FILE          *Fp;
  char register  Memory;
  char register  P;
  char register  Q;
  char           T[81];
  int  register  WadNumber;
  boolean        First;
  boolean        New;
  boolean        More;

  if (!(Fp = fopen (FileName, "w")))
    Bye (RETURNERROR, "\nERROR - Cannot write WAD info file\n");
  for (WadNumber = 0 ; WadNumber < TotalWads ; WadNumber ++)
  {
    More = FALSE;
    S[0] = '\0';
    fprintf (Fp, "%d %s %s ", WadInfo[WadNumber]->Drive, WadInfo[WadNumber]->Path, WadInfo[WadNumber]->OrigName);
    More = (WadInfo[WadNumber]->NewLevels1 != 0x00000000 ||
            WadInfo[WadNumber]->NewLevels2 != 0x00000000 ||
            WadInfo[WadNumber]->NewLevels3 != 0x00000000 ||
            WadInfo[WadNumber]->NewLevels4 != 0x00);                                 /* Are their any patch levels in this file ? */
    if (More)                                                                                                      /* Skip if not */
    {
      Memory = -1;
      First = TRUE;
      for (Q = 0 ; Q < 32 ; Q ++)                                                                        /* Handle all level-bits */
        if (WadInfo[WadNumber]->NewLevels1 & ((unsigned long)1 << Q))
        {
          if (Memory == -1)
            if (First)
            {
              sprintf (T, "%s%02d", IdLevels[MAXLEVELS], (int)Q + 1);
              strcat (S, T);
              First = FALSE;
              Memory = Q + 1;
              New = FALSE;
            }
            else
            {
              sprintf (T, ",%02d", (int)Q + 1);
              strcat (S, T);
              Memory = Q + 1;
              New = FALSE;
            }
          else
          {
            Memory ++;
            New = TRUE;
          }
        }
        else
        {
          if (Memory > 0 && New)
          {
            sprintf (T, "-%02d", (int)Memory);
            strcat (S, T);
          }
          Memory = -1;
        }
      for (Q = 0 ; Q < 32 ; Q ++)
        if (WadInfo[WadNumber]->NewLevels2 & ((unsigned long)1 << Q))
        {
          if (Memory == -1)
            if (First)
            {
              sprintf (T, "%s%02d", IdLevels[MAXLEVELS], (int)Q + 33);
              strcat (S, T);
              First = FALSE;
              Memory = Q + 33;
              New = FALSE;
            }
            else
            {
              sprintf (T, ",%02d", (int)Q + 33);
              strcat (S, T);
              Memory = Q + 33;
              New = FALSE;
            }
          else
          {
            Memory ++;
            New = TRUE;
          }
        }
        else
        {
          if (Memory > 0 && New)
          {
            sprintf (T, "-%02d", (int)Memory);
            strcat (S, T);
          }
          Memory = -1;
        }
      for (Q = 0 ; Q < 32 ; Q ++)
        if (WadInfo[WadNumber]->NewLevels3 & ((unsigned long)1 << Q))
        {
          if (Memory == -1)
            if (First)
            {
              sprintf (T, "%s%02d", IdLevels[MAXLEVELS], (int)Q + 65);
              strcat (S, T);
              First = FALSE;
              Memory = Q + 65;
              New = FALSE;
            }
            else
            {
              sprintf (T, ",%02d", (int)Q + 65);
              strcat (S, T);
              Memory = Q + 65;
              New = FALSE;
            }
          else
          {
            Memory ++;
            New = TRUE;
          }
        }
        else
        {
          if (Memory > 0 && New)
          {
            sprintf (T, "-%02d", (int)Memory);
            strcat (S, T);
          }
          Memory = -1;
        }
      for (Q = 0 ; Q < 3 ; Q ++)
        if (WadInfo[WadNumber]->NewLevels4 & ((unsigned short)1 << Q))
        {
          if (Memory == -1)
            if (First)
            {
              sprintf (T, "%s%02d", IdLevels[NUMLEVEL], (int)Q + 97);
              strcat (S, T);
              First = FALSE;
              Memory = Q + 97;
              New = FALSE;
            }
            else
            {
              sprintf (T, ",%02d", (int)Q + 97);
              strcat (S, T);
              Memory = Q + 97;
              New = FALSE;
            }
          else
          {
            Memory ++;
            New = TRUE;
          }
        }
        else
        {
          if (Memory > 0 && New)
          {
            sprintf (T, "-%02d", (int)Memory);
            strcat (S, T);
          }
          Memory = -1;
        }
      if (Memory > 0 && New)
      {
        sprintf (T, "-%02d", (int)Memory);
        strcat (S, T);
      }
      sprintf (T, "%-10s", S);
      strcpy (S, T);
    }                                                                                           /* No new levels in this WAD file */
    else                                                                 /* If only one type was found (for example, only sounds) */
      if (!(WadInfo[WadNumber]->NewStuff ^ NEWCOLORS))                   /* Then use the complete word ('sounds' in this example) */
        sprintf (S, "%s", IdColors[MAXCOLORS + 1]);
      else
        if (!(WadInfo[WadNumber]->NewStuff ^ NEWDEMOS))
          sprintf (S, "%s", IdDemos[MAXDEMOS + 1]);
        else
          if (!(WadInfo[WadNumber]->NewStuff ^ NEWSOUNDS))
            sprintf (S, "%s", IdSounds[MAXSOUNDS + 1]);
          else
            if (!(WadInfo[WadNumber]->NewStuff ^ NEWMUSIC))
              sprintf (S, "%s", IdMusic[MAXMUSIC + 1]);
            else
              if (!(WadInfo[WadNumber]->NewStuff ^ NEWSPRITES))
                sprintf (S, "%s", IdSprites[MAXSPRITES + 1]);
              else
                if (!(WadInfo[WadNumber]->NewStuff ^ NEWGRAPHS))
                  sprintf (S, "%s", IdGraphics[MAXGRAPHS + 1]);
                else
                {
                  More = TRUE;                                                                       /* More than one type found */
                  strcpy (S, NoLevel);                                                                /* Clear level string-part */
                }
    if (More)                                                                               /* Levels found or more types than 1 */
    {
      if (WadInfo[WadNumber]->NewStuff & NEWCOLORS)                                  /* Print one character to indicate the type */
        strcat (S, IdColors[MAXCOLORS]);
      if (WadInfo[WadNumber]->NewStuff & NEWDEMOS)
        strcat (S, IdDemos[MAXDEMOS]);
      if (WadInfo[WadNumber]->NewStuff & NEWSOUNDS)
        strcat (S, IdSounds[MAXSOUNDS]);
      if (WadInfo[WadNumber]->NewStuff & NEWMUSIC)
        strcat (S, IdMusic[MAXMUSIC]);
      if (WadInfo[WadNumber]->NewStuff & NEWSPRITES)
        strcat (S, IdSprites[MAXSPRITES]);
      if (WadInfo[WadNumber]->NewStuff & NEWGRAPHS)
        strcat (S, IdGraphics[MAXGRAPHS]);
    }
    sprintf (WadInfo[WadNumber]->Info, "%-16s", S);                                                                 /* MAXINFOLEN */
    fprintf (Fp, "%-16s\n", S);
  }
  fclose (Fp);
}

int _fastcall NextString (FILE *Fp, char *String)

/**********************************************************************************************************************************/
/* Pre   : 'Fp' points to the open CONFIGFILE, 'String' holds the address of the string to be read.                               */
/* Post  : Any string is read. If it started with a '#' (COMMENT character), then the rest of the line has been ignored. The      */
/*         function does not return before a string was read WITHOUT this character or EOF has been reached. The result of the    */
/*         function is the length of the read string, or 0 if EOF was encountered.                                                */
/* Import: Bye.                                                                                                                   */
/**********************************************************************************************************************************/

{
  char    Ch;
  char    Cnt;
  boolean Ready = FALSE;
  boolean SkipSpaces;

  String[0] = '\0';
  while (!Ready)                                                                            /* Read until a valid string is found */
  {
    SkipSpaces = TRUE;
    while (SkipSpaces)
    {
      fscanf (Fp, "%c", &Ch);                                                                 /* Read until no white-spaces found */
      if (feof (Fp))
      {
        String[0] = '\0';                                                                                         /* Or until EOF */
        return (0);
      }
      SkipSpaces = isspace (Ch);
    }
    if (Ch == COMMENT)                                                              /* First character is the COMMENT character ? */
      do
      {
        fscanf (Fp, "%c", &Ch);                                                                   /* Ignore until end of the line */
        if (feof (Fp))
        {
          String[0] = '\0';                                                                                       /* Or until EOF */
          return (0);
        }
      }
      while (Ch != '\n');
    else
      Ready = TRUE;
  }
  Cnt = 0;
  Ready = FALSE;
  while (!Ready)
  {
    while (!isspace (Ch) && Ch != '"')                                                             /* Trap quoted argument(part)s */
    {
      String[Cnt ++] = Ch;
      fscanf (Fp, "%c", &Ch);                                                                     /* Read until first white-space */
      if (feof (Fp))
      {
        String[Cnt] = '\0';                                                                                       /* Or until EOF */
        return (Cnt);
      }
    }
    if (Ch == '"')                                                                                          /* Handle quoted part */
    {
      do
      {
        fscanf (Fp, "%c", &Ch);
        if (feof (Fp))
          Bye (RETURNERROR, "ERROR - Unexpected end of configuration file\n");
        if (Ch == '\n')
          Bye (RETURNERROR, "ERROR - Unexpected end of line in configuration file\n");
        String[Cnt ++] = Ch;
      }
      while (Ch != '"');
      Cnt --;
      fscanf (Fp, "%c", &Ch);                                                                                 /* Read first after */
      if (feof (Fp))
      {
        String[Cnt] = '\0';                                                                                       /* Or until EOF */
        return (Cnt);
      }
    }
    else
      Ready = TRUE;
  }
  String[Cnt] = '\0';
  return (Cnt);
}

void _fastcall HandleOptWaddir (char *Item)

/**********************************************************************************************************************************/
/* Pre   : 'Item' holds an operand that has been read following an 'WADDIR' option.                                               */
/* Post  : If 'Item' holds '/S' (or '-S'), then the previously declared WadDir is flagged 'DoSubDirs'. Otherwise, 'Item' holds a  */
/*         filename, that is included in the WadDir list. If any error occurs, then no return is made.                            */
/* Import: TestName, Bye.                                                                                                         */
/**********************************************************************************************************************************/

{
  int register DriveNo;

  if (!stricmp (Item, DOSUB1) || !stricmp (Item, DOSUB2))
    if (TotalWadDirs == 0)
      Bye (RETURNERROR, "ERROR - Badly placed switch %s in WADDIR field\n", DOSUB1);
    else
      WadDir[TotalWadDirs - 1]->DoSubDirs = TRUE;
  else
  {
    if (TotalWadDirs == MAXWADDIRS)
      Bye (RETURNERROR, "ERROR - Too many WADDIR entries\n");
    if ((WadDir[TotalWadDirs] = ((struct WadDir_s far *)_fmalloc ((size_t)sizeof (struct WadDir_s)))) == NOMEM)
      Bye (RETURNERROR, "FATAL ERROR - Out of memory\n");
    strcpy (S, TestName (Item));
    if (S[1] == ':')                                                                                      /* Preceded by drive: ? */
    {
      if (strlen (S) == 2)                                                                             /* Just a drive, no path ? */
        Bye (RETURNERROR, "ERROR - Missing path for WADDIR in configuration file\n");
      DriveNo = ToNumber (S[0]);
      if (DriveNo != CurDrive)                                                                                   /* A new drive ? */
        WadDir[TotalWadDirs]->Drive = DriveNo;                                                              /* Store drive number */
      else
        WadDir[TotalWadDirs]->Drive = 0;                                                            /* Signal: no new drive given */
      strcpy (WadDir[TotalWadDirs]->Name, strupr (S + 2));                                                          /* Store path */
    }
    else
    {
      WadDir[TotalWadDirs]->Drive = 0;                                                                  /* Signal: no drive given */
      strcpy (WadDir[TotalWadDirs]->Name, strupr (S));
    }
    WadDir[TotalWadDirs]->DoSubDirs = FALSE;
    TotalWadDirs ++;
  }
}

void _fastcall HandleOptDoomdir (char *Item)

/**********************************************************************************************************************************/
/* Pre   : 'Item' holds the operand that has been read following a 'DOOMDIR' option.                                              */
/* Post  : The (global) variables 'DoomDrive' and 'DoomDirectory' have been initialized.                                          */
/* Import: TestName, Bye.                                                                                                         */
/**********************************************************************************************************************************/

{
  int register DriveNo;

  strcpy (S, TestName (Item));
  if (S[1] == ':')                                                                                        /* Preceded by drive: ? */
  {
    if (strlen (S) == 2)                                                                               /* Just a drive, no path ? */
      Bye (RETURNERROR, "ERROR - Missing path for DOOMDIR in configuration file\n");
    DriveNo = ToNumber (S[0]);
    if (DriveNo != CurDrive)                                                                                     /* A new drive ? */
      DoomDrive = DriveNo;                                                                                  /* Store drive number */
    else
      DoomDrive = 0;                                                                                /* Signal: no new drive given */
    strcpy (DoomDirectory, strupr (S + 2));                                                                         /* Store path */
  }
  else
  {
    DoomDrive = 0;                                                                                      /* Signal: no drive given */
    strcpy (DoomDirectory, strupr (S));
  }
}

void _fastcall HandleOptAutoinc (char *Item)

/**********************************************************************************************************************************/
/* Pre   : 'Item' holds a filename that has been read following an 'AUTOINCLUDE' option.                                          */
/* Post  : The filename is include in the AutoInc list. If any error occurs, then no return is made.                              */
/* Import: TestName, Bye.                                                                                                         */
/**********************************************************************************************************************************/

{
  int register DriveNo;
  int register Index;

  if (TotalAutoInc == MAXAUTOINCLUDE)
    Bye (RETURNERROR, "ERROR - Too many AUTOINCLUDE entries\n");
  if ((AutoInc[TotalAutoInc] = ((struct WadInfo_s far *)_fmalloc ((size_t)sizeof (struct WadInfo_s)))) == NOMEM)
    Bye (RETURNERROR, "FATAL ERROR - Out of memory\n");
  strcpy (S, TestName (Item));
  if (S[1] == ':')                                                                                        /* Preceded by drive: ? */
  {
    if (strlen (S) == 2)                                                                               /* Just a drive, no path ? */
      Bye (RETURNERROR, "ERROR - Missing path for AUTOINCLUDE in configuration file\n");
    DriveNo = ToNumber (S[0]);
    if (DriveNo != CurDrive)                                                                                     /* A new drive ? */
      AutoInc[TotalAutoInc]->Drive = DriveNo;                                                               /* Store drive number */
    else
      AutoInc[TotalAutoInc]->Drive = 0;                                                             /* Signal: no new drive given */
    strcpy (AutoInc[TotalAutoInc]->Path, strupr (S + 2));                                                           /* Store path */
  }
  else
  {
    AutoInc[TotalAutoInc]->Drive = 0;                                                                   /* Signal: no drive given */
    strcpy (AutoInc[TotalAutoInc]->Path, strupr (S));
  }
  Index = strlen (AutoInc[TotalAutoInc]->Path) - 1;
  while (AutoInc[TotalAutoInc]->Path[Index] != '\\' && Index > 0)
    Index --;
  if (Index == 0)
  {
    strcpy (AutoInc[TotalAutoInc]->OrigName, AutoInc[TotalAutoInc]->Path);                                   /* No preceding path */
    strcpy (AutoInc[TotalAutoInc]->Path, DEFAULTWADDIR);
  }
  else
  {
    strcpy (AutoInc[TotalAutoInc]->OrigName, AutoInc[TotalAutoInc]->Path + Index + 1);          /* Copy over last part (filename) */
    AutoInc[TotalAutoInc]->Path[Index] = '\0';                                                          /* Cut filename from path */
  }
  TotalAutoInc ++;
}

void _fastcall HandleOptAddSwitches (char *Item)

/**********************************************************************************************************************************/
/* Pre   : 'Item' holds the operand that has been read following an 'ADDSWITCHES' option.                                         */
/* Post  : If 'Item' holds a valid switch, then the 'InUse' flag of that switch is set. Otherwise no return is made.              */
/* Import: Bye.                                                                                                                   */
/**********************************************************************************************************************************/

{
  int register Index;

  if (Item[0] == '-')                                                    /* Remember that all switches start with a '-' character */
  {
    Index = 0;
    while (Index < MAXADDSWITCHES && stricmp (Item, ExtCom[Index].Command))         /* Test the name against all allowed switches */
      Index ++;
    if (Index == MAXADDSWITCHES)
      Bye (RETURNERROR, "ERROR - Switch %s not supported in ADDSWITCHES\n", Item);
    ExtCom[Index].InUse = TRUE;
  }
  else
    Bye (RETURNERROR, "ERROR - Unrecognised ADDSWITCHES %s in configuration file\n", Item);
}

void ReadConfig (void)

/**********************************************************************************************************************************/
/* Pre   : The (global) 'ConfigFile' should be initialized.                                                                       */
/* Post  : If a configuration file is found, then it has been read out. Only 8 keywords are recognised:                           */
/*         - DOOMDIR, after which the name of the main DOOM directory should be given.                                            */
/*         - WADDIR, after which a maximum of 400 WAD directories may be given; If an entry '/S' or '-S' is encountered, then all */
/*           subdirectories of the previously declared directory will also be used as WADDIRs.                                    */
/*         - WADINFOFILE, after which a WAD info file may be given. All simple errors are reported.                               */
/*         - SERDRIVER, after which a new serial driver should be given (in stead of SERSETUP);                                   */
/*         - IPXDRIVER, after which a new network driver should be given (in stead of IPXSETUP);                                  */
/*         - SETSKILL, after which the default skill (1-5) must be given.                                                         */
/*         - DEATHMATCH (no parameters). This means that deathmatch will be set as default.                                       */
/*         - AUTOINCLUDE, after which a maximum of 5 WAD files (complete with (partial) path) may be given. These files will then */
/*           automatically be selected when starting. If the file is not crossed when reading the directories (WADDIRs), then the */
/*           file is just not selected (no error will be generated).                                                              */
/*         - NOSEARCH (no parameters). If this keyword is given, then the program will not search all given WADDIR directories,   */
/*           it will use the WADINFOFILE instead and use all named entries instead. (This should be used with caution!)           */
/*         - SETCOMPORT, after which the default COM port (1-4) must be given for null-modem link.                                */
/*         - SETNODES, after which the default number of players (2-4) must be given for IPX link.                                */
/*         - SETSOCKET, after which a network socket (0-255) must be given.                                                       */
/*         - SETPLAYTYPE, after which one of the keywords "ALONE", "IPX" or "SERIAL" must be given.                               */
/*         - ADDSWITCHES, after which all the direct DOOM switches should be typed.                                               */
/*         - SORTFILES, after which one of the keywords "NAME" or "INFO" must be given.                                           */
/*         - NOFULLNAME (no parameters). Will stop using "music" i.s.o. "-         m".                                            */
/*         - NOAUTORETURN (no parameters). If this option is given, then no <CR> is passed to DOOM at startup.                    */
/*                                                                                                                                */
/*         If one (or all) are not found, then they are initialized with the defaults.                                            */
/*                                                                                                                                */
/*         If a character '#' is encountered, then the rest of this line is ignored (comment).                                    */
/* Import: NextString, HandleOptWaddir, HandleOptAutoinc, HandleOptAddSwitches, HandleOptDoomdir, Bye.                            */
/**********************************************************************************************************************************/

{
  FILE    *Fp;
  char     Item[256];
  char     ContinueOption;
  boolean  Handled;
  
  TotalWadDirs = 0;                                                                                    /* Initialize all counters */
  TotalAutoInc = 0;
  strcpy (DoomDirectory, DEFAULTWADDIR);                                                  /* Use the current directory as default */
  strcpy (InfoFile, DEFAULTINFOFILE);                                                           /* Initialize a default file name */
  strcpy (IpxDriver, STARTIPX);
  strcpy (SerDriver, STARTLINK);
  if (Fp = fopen (ConfigFile, "r"))                                                            /* Skip if no CONFIGFILE was found */
  {
    NextString (Fp, Item);                                                          /* Read-ahead first string: must be a keyword */
    while (!feof (Fp))
    {
      Handled = FALSE;
      for (M = 0 ; !Handled && M < NUMOPTIONS ; M ++)
        if (!stricmp (Item, ConfOp[M].OptionName))
        {
          if (ConfOp[M].OptionType != OPT_SET)                                         /* Option takes one (or more) operand(s) ? */
          {
            NextString (Fp, Item);                                                                            /* Read the operand */
            if (Item[0] == '\0')
              Bye (RETURNERROR, "ERROR - Missing operand after switch %s\n", ConfOp[M].OptionName);
          }
          ContinueOption = 0;                                                      /* Signal: no option has more than one operand */
          switch (ConfOp[M].OptionType)
          {
            case OPT_DOOMDIR : HandleOptDoomdir (Item);
                               break;
            case OPT_FILE    : strcpy (S, TestName (Item));
                               if (S[1] == ':' && S[2] == '\0')                                             /* Only a drive given */
                                 Bye (RETURNERROR, "ERROR - Missing pathname after %s in switch %s\n", S, ConfOp[M].OptionName);
                               strcpy ((char *)ConfOp[M].DataPtr, S);
                               break;
            case OPT_NUM     : for (N = 0 ; N < strlen (Item) ; N ++)
                                 if (strlen (Item) > 3 || !isdigit (Item[N]))
                                   Bye (RETURNERROR, "ERROR - Invalid number %s after switch %s\n", Item, ConfOp[M].OptionName);
                               *((int *)ConfOp[M].DataPtr) = atoi (Item);
                               break;
            case OPT_SET     : *((char *)ConfOp[M].DataPtr) = TRUE;
                               break;
            case OPT_STRING  : strcpy ((char *)ConfOp[M].DataPtr, Item);
                               break;
            case OPT_WADDIR  : ContinueOption = 1;
                               HandleOptWaddir (Item);
                               break;
            case OPT_AUTOINC : ContinueOption = 2;
                               HandleOptAutoinc (Item);
                               break;
            case OPT_ADDSWIT : ContinueOption = 3;
                               HandleOptAddSwitches (Item);
                               break;
            case OPT_PLAYTYP : if (!stricmp (Item, "ALONE"))
                                 PlayTypeActive = 1;
                               else
                                 if (!stricmp (Item, "IPX"))
                                   PlayTypeActive = 2;
                                 else
                                   if (!stricmp (Item, "SERIAL"))
                                     PlayTypeActive = 3;
                                   else
                                     Bye (RETURNERROR, "ERROR - Unknown playtype %s\n", Item);
                               break;
            case OPT_SORTFIL : SortWadFiles = TRUE;
                               if (!stricmp (Item, "NAME") || !stricmp (Item, "INFO"))
                                 SortByName = !stricmp (Item, "NAME");
                               else
                                 Bye (RETURNERROR, "ERROR - Unknown sort criteria %s after keyword SORTFILES\n", Item);
                               break;
          }
          Handled = TRUE;
        }
      if (!Handled)                                                                       /* Read item is not one of the keywords */
        switch (ContinueOption)
        {
          case 0 : Bye (RETURNERROR, "ERROR - Unknown keyword %s in configuration file\n", Item);
          case 1 : HandleOptWaddir (Item);
                   break;
          case 2 : HandleOptAutoinc (Item);
                   break;
          case 3 : HandleOptAddSwitches (Item);
        }
      NextString (Fp, Item);                                                                                  /* Read next option */
    }
    fclose (Fp);
  }
  else
    if (ConfigChange)                                                             /* It is an error if a different file was given */
      Bye (RETURNERROR, "ERROR - configuration file not found\n");
  OtherSerDriver = strcmp (SerDriver, STARTLINK);
  OtherIpxDriver = strcmp (IpxDriver, STARTIPX);
  if (TotalWadDirs == 0)                                                               /* No CONFIGFILE or WADDIR entries found ? */
  {
    if ((WadDir[0] = ((struct WadDir_s far *)_fmalloc ((size_t)sizeof (struct WadDir_s)))) == NOMEM)
      Bye (RETURNERROR, "FATAL ERROR - Out of memory\n");
    WadDir[0]->Drive = 0;                                                               /* Then use the DOOM directory as default */
    strcpy (WadDir[0]->Name, DoomDirectory);
    TotalWadDirs = 1;
  }
  if (DifficultyActive == 0 || DifficultyActive > 5)                                     /* Filter out nonsense numerical options */
    Bye (RETURNERROR, "ERROR - Invalid skill number %d\n", DifficultyActive);
  if (CommPortActive == 0 || CommPortActive > 4)
    Bye (RETURNERROR, "ERROR - Invalid COM port number %d\n", CommPortActive);
  if (NumNodesActive < 2 || NumNodesActive > 4)
    Bye (RETURNERROR, "ERROR - You cannot play network DOOM with %d player(s)\n", NumNodesActive);
  if (DeathmatchOn)
  {
    DeathmatchOn = FALSE;                                                    /* Select the better deathmatch version if available */
    DeathmatchV2On = TRUE;
  }
}

void _fastcall PrintDifficulties (int HighLite)

/**********************************************************************************************************************************/
/* Pre   : 'HighLite' contains the pointed difficulty, or 0 if none was pointed to.                                               */
/* Post  : The difficulties have been printed. The active difficulty has a checked box, all others have empty boxes. The pointed  */
/*         difficulty is printed in a different color.                                                                            */
/* Import: PrText.                                                                                                                */
/**********************************************************************************************************************************/

{
  for (M = 0 ; M < NUMDIFFICULTY ; M ++)
    if (M == HighLite - 1)
      PrText ((M == DifficultyActive - 1), 3 + M, 28, LRED, Difficulties[M]);
    else
      PrText ((M == DifficultyActive - 1), 3 + M, 28, LMAGENTA, Difficulties[M]);
}

void _fastcall PrintPlayTypes (int HighLite)

/**********************************************************************************************************************************/
/* Pre   : 'HighLite' contains the pointed playtype, or 0 if none was pointed to.                                                 */
/* Post  : The playtypes have been printed. The active playtype has a checked box, all others have empty boxes. The pointed       */
/*         playtype is printed in a different color.                                                                              */
/* Import: PrText.                                                                                                                */
/**********************************************************************************************************************************/

{
  for (M = 0 ; M < NUMPLAYTYPE ; M ++)
    if (M == HighLite - 1)
      PrText ((M == PlayTypeActive - 1), 3 + M, 55, LRED, PlayTypes[M]);
    else
      PrText ((M == PlayTypeActive - 1), 3 + M, 55, LMAGENTA, PlayTypes[M]);
  switch (PlayTypeActive)
  {
    case 1: PrText (-1, 6, 55, DBLACK, "                     ");                                                         /* Alone */
            break;
    case 2: if (!OtherIpxDriver)                                                                                /* IPX compatible */
              if (HighLite == 4)
                PrText (-1, 6, 55, LRED, "(%c) %s", NumNodesActive + '0', NumberNodes);
              else
                PrText (-1, 6, 55, LMAGENTA, "(%c) %s", NumNodesActive + '0', NumberNodes);
            else
              PrText (-1, 6, 55, LBLACK, "(%c) %s", NumNodesActive + '0', NumberNodes);
            break;
    case 3: if (!OtherSerDriver)                                                                               /* Null-modem link */
              if (HighLite == 4)
                PrText (-1, 6, 55, LRED, "(%c) %s", CommPortActive + '0', CommPort);
              else
                PrText (-1, 6, 55, LMAGENTA, "(%c) %s", CommPortActive + '0', CommPort);
            else
              PrText (-1, 6, 55, LBLACK, "(%c) %s", CommPortActive + '0', CommPort);
  }
}

void _fastcall PrintRespawnMonsters (boolean HighLite)

/**********************************************************************************************************************************/
/* Pre   : 'HighLite' is TRUE if this item was pointed to.                                                                        */
/* Post  : The RESPAWN text has been printed, with a checked box before it if it was selected, or empty otherwise.                */
/*         If 'HighLite' was TRUE, then the text is printed in a different color.                                                 */
/* Import: PrText.                                                                                                                */
/**********************************************************************************************************************************/

{
  if (HighLite)
    PrText (RespMonstersOn, 10, 28, LRED, RespawnMonsters);
  else
    PrText (RespMonstersOn, 10, 28, LMAGENTA, RespawnMonsters);
}

void _fastcall PrintNoMonsters (boolean HighLite)

/**********************************************************************************************************************************/
/* Pre   : 'HighLite' is TRUE if this item was pointed to.                                                                        */
/* Post  : The NOMONSTERS text has been printed, with a checked box before it if it was selected, or empty otherwise.             */
/*         If 'HighLite' was TRUE, then the text is printed in a different color.                                                 */
/* Import: PrText.                                                                                                                */
/**********************************************************************************************************************************/

{
  if (HighLite)
    PrText (NoMonstersOn, 9, 28, LRED, NoMonsters);
  else
    PrText (NoMonstersOn, 9, 28, LMAGENTA, NoMonsters);
}

void _fastcall PrintFastMonsters (boolean HighLite)

/**********************************************************************************************************************************/
/* Pre   : 'HighLite' is TRUE if this item was pointed to.                                                                        */
/* Post  : The FASTMONSTERS text has been printed, with a checked box before it if it was selected, or empty otherwise.           */
/*         If 'HighLite' was TRUE, then the text is printed in a different color.                                                 */
/* Import: PrText.                                                                                                                */
/**********************************************************************************************************************************/

{
  if (HighLite)
    PrText (FastMonstersOn, 11, 28, LRED, FastMonsters);
  else
    PrText (FastMonstersOn, 11, 28, LMAGENTA, FastMonsters);
}

void _fastcall PrintDeathmatch (boolean HighLite)

/**********************************************************************************************************************************/
/* Pre   : 'HighLite' is TRUE if this item was pointed to.                                                                        */
/* Post  : The DEATHMATCH text has been printed, with a checked box before it if it was selected, or empty otherwise.             */
/*         If 'HighLite' was TRUE, then the text is printed in a different color.                                                 */
/* Import: PrText.                                                                                                                */
/**********************************************************************************************************************************/

{
  if (HighLite)
    PrText (DeathmatchOn, 8, 55, LRED, Deathmatch);
  else
    PrText (DeathmatchOn, 8, 55, LMAGENTA, Deathmatch);
}

void _fastcall PrintV2Deathmatch (boolean HighLite)

/**********************************************************************************************************************************/
/* Pre   : 'HighLite' is TRUE if this item was pointed to.                                                                        */
/* Post  : The DEATHMATCH V2 text has been printed, with a checked box before it if it was selected, or empty otherwise.          */
/*         If 'HighLite' was TRUE, then the text is printed in a different color.                                                 */
/* Import: PrText.                                                                                                                */
/**********************************************************************************************************************************/

{
  if (HighLite)
    PrText (DeathmatchV2On, 9, 55, LRED, DeathmatchV2);
  else
    PrText (DeathmatchV2On, 9, 55, LMAGENTA, DeathmatchV2);
}

void _fastcall PrintLevelPagers (int HighLite)

/**********************************************************************************************************************************/
/* Pre   : 'HighLite' is 1 for left, 2 for right or 0 for no pager.                                                               */
/* Post  : The pagers have been printed. The 'HighLite' pager in a different color.                                               */
/* Import: PrText.                                                                                                                */
/**********************************************************************************************************************************/

{
  if (CurrentLevel > 1)                                                                            /* Are there previous levels ? */
    if (HighLite == 1)                                                                 /* Print pager for 'previous' level (left) */
      PrText (-1, 3, 3, LRED, PreviousLevel);
    else
      PrText (-1, 3, 3, LWHITE, PreviousLevel);
  else
    PrText (-1, 3, 3, LBLACK, PreviousLevel);
  if (CurrentLevel < NUMLEVEL)                                                                         /* Are there next levels ? */
    if (HighLite == 2)                                                                    /* Print pager for 'next' level (right) */
      PrText (-1, 3, 14, LRED, NextLevel);
    else
      PrText (-1, 3, 14, LWHITE, NextLevel);
  else
    PrText (-1, 3, 14, LBLACK, NextLevel);
}

void _fastcall PrintLevel (void)

/**********************************************************************************************************************************/
/* Pre   : None.                                                                                                                  */
/* Post  : The current level is printed, with the level name below it.                                                            */
/* Import: PrText.                                                                                                                */
/**********************************************************************************************************************************/

{
  PrText (-1, 3, 7, DCYAN, "MAP %02d", CurrentLevel);
  if (CurrentLevel <= 32)
    PrText (-1, 5, 1, DWHITE, IdLevelNames[CurrentLevel - 1]);
  else
    PrText (-1, 5, 1, DWHITE, "                   ");
}

void _fastcall PrintWadFiles (void)

/**********************************************************************************************************************************/
/* Pre   : None.                                                                                                                  */
/* Post  : The active page with WAD files has been printed (as determined by 'CurrentPage') has been printed. All selected WAD    */
/*         files are printed in a different color, any unused part of the page has been cleared from the screen. After each name  */
/*         is the read info printed. Highliting of a pointed wadfile is not handled here.                                         */
/* Import: PrText.                                                                                                                */
/**********************************************************************************************************************************/

{
  short register PositionX;
  short register PositionY;

  for (M = CurrentPage * PAGE ; M < (CurrentPage + 1) * PAGE ; M += WADHEIGHT)                              /* Handle each column */
    for (N = M ; N < M + WADHEIGHT ; N ++)                                                       /* Handle each row in the column */
    {
      PositionY = 13 + (N % WADHEIGHT);
      PositionX = ((M - CurrentPage * PAGE) / WADHEIGHT) * WADWIDTH + 1;
      if (N < TotalWads)                                                                              /* WAD file number exists ? */
      {
        if (WadInfo[N]->Selected)
          PrText (-1, PositionY, PositionX, DGREEN, WadInfo[N]->Name);                                          /* Print filename */
        else
          PrText (-1, PositionY, PositionX, DWHITE, WadInfo[N]->Name);
        PrText (-1, PositionY, PositionX + MAXFNAME, DCYAN, WadInfo[N]->Info);                                      /* Print info */
      }
      else                                                                             /* WAD file number after the last WAD file */
        PrText (-1, PositionY, PositionX, DBLACK, "                         ");                         /* Clear this screen part */
    }
}

void _fastcall PrintPagers (int HighLite)

/**********************************************************************************************************************************/
/* Pre   : 'HighLite' is 1 for left, 2 for right or 0 for no pager.                                                               */
/* Post  : The pagers have been printed. The 'HighLite' pager in a different color.                                               */
/* Import: PrText.                                                                                                                */
/**********************************************************************************************************************************/

{
  if (CurrentPage > 0)                                                                              /* Are there previous pages ? */
    if (HighLite == 1)                                                                  /* Print pager for 'previous' page (left) */
      PrText (-1, 11, 60, LRED, PreviousPage);
    else
      PrText (-1, 11, 60, LWHITE, PreviousPage);
  else
    PrText (-1, 11, 60, LBLACK, PreviousPage);
  if (CurrentPage < LastPage)                                                                           /* Are there next pages ? */
    if (HighLite == 2)                                                                     /* Print pager for 'next' page (right) */
      PrText (-1, 11, 78, LRED, NextPage);
    else
      PrText (-1, 11, 78, LWHITE, NextPage);
  else
    PrText (-1, 11, 78, LBLACK, NextPage);
}

void _fastcall PrintRdPrev (boolean HighLite)

/**********************************************************************************************************************************/
/* Pre   : 'HighLite' is TRUE if this item is selected.                                                                           */
/* Post  : The read previous text has been printed. If 'HighLite' was TRUE, than in a different color.                            */
/* Import: PrText.                                                                                                                */
/**********************************************************************************************************************************/

{
  if (HighLite)
    PrText (-1, 11, 1, LRED, ReadPrevious);
  else
    PrText (-1, 11, 1, LMAGENTA, ReadPrevious);
}

void _fastcall PrintAutomatic (boolean HighLite)

/**********************************************************************************************************************************/
/* Pre   : 'HighLite' is TRUE if this item is selected.                                                                           */
/* Post  : The automatic text has been printed. If 'HighLite' was TRUE, than in a different color.                                */
/* Import: PrText.                                                                                                                */
/**********************************************************************************************************************************/

{
  if (HighLite)
    PrText (-1, 10, 1, LRED, Automatic);
  else
    PrText (-1, 10, 1, LMAGENTA, Automatic);
}

void _fastcall PrintStart (boolean HighLite)

/**********************************************************************************************************************************/
/* Pre   : 'HighLite' is TRUE if this item is selected.                                                                           */
/* Post  : The start text has been printed. If 'HighLite' was TRUE, than in a different color.                                    */
/* Import: PrText.                                                                                                                */
/**********************************************************************************************************************************/

{
  if (HighLite)
    PrText (-1, 9, 1, LRED, StartGame);
  else
    PrText (-1, 9, 1, LMAGENTA, StartGame);
}

void _fastcall UnselectPreviousField (char SkipFieldNum)

/**********************************************************************************************************************************/
/* Pre   : 'SkipFieldNum' holds the field number that should NOT be unselected.                                                   */
/* Post  : The previous selected field has been unselected, if one was pointed to and it was not 'SkipFieldNum'.                  */
/* Import: PrintDifficulties, PrintPlayTypes, PrintLevelPagers, PrintDeathmatch, PrintPagers, PrintRdPrev, PrintStart,            */
/*         PrintAutomatic, PrintDeathmatchV2, PrintRespawnMonsters, PrintNoMonsters, PrintFastMonsters, PrText, HideMouse,        */
/*         ShowMouse.                                                                                                             */
/**********************************************************************************************************************************/

{
  short register PositionX;
  short register PositionY;
  int   register OldWadNumber;

  if (CurrentField != NOFIELD && CurrentField != SkipFieldNum)
  {
    switch (CurrentField)
    {
      case DIFFICULTYFIELD   : PrintDifficulties (0);
                               break;
      case PLAYTYPEFIELD     : PrintPlayTypes (0);
                               break;
      case LEVELFIELD        : PrintLevelPagers (0);
                               break;
      case DEATHMATCHFIELD   : PrintDeathmatch (FALSE);
                               break;
      case DEATHMATCHV2FIELD : PrintV2Deathmatch (FALSE);
                               break;
      case RESPAWNFIELD      : PrintRespawnMonsters (FALSE);
                               break;
      case NOMONSTERSFIELD   : PrintNoMonsters (FALSE);
                               break;
      case FASTMONSTERSFIELD : PrintFastMonsters (FALSE);
                               break;
      case PAGERFIELD        : PrintPagers (0);
                               break;
      case RDPREVFIELD       : PrintRdPrev (FALSE);
                               break;
      case STARTFIELD        : PrintStart (FALSE);
                               break;
      case AUTOMATICFIELD    : PrintAutomatic (FALSE);
                               break;
      case FILEFIELD         : PositionY = 13 + ((PreviousWad - 1) % WADHEIGHT);           /* Location of previously selected WAD */
                               PositionX = ((PreviousWad - 1) / WADHEIGHT) * WADWIDTH + 1;
                               OldWadNumber = CurrentPage * PAGE + PreviousWad - 1;                         /* Number of that WAD */
                               HideMouse ();
                               if (WadInfo[OldWadNumber]->Selected)
                                 PrText (-1, PositionY, PositionX, DGREEN, WadInfo[OldWadNumber]->Name);
                               else
                                 PrText (-1, PositionY, PositionX, DWHITE, WadInfo[OldWadNumber]->Name);
                               ShowMouse ();
    }
  }
}

boolean WaitForConfirmation (void)

/**********************************************************************************************************************************/
/* Pre   : None.                                                                                                                  */
/* Post  : The user must press a key. If this key is 'Y', then TRUE is returned, otherwise FALSE.                                 */
/* Import: None.                                                                                                                  */
/**********************************************************************************************************************************/

{
  unsigned int Key;

  while (!_bios_keybrd (_KEYBRD_READY))                                                                         /* Wait for a key */
    ;
  Key = _bios_keybrd (_KEYBRD_READ) & 0x00FF;                                                                 /* Read pressed key */
  return (toupper ((char)Key) == 'Y');                                                                /* Return TRUE if it is 'Y' */
}

boolean _fastcall Requester (short Y0, short X0, short DY, short DX)

/**********************************************************************************************************************************/
/* Pre   : 'Y0' and 'X0' hold the top-left coordinates of the requester that is to be printed. The hight will be 'DY' lines, the  */
/*         width will be 'DX' columns.                                                                                            */
/* Post  : The requester has been drawn.                                                                                          */
/*         Note that the requester is surrounded with an empty zone.                                                              */
/* Import: HideMouse, UnselectPreviousField, PrText.                                                                              */
/**********************************************************************************************************************************/

{
  HideMouse ();                                                                           /* Clear mousepointer and hi-light bars */
  UnselectPreviousField (NOFIELD);
  CurrentField = NOFIELD;
  Mouse.CoChange = TRUE;                                                                         /* Signal: re-hi-light on return */
  for (M = 0 ; M < DX ; M ++)
    PrText (-1, Y0, X0 + M, DBLACK, " ");
  PrText (-1, Y0 + 1, X0, LRED, " \xC9");
  for (M = 0 ; M < (DX - 4) ; M ++)
    PrText (-1, 0, 0, LRED, "\xCD");
  PrText (-1, 0, 0, LRED, "\xBB ");
  for (M = 2 ; M < (DY - 2) ; M ++)
  {
    PrText (-1, Y0 + M, X0, LRED, " \xBA");
    for (N = 0 ; N < (DX - 4) ; N ++)
      PrText (-1, 0, 0, DBLACK, " ");
    PrText (-1, 0, 0, LRED, "\xBA ");
  }
  PrText (-1, Y0 + DY - 2, X0, LRED, " \xC8");
  for (M = 0 ; M < (DX - 4) ; M ++)
    PrText (-1, 0, 0, LRED, "\xCD");
  PrText (-1, 0, 0, LRED, "\xBC ");
  for (M = 0 ; M < DX ; M ++)
    PrText (-1, Y0 + DY - 1, X0 + M, DBLACK, " ");
}
 
void GiveHelp (void)

/**********************************************************************************************************************************/
/* Pre   : The user pressed [F1].                                                                                                 */
/* Post  : A requester has been drawn, containing a listing of all available keys (depending whether a mouse has been found).     */
/*         The user must press a key to return.                                                                                   */
/* Import: Requester, PrText, WaitForConfirmation, PrintWadFiles, ShowMouse.                                                      */
/**********************************************************************************************************************************/

{
  Requester (14, 2, 16, 78);
  PrText (-1, 17, 24, DCYAN, "The following keys are available:");
  PrText (-1, 19, 5, DWHITE, "%c  ", KEY_LEVELUP);
  PrText (-1, 0, 0, DCYAN, "Level up");
  PrText (-1, 20, 5, DWHITE, "%c  ", KEY_LEVELDOWN);
  PrText (-1, 0, 0, DCYAN, "Level down");
  PrText (-1, 21, 5, DWHITE, "%c  ", KEY_DIFFICULTY);
  PrText (-1, 0, 0, DCYAN, "Skill");
  PrText (-1, 22, 5, DWHITE, "%c  ", KEY_PLAYTYPE);
  PrText (-1, 0, 0, DCYAN, "Playtype");
  if (!OtherIpxDriver)
  {
    PrText (-1, 25, 5, DWHITE, "%c  ", KEY_NODES);
    PrText (-1, 0, 0, DCYAN, "Number of players");
  }
  PrText (-1, 23, 5, DWHITE, "%c  ", KEY_DEATHMATCH);
  PrText (-1, 0, 0, DCYAN, "Deathmatch");
  if (!OtherSerDriver)
  {
    PrText (-1, 26, 5, DWHITE, "%c  ", KEY_COMPORT);
    PrText (-1, 0, 0, DCYAN, "COM port");
  }
  PrText (-1, 19, 28, DWHITE, "%c       ", KEY_NOMONSTERS);
  PrText (-1, 0, 0, DCYAN, "No monsters");
  PrText (-1, 20, 28, DWHITE, "%c       ", KEY_RESPAWNMONST);
  PrText (-1, 0, 0, DCYAN, "Respawn monsters");
  PrText (-1, 24, 5, DWHITE, "%c  ", KEY_DEATHMATCHV2);
  PrText (-1, 0, 0, DCYAN, "Deathmatch v2.0");
  PrText (-1, 21, 28, DWHITE, "%c       ", KEY_FASTMONSTERS);
  PrText (-1, 0, 0, DCYAN, "Fast monsters");
  PrText (-1, 22, 28, DWHITE, "%c       ", KEY_AUTO);
  PrText (-1, 0, 0, DCYAN, "AUTO SELECT");
  PrText (-1, 23, 28, DWHITE, "%c       ", KEY_READPREVIOUS);
  PrText (-1, 0, 0, DCYAN, "READ PREVIOUS");
  PrText (-1, 24, 28, DWHITE, "[ENTER] ");
  PrText (-1, 0, 0, DCYAN, "START DOOM!");
  PrText (-1, 25, 28, DWHITE, "[ESC]   ");
  PrText (-1, 0, 0, DCYAN, "Abort EasyWAD");
  PrText (-1, 26, 28, DWHITE, "[DEL]   ");
  PrText (-1, 0, 0, DCYAN, "Delete WAD");
  PrText (-1, 19, 55, DWHITE, "[PG UP] ");
  PrText (-1, 0, 0, DCYAN, "Next WAD page");
  PrText (-1, 20, 55, DWHITE, "[PG DN] ");
  PrText (-1, 0, 0, DCYAN, "Previous page");
  PrText (-1, 21, 55, DWHITE, "[F1]    ");
  PrText (-1, 0, 0, DCYAN, "This help page");
  PrText (-1, 22, 55, DWHITE, "[F5]    ");
  PrText (-1, 0, 0, DCYAN, "Rescan WADDIRs");
  PrText (-1, 23, 55, DWHITE, "[F7]    ");
  PrText (-1, 0, 0, DCYAN, "Reset fullname");
  PrText (-1, 24, 55, DWHITE, "[F8]    ");
  PrText (-1, 0, 0, DCYAN, "Resort WADs");
  if (!UseMouse)
  {
    PrText (-1, 25, 55, DWHITE, "[CURS]  ");
    PrText (-1, 0, 0, DCYAN, "Move around");
    PrText (-1, 26, 55, DWHITE, "[SPACE] ");
    PrText (-1, 0, 0, DCYAN, "Select WAD");
  }
  WaitForConfirmation ();
  for (M = 15 ; M < 29 ; M ++)                                                                                 /* Erase requester */
    PrText (-1, M, 3, DBLACK, "                                                                            ");
  PrintWadFiles ();
  ShowMouse ();
}

void SortFiles (void)

/**********************************************************************************************************************************/
/* Pre   : None.                                                                                                                  */
/* Post  : If (global) 'SortWadFiles' is TRUE, then the WAD files in memory have been sorted according to the sorttype.           */
/*         If (global) 'SortByName' is TRUE, then they have been sorted by name, otherwise they have been sorted by info field.   */
/* Import: None.                                                                                                                  */
/**********************************************************************************************************************************/

{
  boolean  More;
  struct   WadInfo_s far *TmpPtr;                                                           /* Entry as read from the WADINFOFILE */

  if (SortWadFiles)                                                                            /* SORTFILES given in CONFIGFILE ? */
  {
    More = TRUE;
    for (N = 0 ; N < TotalWads - 1 && More ; N ++)                                                        /* Perform a bubblesort */
    {
      More = FALSE;
      for (M = 0 ; M < TotalWads - 1 ; M ++)
        if (SortByName)                                                                                   /* Sort by 'Name' field */
        {
          if (strcmp (WadInfo[M]->Name, WadInfo[M + 1]->Name) > 0)                                /* Next 'larger' than current ? */
          {
            More = TRUE;
            TmpPtr = WadInfo[M];                                                                        /* Then flip the pointers */
            WadInfo[M] = WadInfo[M + 1];
            WadInfo[M + 1] = TmpPtr;
          }
        }
        else                                                                                              /* Sort by 'Info' field */
        {
          if (strcmp (WadInfo[M]->Info, WadInfo[M + 1]->Info) > 0)
          {
            More = TRUE;
            TmpPtr = WadInfo[M];
            WadInfo[M] = WadInfo[M + 1];
            WadInfo[M + 1] = TmpPtr;
          }
        }
    }
  }
}

void ConvertFullName (struct WadInfo_s far *ConInfo)

/**********************************************************************************************************************************/
/* Pre   : 'ConInfo' points to the WadInfo structure that should have its Info field re-examined.                                 */
/* Post  : If the Info field contained a full name (e.g. 'music'), then it is converted to the short name (e.g. '-         m').   */
/*         Before returning, the Info field is expanded to contain exactly MAXINFOLEN characters.                                 */
/* Import: None.                                                                                                                  */
/**********************************************************************************************************************************/

{
  int register Index;
  int register SLNoLevel;                                                                                       /* StrLen NoLevel */

  for (Index = strlen (ConInfo->Info) - 1 ; ConInfo->Info[Index] == ' ' && Index > 0 ; Index --)           /* Cut trailing spaces */
    ConInfo->Info[Index] = '\0';
  if (NoFullName)                                                                   /* Convert full names to short name if wanted */
  {
    if (!strcmp (ConInfo->Info, IdColors[MAXCOLORS + 1]))
      sprintf (ConInfo->Info, "%s%s", NoLevel, IdColors[MAXCOLORS]);
    else
      if (!strcmp (ConInfo->Info, IdDemos[MAXDEMOS + 1]))
        sprintf (ConInfo->Info, "%s%s", NoLevel, IdDemos[MAXDEMOS]);
      else
        if (!strcmp (ConInfo->Info, IdSounds[MAXSOUNDS + 1]))
          sprintf (ConInfo->Info, "%s%s", NoLevel, IdSounds[MAXSOUNDS]);
        else
          if (!strcmp (ConInfo->Info, IdMusic[MAXMUSIC + 1]))
            sprintf (ConInfo->Info, "%s%s", NoLevel, IdMusic[MAXMUSIC]);
          else
            if (!strcmp (ConInfo->Info, IdSprites[MAXSPRITES + 1]))
              sprintf (ConInfo->Info, "%s%s", NoLevel, IdSprites[MAXSPRITES]);
            else
              if (!strcmp (ConInfo->Info, IdGraphics[MAXGRAPHS + 1]))
                sprintf (ConInfo->Info, "%s%s", NoLevel, IdGraphics[MAXGRAPHS]);
  }
  else
  {
    SLNoLevel = strlen (NoLevel);                                                         /* Just to speed things up a little ... */
    if (!strncmp (ConInfo->Info, NoLevel, SLNoLevel))
    {
      if (!strcmp (ConInfo->Info + SLNoLevel, IdColors[MAXCOLORS]))
        strcpy (ConInfo->Info, IdColors[MAXCOLORS + 1]);
      else
        if (!strcmp (ConInfo->Info + SLNoLevel, IdDemos[MAXDEMOS]))
          strcpy (ConInfo->Info, IdDemos[MAXDEMOS + 1]);
        else
          if (!strcmp (ConInfo->Info + SLNoLevel, IdSounds[MAXSOUNDS]))
            strcpy (ConInfo->Info, IdSounds[MAXSOUNDS + 1]);
          else
            if (!strcmp (ConInfo->Info + SLNoLevel, IdMusic[MAXMUSIC]))
              strcpy (ConInfo->Info, IdMusic[MAXMUSIC + 1]);
            else
              if (!strcmp (ConInfo->Info + SLNoLevel, IdSprites[MAXSPRITES]))
                strcpy (ConInfo->Info, IdSprites[MAXSPRITES + 1]);
              else
                if (!strcmp (ConInfo->Info + SLNoLevel, IdGraphics[MAXGRAPHS]))
                  strcpy (ConInfo->Info, IdGraphics[MAXGRAPHS + 1]);
      }
    }
  for (Index = strlen (ConInfo->Info) ; Index < MAXINFOLEN ; Index ++)                              /* Fill info field to maximum */
    ConInfo->Info[Index] = ' ';
  ConInfo->Info[Index] = '\0';
}

void _fastcall HandleFileSort (boolean Toggle)

/**********************************************************************************************************************************/
/* Pre   : 'Toggle' is TRUE if the sort criterium should be toggled (when called from ToggleFileSort), FALSE if not (when called  */
/*         from RescanFiles). A requester border should have been previously drawn, together with the top text line.              */
/* Post  : The question is finished in the requester. The user must confirm the question. If confirmed, then the files are sorted */
/*         according to 'Toggle'. If this was TRUE, then the criterium is toggled from "NAME" to "INFO" first. Afterward the      */
/*         requester has been removed and the files reprinted (if the sort was done, then page 1 is automatically selected).      */
/* Import: WaitForConfirmation, PrintWadFiles, ShowMouse, SortFiles, PrText, PrintPagers.                                         */
/**********************************************************************************************************************************/

{
  PrText (-1, 20, 32, LRED, "RESORT THE FILES ?");
  PrText (-1, 22, 31, DRED, "PRESS <Y> TO CONFIRM");
  if (!WaitForConfirmation ())                                                                                  /* Acknowledged ? */
  {                                                                                                            /* Step out if not */
    for (M = 16 ; M < 25 ; M ++)                                                                               /* Erase requester */
      PrText (-1, M, 26, DBLACK, "                                ");
    PrintWadFiles ();
    ShowMouse ();
    return;
  }
  if (Toggle)
  {
    SortWadFiles = TRUE;
    SortByName = !SortByName;                                                                 /* Toggle between "NAME" and "INFO" */
  }
  PrText (-1, 18, 29, DRED, "                        ");
  PrText (-1, 20, 32, LRED, "    Sorting ...   ");
  PrText (-1, 22, 31, DRED, "                    ");
  SortFiles ();
  for (M = 16 ; M < 25 ; M ++)                                                                                 /* Erase requester */
    PrText (-1, M, 26, DBLACK, "                                ");
  CurrentPage = 0;
  CurrentSelected = 0;
  PrintPagers (0);
  PrintWadFiles ();
  ShowMouse ();
}

void ToggleFileSort (void)

/**********************************************************************************************************************************/
/* Pre   : The user pressed [F8].                                                                                                 */
/* Post  : A requester has been drawn to confirm the action. If this was acknowledged, then the file sort criterium has been      */
/*         toggled, the files have been resorted and reprinted.                                                                   */
/* Import: Requester, PrText, HandleFileSort.                                                                                     */
/**********************************************************************************************************************************/

{
  Requester (15, 25, 11, 32);
  PrText (-1, 18, 29, DRED, "ARE YOU SURE YOU WANT TO");
  HandleFileSort (TRUE);
}

void RescanFiles (void)

/**********************************************************************************************************************************/
/* Pre   : The user pressed [F5].                                                                                                 */
/* Post  : A requester has been drawn to confirm the action. If this was acknowledged, then all files that are currently in       */
/*         memory have been rescanned, and the result has been written to the WADINFOFILE. After this, the user has been asked if */
/*         the files should be resorted. At the end, all files are reprinted on screen.                                           */
/* Import: Requester, PrText, HandleFileSort, GetWadInfo, WriteWadInfo, WaitForConfirmation, ShowMouse, ConvertFullName.          */
/**********************************************************************************************************************************/

{
  int register CountWadFiles;

  Requester (15, 25, 11, 32);
  PrText (-1, 18, 29, DRED, "ARE YOU SURE YOU WANT TO");
  PrText (-1, 20, 32, LRED, "RESCAN THE FILES ?");
  PrText (-1, 22, 31, DRED, "PRESS <Y> TO CONFIRM");
  if (!WaitForConfirmation ())                                                                                  /* Acknowledged ? */
  {                                                                                                            /* Step out if not */
    for (M = 16 ; M < 25 ; M ++)                                                                               /* Erase requester */
      PrText (-1, M, 26, DBLACK, "                                ");
    PrintWadFiles ();
    ShowMouse ();
    return;
  }
  PrText (-1, 18, 29, DRED, "                        ");
  PrText (-1, 20, 32, LRED, "   Searching ...  ");
  PrText (-1, 22, 31, DRED, "                    ");
  for (CountWadFiles = 0 ; CountWadFiles < TotalWads ; CountWadFiles ++)
    GetWadInfo (CountWadFiles, TRUE);                                                     /* Collect information on each WAD file */
  PrText (-1, 20, 29, LRED, "Writing WADINFOFILE ... ");
  WriteWadInfo (InfoFile);                                                                                    /* Write the result */
  for (CountWadFiles = 0 ; CountWadFiles < TotalWads ; CountWadFiles ++)
    ConvertFullName (WadInfo[CountWadFiles]);                                      /* Convert full names to short names if wanted */
  PrText (-1, 20, 29, LRED, "                        ");
  PrText (-1, 18, 34, DRED, "DO YOU WANT TO");
  HandleFileSort (FALSE);                                                                         /* Handle sorting of the result */
}

void ToggleFullName (void)

/**********************************************************************************************************************************/
/* Pre   : The user pressed [F7].                                                                                                 */
/* Post  : A requester has been drawn to confirm the action. If this was acknowledged, then the full name criterium has been      */
/*         toggled, the files have been resorted and reprinted.                                                                   */
/* Import: Requester, PrText, HandleFileSort.                                                                                     */
/**********************************************************************************************************************************/

{
  int register CountWadFiles;

  Requester (15, 25, 11, 32);
  PrText (-1, 18, 29, DRED, "ARE YOU SURE YOU WANT TO");
  PrText (-1, 20, 31, LRED, "CONVERT FULL NAMES ?");
  PrText (-1, 22, 31, DRED, "PRESS <Y> TO CONFIRM");
  if (!WaitForConfirmation ())                                                                                  /* Acknowledged ? */
  {                                                                                                            /* Step out if not */
    for (M = 16 ; M < 25 ; M ++)                                                                               /* Erase requester */
      PrText (-1, M, 26, DBLACK, "                                ");
    PrintWadFiles ();
    ShowMouse ();
    return;
  }
  PrText (-1, 18, 29, DRED, "                        ");
  PrText (-1, 20, 31, LRED, "   Converting ...   ");
  PrText (-1, 22, 31, DRED, "                    ");
  NoFullName = !NoFullName;                                                                             /* Toggle NoFullName flag */
  for (CountWadFiles = 0 ; CountWadFiles < TotalWads ; CountWadFiles ++)
    ConvertFullName (WadInfo[CountWadFiles]);
  PrText (-1, 20, 31, DRED, "                    ");
  PrText (-1, 18, 34, DRED, "DO YOU WANT TO");
  HandleFileSort (FALSE);                                                                         /* Handle sorting of the result */
}

void DeleteWadFile (void)

/**********************************************************************************************************************************/
/* Pre   : None.                                                                                                                  */
/* Post  : The user has been asked permission to delete the hi-lighted file. If he/she comfirmed, then the file has been deleted  */
/*         from disk and memory. If the file is protected on disk, then nothing is done.                                          */
/* Import: Requester, PrintWadFiles, ShowMouse, UnselectPreviousWad, WaitForConfirmation.                                         */
/**********************************************************************************************************************************/

{
  int register DeleteWadNumber;

  if (CurrentField != FILEFIELD)                                                       /* Return immediately if no file appointed */
    return;
  DeleteWadNumber = CurrentPage * PAGE + CurrentSelected - 1;                                         /* Determine the WAD number */
  Requester (15, 25, 11, 32);
  PrText (-1, 18, 29, DRED, "ARE YOU SURE YOU WANT TO");
  sprintf (S, "DELETE %s", WadInfo[DeleteWadNumber]->Name);
  for (M = strlen (S) - 1 ; S[M] == ' ' ; M --)
    S[M] = '\0';
  strcat (S, " ?");
  PrText (-1, 20, 40 - (strlen (S) / 2), LRED, S);
  PrText (-1, 22, 31, DRED, "PRESS <Y> TO CONFIRM");
  if (!WaitForConfirmation ())                                                                                  /* Acknowledged ? */
  {                                                                                                            /* Step out if not */
    for (M = 16 ; M < 25 ; M ++)                                                                               /* Erase requester */
      PrText (-1, M, 26, DBLACK, "                                ");
    PrintWadFiles ();
    ShowMouse ();
    return;
  }
  for (M = 16 ; M < 25 ; M ++)                                                                                 /* Erase requester */
    PrText (-1, M, 26, DBLACK, "                                       ");
  if (WadInfo[DeleteWadNumber]->Drive)                                                         /* Build complete path to the file */
    sprintf (S, "%c:", ToName (WadInfo[DeleteWadNumber]->Drive));
  else
    sprintf (S, "%c:", ToName (CurDrive));
  if (!strcmp (WadInfo[DeleteWadNumber]->Path, DEFAULTWADDIR))
    strcat (S, CurPath + 2);
  else
    strcat (S, WadInfo[DeleteWadNumber]->Path);
  strcat (S, "\\");
  strcat (S, WadInfo[DeleteWadNumber]->OrigName);
  if (_dos_setfileattr (S, _A_NORMAL))                                                    /* Clear any preventing file attributes */
  {                                                                                  /* Step out if protected from a higher level */
    PrintWadFiles ();
    ShowMouse ();
    return;
  }
  if (remove (S) == -1)                                                                          /* Now remove the file from disk */
  {                                                                                  /* Step out if protected from a higher level */
    PrintWadFiles ();
    ShowMouse ();
    return;
  }
  _ffree (WadInfo[DeleteWadNumber]);                                                               /* Remove the file from memory */
  for (M = DeleteWadNumber ; M < TotalWads ; M ++)                                          /* Move all files thereafter one back */
    WadInfo[M] = WadInfo[M + 1];
  TotalWads --;
  PrintWadFiles ();                                                                                           /* Print result ... */
  ShowMouse ();
}

void _fastcall HandleFile (unsigned int Key)

/**********************************************************************************************************************************/
/* Pre   : 'Key' holds the preesed (raw) key code that caused calling this routine; if (global) 'UseMouse' was FALSE.             */
/*         Considered keys are the cursor keys and space. If 'UseMouse' was TRUE, than 'Key' is always 0x0000 (dummy).            */
/* Post  : The mouse pointer was at the bottom of the screen. The pointed filename has been highlighted. If the right mousebutton */
/*         was pressed, then the file is selected, which is shown by a different color. If the file was already selected, then it */
/*         is now deselected. Selection can only be done once on a button press. To invert the selection, the mouse button must   */
/*         first be released, and then pressed again.                                                                             */
/* Import: HideMouse, ShowMouse, UnselectPreviousField.                                                                           */
/**********************************************************************************************************************************/

{
  int register PositionX;
  int register PositionY;
  int register OldWadNumber;
  int register NewWadNumber;

  UnselectPreviousField (FILEFIELD);
  PositionY = 13 + ((PreviousWad - 1) % WADHEIGHT);                                        /* Location of previously selected WAD */
  PositionX = ((PreviousWad - 1) / WADHEIGHT) * WADWIDTH + 1;
  OldWadNumber = CurrentPage * PAGE + PreviousWad - 1;                                                      /* Number of that WAD */
  CurrentSelected = 0;                                                                              /* Signal: no file pointed to */
  if (UseMouse)
  {
    if (Mouse.Xx <= 7)                                                                               /* Determine the file column */
      CurrentSelected = Mouse.Yy - 11;
    else
      if (Mouse.Xx >= 27 && Mouse.Xx <= 34)
        CurrentSelected = Mouse.Yy - 11 + WADHEIGHT;
      else
        if (Mouse.Xx >= 54 && Mouse.Xx <= 61)
          CurrentSelected = Mouse.Yy - 11 + (2 * WADHEIGHT);
    if (CurrentPage * PAGE + CurrentSelected > TotalWads)                                                    /* Empty screen part */
      CurrentSelected = -1;
    MouseHidden = FALSE;
    if (CurrentField == FILEFIELD && Mouse.CoChange)                       /* Only unhighlite the previous one if the mouse moved */
    {
      HideMouse ();
      MouseHidden = TRUE;                                                                         /* Signal: mouse pointer hidden */
      if (WadInfo[OldWadNumber]->Selected)
        PrText (-1, PositionY, PositionX, DGREEN, WadInfo[OldWadNumber]->Name);
      else
        PrText (-1, PositionY, PositionX, DWHITE, WadInfo[OldWadNumber]->Name);
    }
  }
  else
  {
    switch (Key)
    {
      case KEY_CURSLEFT   : if (PreviousWad > WADHEIGHT)                                                 /* Possible to go left ? */
                              CurrentSelected = PreviousWad - WADHEIGHT;
                            else                                                          /* Determine the far right and go there */
                              if ((CurrentPage * PAGE + PreviousWad + 2 * WADHEIGHT) <= TotalWads)
                                CurrentSelected = PreviousWad + 2 * WADHEIGHT;                                       /* 3 columns */
                              else
                                if ((CurrentPage * PAGE + PreviousWad + WADHEIGHT) <= TotalWads)                     /* 2 columns */
                                  CurrentSelected = PreviousWad + WADHEIGHT;
                                else                                                           /* Only 1 column; no move possible */
                                  CurrentSelected = PreviousWad;
                            break;
      case KEY_CURSRIGHT  : if ((PreviousWad < 2 * WADHEIGHT) && (CurrentPage * PAGE + PreviousWad + WADHEIGHT) <= TotalWads)
                              CurrentSelected = PreviousWad + WADHEIGHT;
                            else
                              CurrentSelected = PreviousWad % WADHEIGHT;
                            break;
      case KEY_CURSUP     : if (((PreviousWad - 1) % WADHEIGHT) > 0)
                              CurrentSelected = PreviousWad - 1;
                            else
                              if ((CurrentPage * PAGE + PreviousWad + WADHEIGHT) <= TotalWads)
                                CurrentSelected = PreviousWad - 1 + WADHEIGHT;
                              else
                                CurrentSelected = TotalWads % PAGE;
                            break;
      case KEY_CURSDOWN   : if (((PreviousWad - 1) % WADHEIGHT) < (WADHEIGHT - 1))
                              if ((CurrentPage * PAGE + PreviousWad + 1) <= TotalWads)
                                CurrentSelected = PreviousWad + 1;
                              else
                                CurrentSelected = (TotalWads % PAGE) - (TotalWads % WADHEIGHT) + 1;
                            else
                              CurrentSelected = PreviousWad + 1 - WADHEIGHT;
                            break;
      case KEY_SELECTFILE : CurrentSelected = PreviousWad;
    }
    if (Key != KEY_SELECTFILE)                                                                     /* Unhighlite the previous one */
      if (WadInfo[OldWadNumber]->Selected)
        PrText (-1, PositionY, PositionX, DGREEN, WadInfo[OldWadNumber]->Name);
      else
        PrText (-1, PositionY, PositionX, DWHITE, WadInfo[OldWadNumber]->Name);
  }
  NewWadNumber = CurrentPage * PAGE + CurrentSelected - 1;
  if ((UseMouse && (Mouse.Left && !Mouse.LeftStillPressed && CurrentSelected > 0))                      /* Mouse button pressed ? */
      || (Key == KEY_SELECTFILE))
  {
    WadInfo[NewWadNumber]->Selected = !WadInfo[NewWadNumber]->Selected;                                       /* Invert selection */
    SelectionChange = TRUE;                                                                               /* Signal: screenchange */
  }
  else
    SelectionChange = FALSE;
  if (CurrentSelected > 0)                                                                    /* A (valid) new file is pointed to */
  {
    if (Mouse.CoChange || SelectionChange || !UseMouse)                                                  /* Color change needed ? */
    {
      PositionY = 13 + ((CurrentSelected - 1) % WADHEIGHT);
      PositionX = ((CurrentSelected - 1) / WADHEIGHT) * WADWIDTH + 1;
      if (!MouseHidden)                                                                   /* Hide the mouse if not hidden already */
        HideMouse ();
      MouseHidden = TRUE;
      if (WadInfo[NewWadNumber]->Selected)                                                                 /* 'Draw' highlite bar */
        PrText (-1, PositionY, PositionX, LGREEN, WadInfo[NewWadNumber]->Name);
      else
        PrText (-1, PositionY, PositionX, LRED, WadInfo[NewWadNumber]->Name);
    }
    CurrentField = FILEFIELD;
    PreviousWad = CurrentSelected;
  }
  else
    CurrentField = NOFIELD;
  if (MouseHidden)                                                                         /* Reprint the mouse pointer if needed */
    ShowMouse ();
}

void _fastcall HandleDifficulty (boolean KeyInput)

/**********************************************************************************************************************************/
/* Pre   : 'KeyInput' is TRUE if this routine was called because of a keypress.                                                   */
/* Post  : The mouse pointer was at the difficulty block. The pointed difficulty has been highlited. If the mouse button was      */
/*         pressed then this difficulty is selected (and the previous automatically de-selected). The new result is reprinted.    */
/* Import: HideMouse, ShowMouse, PrintDifficulties, UnselectPreviousField.                                                        */
/**********************************************************************************************************************************/

{
  int register MaxDiff;

  HideMouse ();
  if (!KeyInput)
    UnselectPreviousField (DIFFICULTYFIELD);
  if (Mouse.Left && !Mouse.LeftStillPressed)
    DifficultyActive = Mouse.Yy - 1;
  else
    if (KeyInput)
      if (++ DifficultyActive > NUMDIFFICULTY)
        DifficultyActive = 1;
  if (KeyInput)
    if (CurrentField == DIFFICULTYFIELD)
      PrintDifficulties (Mouse.Yy - 1);
    else
      PrintDifficulties (0);
  else
  {
    PrintDifficulties (Mouse.Yy - 1);
    CurrentField = DIFFICULTYFIELD;
  }
  ShowMouse ();
}
  
void _fastcall HandlePlayType (boolean KeyInput, char Key)

/**********************************************************************************************************************************/
/* Pre   : 'KeyInput' is TRUE if this routine was called because of a keypress.                                                   */
/*         If it was TRUE, then 'Key' holds the (ASCII) keyvalue, otherwise 'Key' holds 0x00 (dummy).                             */
/* Post  : The mouse pointer was at the playtype block. The pointed playtype has been highlited. If the mouse button was pressed  */
/*         then this playtype is selected (and the previous automatically de-selected). The new result is reprinted.              */
/*         If the mouse pointer was at the fourth line, than the PlayType parameter of the current PlayType is handled.           */
/* Import: HideMouse, ShowMouse, PrintPlayTypes, UnselectPreviousField.                                                           */
/**********************************************************************************************************************************/

{
  HideMouse ();
  if (!KeyInput)
    UnselectPreviousField (PLAYTYPEFIELD);
  if (KeyInput)
  {
    switch (Key)
    {
      case KEY_PLAYTYPE : if (++ PlayTypeActive > NUMPLAYTYPE)
                            PlayTypeActive = 1;
                          break;
      case KEY_NODES    : if (PlayTypeActive == 2 && !OtherIpxDriver)                                           /* IPX compatible */
                            if (++ NumNodesActive == 5)                                             /* Increase number of players */
                              NumNodesActive = 2;                                                      /* Must be between 2 and 4 */
                          break;
      case KEY_COMPORT  : if (PlayTypeActive == 3 && !OtherSerDriver)                                          /* Null-modem link */
                            if (++ CommPortActive == 5)                                               /* Increase COM port number */
                              CommPortActive = 1;                                                      /* Must be between 1 and 4 */
    }
    if (CurrentField == PLAYTYPEFIELD)
      PrintPlayTypes (Mouse.Yy - 1);
    else
      PrintPlayTypes (0);
  }
  else                                                                                                             /* Mouse input */
  {
    if (Mouse.Left && !Mouse.LeftStillPressed)
      if (Mouse.Yy <= NUMPLAYTYPE + 1)                                                                       /* Change PlayType ? */
        PlayTypeActive = Mouse.Yy - 1;
      else
        if (PlayTypeActive == 2 && !OtherIpxDriver)                                                             /* IPX compatible */
        {
          if (++ NumNodesActive == 5)                                                               /* Increase number of players */
            NumNodesActive = 2;                                                                        /* Must be between 2 and 4 */
        }
        else
          if (PlayTypeActive == 3 && !OtherSerDriver)                                                         /* Null-modem link */
          {
            if (++ CommPortActive == 5)                                                               /* Increase COM port number */
              CommPortActive = 1;                                                                      /* Must be between 1 and 4 */
          }
    PrintPlayTypes (Mouse.Yy - 1);
    CurrentField = PLAYTYPEFIELD;
  }
  ShowMouse ();
}
  
void _fastcall HandleRespawnMonsters (boolean KeyInput)

/**********************************************************************************************************************************/
/* Pre   : 'KeyInput' is TRUE if this routine was called because of a keypress.                                                   */
/* Post  : The mouse pointer was at the respawn item. This item has been highlited. If the mouse button was pressed, then the     */
/*         active value is inverted. The new result is reprinted.                                                                 */
/* Import: HideMouse, ShowMouse, PrintRespawnMonsters, PrintNoMonsters, UnselectPreviousField.                                    */
/**********************************************************************************************************************************/

{
  HideMouse ();
  if (!KeyInput)
    UnselectPreviousField (RESPAWNFIELD);
  if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput)
    if (RespMonstersOn = !RespMonstersOn)                                                              /* Toggle the RESPAWN item */
      NoMonstersOn = FALSE;
  if (KeyInput)
  {
    PrintRespawnMonsters ((boolean)(CurrentField == RESPAWNFIELD));
    PrintNoMonsters ((boolean)(CurrentField == NOMONSTERSFIELD));
  }
  else
  {
    PrintRespawnMonsters (TRUE);
    PrintNoMonsters (FALSE);
    CurrentField = RESPAWNFIELD;
  }
  ShowMouse ();
}

void _fastcall HandleNoMonsters (boolean KeyInput)

/**********************************************************************************************************************************/
/* Pre   : 'KeyInput' is TRUE if this routine was called because of a keypress.                                                   */
/* Post  : The mouse pointer was at the nomonsters item. This item has been highlited. If the mouse button was pressed, then the  */
/*         active value is inverted. The new result is reprinted.                                                                 */
/* Import: HideMouse, ShowMouse, PrintNoMonsters, PrintRespawnMonsters, PrintFastMonsters, UnselectPreviousField.                 */
/**********************************************************************************************************************************/

{
  HideMouse ();
  if (!KeyInput)
    UnselectPreviousField (NOMONSTERSFIELD);
  if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput)
    if (NoMonstersOn = !NoMonstersOn)                                                               /* Toggle the NOMONSTERS item */
    {
      RespMonstersOn = FALSE;                                                                       /* These are contradictionary */
      FastMonstersOn = FALSE;
    }
  if (KeyInput)
  {
    PrintNoMonsters ((boolean)(CurrentField == NOMONSTERSFIELD));
    PrintRespawnMonsters ((boolean)(CurrentField == RESPAWNFIELD));
    PrintFastMonsters ((boolean)(CurrentField == FASTMONSTERSFIELD));
  }
  else
  {
    PrintNoMonsters (TRUE);
    PrintRespawnMonsters (FALSE);
    PrintFastMonsters (FALSE);
    CurrentField = NOMONSTERSFIELD;
  }
  ShowMouse ();
}

void _fastcall HandleFastMonsters (boolean KeyInput)

/**********************************************************************************************************************************/
/* Pre   : 'KeyInput' is TRUE if this routine was called because of a keypress.                                                   */
/* Post  : The mouse pointer was at the fastmonsters item. This item has been highlited. If the mouse button was pressed, then    */
/*         the active value is inverted. The new result is reprinted.                                                             */
/* Import: HideMouse, ShowMouse, PrintFastMonsters, PrintNoMonsters, UnselectPreviousField.                                       */
/**********************************************************************************************************************************/

{
  HideMouse ();
  if (!KeyInput)
    UnselectPreviousField (FASTMONSTERSFIELD);
  if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput)
    if (FastMonstersOn = !FastMonstersOn)                                                         /* Toggle the FASTMONSTERS item */
      NoMonstersOn = FALSE;                                                                         /* These are contradictionary */
  if (KeyInput)
  {
    PrintFastMonsters ((boolean)(CurrentField == FASTMONSTERSFIELD));
    PrintNoMonsters ((boolean)(CurrentField == NOMONSTERSFIELD));
  }
  else
  {
    PrintFastMonsters (TRUE);
    PrintNoMonsters (FALSE);
    CurrentField = FASTMONSTERSFIELD;
  }
  ShowMouse ();
}

void _fastcall HandleDeathmatch (boolean KeyInput)

/**********************************************************************************************************************************/
/* Pre   : 'KeyInput' is TRUE if this routine was called because of a keypress.                                                   */
/* Post  : The mouse pointer was at the deathmatch item. This item has been highlited. If the mouse button was pressed, then the  */
/*         active value is inverted. The new result is reprinted.                                                                 */
/* Import: HideMouse, ShowMouse, PrintDeathmatch, UnselectPreviousField, PrintV2Deathmatch.                                       */
/**********************************************************************************************************************************/

{
  HideMouse ();
  if (!KeyInput)
    UnselectPreviousField (DEATHMATCHFIELD);
  if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput)
    if (DeathmatchOn = !DeathmatchOn)                                                               /* Toggle the DEATHMATCH item */
      DeathmatchV2On = FALSE;
  if (KeyInput)
  {
    PrintDeathmatch ((boolean)(CurrentField == DEATHMATCHFIELD));
    PrintV2Deathmatch ((boolean)(CurrentField == DEATHMATCHV2FIELD));
  }
  else
  {
    PrintDeathmatch (TRUE);
    PrintV2Deathmatch (FALSE);
    CurrentField = DEATHMATCHFIELD;
  }
  ShowMouse ();
}
  
void _fastcall HandleV2Deathmatch (boolean KeyInput)

/**********************************************************************************************************************************/
/* Pre   : 'KeyInput' is TRUE if this routine was called because of a keypress.                                                   */
/* Post  : The mouse pointer was at the deathmatch item. This item has been highlited. If the mouse button was pressed, then the  */
/*         active value is inverted. The new result is reprinted.                                                                 */
/* Import: HideMouse, ShowMouse, PrintDeathmatch, UnselectPreviousField, PrintV2Deathmatch.                                       */
/**********************************************************************************************************************************/

{
  HideMouse ();
  if (!KeyInput)
    UnselectPreviousField (DEATHMATCHV2FIELD);
  if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput)
    if (DeathmatchV2On = !DeathmatchV2On)
      DeathmatchOn = FALSE;
  if (KeyInput)
  {
    PrintV2Deathmatch ((boolean)(CurrentField == DEATHMATCHV2FIELD));
    PrintDeathmatch ((boolean)(CurrentField == DEATHMATCHFIELD));
  }
  else
  {
    PrintV2Deathmatch (TRUE);
    PrintDeathmatch (FALSE);
    CurrentField = DEATHMATCHV2FIELD;
  }
  ShowMouse ();
}
  
void _fastcall HandleNextLevel (boolean KeyInput)

/**********************************************************************************************************************************/
/* Pre   : 'KeyInput' is TRUE if this routine was called because of a keypress.                                                   */
/* Post  : The mouse pointer was at the nextlevel item. This item has been highlited. If the mouse button was pressed, then a     */
/*         test is made if there is a next level. If there is one, then the active level is increased.                            */
/* Import: HideMouse, ShowMouse, PrintLevel, PrintLevelPagers, UnselectPreviousField.                                             */
/**********************************************************************************************************************************/

{
  if (CurrentLevel == NUMLEVEL)
    return;
  HideMouse ();
  if (!KeyInput)
    UnselectPreviousField (LEVELFIELD);
  if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput)
  {
    CurrentLevel ++;                                                                                    /* Increase current level */
    PrintLevel ();
  }
  if (!KeyInput)
  {
    PrintLevelPagers (2);
    CurrentField = LEVELFIELD;
  }
  else
    PrintLevelPagers (CurrentField == LEVELFIELD ? 2 : 0);
  ShowMouse ();
}
  
void _fastcall HandlePreviousLevel (boolean KeyInput)

/**********************************************************************************************************************************/
/* Pre   : 'KeyInput' is TRUE if this routine was called because of a keypress.                                                   */
/* Post  : The mouse pointer was at the previouslevel item. This item has been highlited. If the mouse button was pressed, then a */
/*         test is made if there is a previous level. If there is one, then the active level is decreased.                        */
/* Import: HideMouse, ShowMouse, PrintLevel, PrintLevelPagers, UnselectPreviousField.                                             */
/**********************************************************************************************************************************/

{
  if (CurrentLevel == 1)
    return;
  HideMouse ();
  if (!KeyInput)
    UnselectPreviousField (LEVELFIELD);
  if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput)
  {
    CurrentLevel --;                                                                                    /* Decrease current level */
    PrintLevel ();
  }
  if (!KeyInput)
  {
    PrintLevelPagers (1);
    CurrentField = LEVELFIELD;
  }
  else
    PrintLevelPagers (CurrentField == LEVELFIELD ? 1 : 0);
  ShowMouse ();
}
  
void _fastcall HandlePreviousPage (boolean KeyInput)

/**********************************************************************************************************************************/
/* Pre   : 'KeyInput' is TRUE if this routine is entered because the [PAGE UP] key was pressed.                                   */
/* Post  : The mouse pointer was at the previouspage item. This item has been highlited. If the mouse button was pressed, then    */
/*         a test is made if there are previous pages. If not, then the keyboard bell is sound, otherwise the previous page has   */
/*         been made the current. This new page has been printed.                                                                 */
/* Import: HideMouse, ShowMouse, PrintWadFiles, PrintPagers, UnselectPreviousField.                                               */
/**********************************************************************************************************************************/

{
  if (CurrentPage == 0)                                                                  /* Step out if there is no previous page */
    return;
  HideMouse ();
  if (!KeyInput)
    UnselectPreviousField (PAGERFIELD);
  if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput)
  {
    CurrentPage --;                                                                                             /* Go back a page */
    PrText (-1, 11, 69, LWHITE, "%2d", CurrentPage + 1);
    PrintWadFiles ();                                                                                      /* Print this new page */
    if (!UseMouse)
      PrText (-1, 13 + ((PreviousWad - 1) % WADHEIGHT),
                  ((PreviousWad - 1) / WADHEIGHT) * WADWIDTH + 1,
                  LRED, WadInfo[CurrentPage * PAGE + PreviousWad - 1]->Name);                  /* Hi-light 'new current' WAD file */
  }
  if (!KeyInput)
  {
    PrintPagers (1);
    CurrentField = PAGERFIELD;
  }
  else
    PrintPagers (CurrentField == PAGERFIELD ? 1 : 0);
  ShowMouse ();
}

void _fastcall HandleNextPage (boolean KeyInput)

/**********************************************************************************************************************************/
/* Pre   : 'KeyInput' is TRUE if this routine was called because the user pressed the [PAGE DOWN] key.                            */
/* Post  : The mouse pointer was at the nextpage item. This item has been highlited. If the mouse button was pressed, then a test */
/*         is made if there are next pages. If not, then the keyboard bell is sound, otherwise the next page has been made the    */
/*         current. This new page has been printed.                                                                               */
/* Import: HideMouse, ShowMouse, PrintWadFiles, PrintPagers, UnselectPreviousField.                                               */
/**********************************************************************************************************************************/

{
  if (CurrentPage == LastPage)                                                               /* Step out if there is no next page */
    return;
  HideMouse ();
  if (!KeyInput)
    UnselectPreviousField (PAGERFIELD);
  if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput)
  {
    CurrentPage ++;
    PrText (-1, 11, 69, LWHITE, "%2d", CurrentPage + 1);
    PrintWadFiles ();
    if (!UseMouse)
    {
      if ((CurrentPage * PAGE + PreviousWad - 1) >= TotalWads)              /* If not a full page, then test that the new pointed */
        PreviousWad = TotalWads % PAGE;                                    /* WAD file is valid, otherwise set it to the last one */
      PrText (-1, 13 + ((PreviousWad - 1) % WADHEIGHT),
                  ((PreviousWad - 1) / WADHEIGHT) * WADWIDTH + 1,
                  LRED, WadInfo[CurrentPage * PAGE + PreviousWad - 1]->Name);                  /* Hi-light 'new current' WAD file */
    }
  }
  if (!KeyInput)
  {
    PrintPagers (2);
    CurrentField = PAGERFIELD;
  }
  else
    PrintPagers (CurrentField == PAGERFIELD ? 2 : 0);
  ShowMouse ();
}

void _fastcall HandleReadPrev (boolean KeyInput)

/**********************************************************************************************************************************/
/* Pre   : 'KeyInput' is TRUE if this routine was called because of a keypress.                                                   */
/* Post  : The mouse pointer was at the read previous item. This item has been highlited. If the mouse button was pressed, then   */
/*         the file 'START.BAT' is read. Each item is set to the read value, all read WAD files are selected.                     */
/* Import: HideMouse, ShowMouse, PrintRdPrev, PrintDifficulties, PrintPlayTypes, PrintDeathmatch, PrintLevel, PrintLevelPagers,   */
/*         PrintWadFiles, UnselectPreviousField.                                                                                  */
/**********************************************************************************************************************************/

{
  FILE    *Fp;
  char     FileName[MAXFNAME];
  char     Directory[_MAX_DIR];
  char     Tmp[_MAX_DIR];
  char     DriveNum;
  boolean  Handled;
  boolean  Stop;

  HideMouse ();
  if (!KeyInput)
    UnselectPreviousField (RDPREVFIELD);
  if (!KeyInput)
  {
    PrintRdPrev (TRUE);
    CurrentField = RDPREVFIELD;
  }
  if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput)
  {
    Stop = FALSE;
    if (!(Fp = fopen (BATFILE, "r")))                                                                         /* Open "START.BAT" */
      Stop = TRUE;
    else
    {
      DifficultyActive = DEFAULTDIFFICULTY;
      PlayTypeActive = DEFAULTPLAYTYPE;
      CurrentLevel = DEFAULTLEVEL;
      DeathmatchOn = DEFAULTDMATCH;
      DeathmatchV2On = DEFAULTDMATCHV2;
      RespMonstersOn = DEFAULTRESPAWN;
      NoMonstersOn = DEFAULTNOMONSTERS;
      FastMonstersOn = DEFAULTFASTMONST;
      CommPortActive = DEFAULTCOMPORT;
      NumNodesActive = DEFAULTNODES;
      for (M = 0 ; M < TotalWads ; M ++)                                                                /* Unselect all WAD files */
        WadInfo[M]->Selected = FALSE;
      fscanf (Fp, "%s", S);                                                                             /* First read the command */
      while (!feof (Fp)
             && stricmp (S, STARTALONE)
             && stricmp (S, STARTIPX) && stricmp (S, IpxDriver)
             && stricmp (S, STARTLINK) && stricmp (S, SerDriver))
        fscanf (Fp, "%s", S);
      if (!stricmp (S, STARTALONE))
        PlayTypeActive = 1;
      else
        if (!stricmp (S, STARTIPX) || !stricmp (S, IpxDriver))
          PlayTypeActive = 2;
        else
            if (!stricmp (S, STARTLINK) || !stricmp (S, SerDriver))
              PlayTypeActive = 3;
            else
              Stop = TRUE;                                                                            /* Not a DOOM start command */
      if (!Stop)
      {
        fscanf (Fp, "%s", S);
        while (!feof (Fp) && !Stop)                                                                      /* Handle each parameter */
        {
          Handled = FALSE;
          if (S[0] == '@')                                                                           /* 'Response' file following */
          {
            fclose (Fp);                                                                                     /* Close "START.BAT" */
            if (!(Fp = fopen (S + 1, "r")))                                                           /* Open the 'Response' file */
              Stop = TRUE;                                                                     /* Stop if the file does not exist */
            else
              fscanf (Fp, "%s", S);                                                                /* Else: read the first string */
          }
          if (!feof (Fp) && !stricmp (S, NWSOCKET) && !Stop)                                                   /* Found '-SOCKET' */
          {
            fscanf (Fp, "%s", S);                                                           /* '-SOCKET' takes a parameter: 0-255 */
            if (feof (Fp))
              Stop = TRUE;
            for (N = 0 ; N < strlen (S) && !Stop ; N ++)
              Stop = !isdigit (S[N]);
            if (!Stop)
            {
              NetworkSocket = atoi (S);
              Handled = TRUE;
              fscanf (Fp, "%s", S);
            }
          }
          if (!feof (Fp) && !stricmp (S, SKILL) && !Stop)                                                       /* Found '-SKILL' */
          {
            fscanf (Fp, "%s", S);                                                              /* '-SKILL' takes a parameter: 1-5 */
            if (feof (Fp) || strlen (S) != 1 || S[0] < '1' || S[0] > '5')
              Stop = TRUE;
            else
            {
              DifficultyActive = S[0] - '0';
              Handled = TRUE;
              fscanf (Fp, "%s", S);
            }
          }
          if (!feof (Fp) && !stricmp (S, DMATCH) && !Stop)                                                 /* Found '-DEATHMATCH' */
          {
            DeathmatchOn = TRUE;
            Handled = TRUE;
            fscanf (Fp, "%s", S);
          }
          if (!feof (Fp) && !stricmp (S, DMATCHV2) && !Stop)                                                 /* Found '-ALTDEATH' */
          {
            DeathmatchV2On = TRUE;
            Handled = TRUE;
            fscanf (Fp, "%s", S);
          }
          if (!feof (Fp) && !stricmp (S, NOMONSTERS) && !Stop)                                             /* Found '-NOMONSTERS' */
          {
            NoMonstersOn = TRUE;
            Handled = TRUE;
            fscanf (Fp, "%s", S);
          }
          if (!feof (Fp) && !stricmp (S, RESPAWNMONSTERS) && !Stop)                                           /* Found '-RESPAWN' */
          {
            RespMonstersOn = TRUE;
            Handled = TRUE;
            fscanf (Fp, "%s", S);
          }
          if (!feof (Fp) && !stricmp (S, FASTMONSTERS) && !Stop)                                                 /* Found '-FAST' */
          {
            FastMonstersOn = TRUE;
            Handled = TRUE;
            fscanf (Fp, "%s", S);
          }
          if (!feof (Fp) && !stricmp (S, GOTOANYTHING) && !Stop)                                                 /* Found '-WARP' */
          {
            fscanf (Fp, "%s", S);                                                             /* '-WARP' takes a parameter: 01-32 */
            if (feof (Fp))
              Stop = TRUE;
            for (N = 0 ; N < strlen (S) && !Stop ; N ++)
              Stop = !isdigit (S[N]);
            if (!Stop)
            {
              CurrentLevel = atoi (S);
              Handled = TRUE;
              fscanf (Fp, "%s", S);
            }
          }
          if (!feof (Fp) && !strnicmp (S, COMPORT, 4) && !Stop)                                                  /* Found '-COM#' */
          {
            if (strlen (S) != 5 || S[4] < '1' || S[0] > '4')                       /* COM port number is last char in this string */
              Stop = TRUE;                                                                     /* Port number must be between 1-4 */
            else
            {
              CommPortActive = S[4] - '0';
              Handled = TRUE;
              fscanf (Fp, "%s", S);
            }
          }
          if (!feof (Fp) && !stricmp (S, NUMPLAYERS) && !Stop)                                                  /* Found '-NODES' */
          {
            fscanf (Fp, "%s", S);                                                              /* '-NODES' takes a parameter: 2-4 */
            if (feof (Fp) || strlen (S) != 1 || S[0] < '2' || S[0] > '4')
              Stop = TRUE;
            else
            {
              NumNodesActive = S[0] - '0';
              Handled = TRUE;
              fscanf (Fp, "%s", S);
            }
          }
          if (!feof (Fp) && !stricmp (S, INCFILE) && !Stop)                                                      /* Found '-FILE' */
          {
            fscanf (Fp, "%s", S);                                                /* Read-ahead first filename (at least 1 needed) */
            do                                                                    /* Each following word is a filename, until the */
            {                                                                      /* next keyword is found or EOF is encountered */
              if (!feof (Fp) && !Stop)
              {
                DriveNum = DoomDrive;                                                         /* Assume: not preceded by a drive: */
                if (S[1] == ':')                                                                            /* Preceded by drive: */
                  if (toupper (S[0]) < 'A' || toupper (S[0]) > 'Z')
                    Stop = TRUE;
                  else
                  {
                    DriveNum = ToNumber (S[0]);
                    for (M = 2 ; M <= strlen (S) ; M ++)                                                             /* Cut drive */
                      S[M - 2] = S[M];
                  }
                if (!Stop)
                  if (stricmp (S + strlen (S) - 4, ".WAD"))                                      /* Filename MUST end with '.WAD' */
                    Stop = TRUE;
                if (!Stop)
                {
                  S[strlen (S) - 4] = '\0';                                                   /* Cut the '.WAD' from the filename */
                  M = strlen (S);
                  if (!M || S[M - 1] == '\\')                                           /* Ended with a '\' or no filename at all */
                    Stop = TRUE;
                  else
                  {
                    while (S[-- M] != '\\' && M >= 0)
                      ;
                    if (M >= 0)                                                                               /* Preceded by path */
                    {
                      if (M == 0)
                        strcpy (Directory, "\\");                                                      /* Handle root differently */
                      else
                      {
                        strncpy (Directory, strupr (S), M);
                        Directory[M] = '\0';
                        if (DriveNum == DoomDrive && Directory[0] != '\\')
                        {
                          sprintf (Tmp, "%s\\%s", DoomDirectory, Directory);
                          strcpy (Directory, Tmp);
                        }
                      }
                      strcpy (FileName, strupr (S) + M + 1);
                    }
                    else                                                                                               /* No path */
                    {
                      strcpy (FileName, strupr (S));
                      if (DriveNum == DoomDrive)
                        strcpy (Directory, DoomDirectory);
                      else
                        strcpy (Directory, DEFAULTWADDIR);
                    }
                    if (strlen (FileName) > 8)                                        /* Filename contains more than 8 characters */
                      Stop = TRUE;
                    else
                    {
                      for (M = strlen (FileName) ; M < 8 ; M ++)                             /* Fill out filename to 8 characters */
                        FileName[M] = ' ';
                      FileName[M] = '\0';
                      Handled = FALSE;
                      for (M = 0 ; M < TotalWads && !Handled; M ++)                  /* Match against all WAD filenames in memory */
                        if (WadInfo[M]->Drive == DriveNum)
                          if (!strcmp (WadInfo[M]->Path, Directory))
                            if (!strcmp (WadInfo[M]->Name, FileName))
                            {
                              Handled = TRUE;
                              WadInfo[M]->Selected = TRUE;                            /* If it matches, than auto-select the file */
                            }
                      fscanf (Fp, "%s", S);                                                                          /* Read next */
                    }
                  }
                }
              }
            }
            while (!feof (Fp) && S[0] != '-' && !Stop);
            Handled = TRUE;
          }
          if (!Handled && !feof (Fp) && !Stop)
            fscanf (Fp, "%s", S);                                                                           /* Other switch: skip */
        }
      }
      fclose (Fp);
      if (DeathmatchOn && DeathmatchV2On)                                          /* -DEATHMATCH -ALTDEATH means deathmatch v2.0 */
        DeathmatchOn = FALSE;
      if (NoMonstersOn)                                                                              /* Handle contradictionaries */
      {
        RespMonstersOn = FALSE;
        FastMonstersOn = FALSE;
      }
      PrintLevel ();
      PrintLevelPagers (0);
      PrintDifficulties (0);
      PrintPlayTypes (0);
      PrintDeathmatch (FALSE);
      PrintV2Deathmatch (FALSE);
      PrintNoMonsters (FALSE);
      PrintRespawnMonsters (FALSE);
      PrintFastMonsters (FALSE);
      PrintWadFiles ();
    }
  }
  ShowMouse ();
}

void HandleAutomatic (boolean KeyInput)

/**********************************************************************************************************************************/
/* Pre   : 'KeyInput' is TRUE if this routine was called because of a keypress.                                                   */
/* Post  : The mouse pointer was at the automatic item. This item has been highlited. If the mouse button was pressed, then this  */
/*         routine reads all selected WAD files and initializes the current level to the lowest ones found.                       */
/* Import: HideMouse, ShowMouse, PrintAutomatic, PrintLevel, PrintLevelPagers, UnselectPreviousField.                             */
/**********************************************************************************************************************************/

{
  unsigned long  register Startpoint1 = 0x00000000;
  unsigned long  register Startpoint2 = 0x00000000;
  unsigned long  register Startpoint3 = 0x00000000;
  unsigned short register Startpoint4 = 0x00;
  unsigned long  register LevelMask;
  boolean                 Found       = FALSE;

  HideMouse ();
  if (!KeyInput)
    UnselectPreviousField (AUTOMATICFIELD);
  if (!KeyInput)
  {
    PrintAutomatic (TRUE);
    CurrentField = AUTOMATICFIELD;
  }
  ShowMouse ();
  if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput)
  {
    for (M = 0 ; M < TotalWads ; M ++)
      if (WadInfo[M]->Selected)                                       /* Now merge all level information of all selcted WAD files */
      {
        GetWadInfo (M, TRUE);
        Startpoint1 |= WadInfo[M]->NewLevels1;
        Startpoint2 |= WadInfo[M]->NewLevels2;
        Startpoint3 |= WadInfo[M]->NewLevels3;
        Startpoint4 |= WadInfo[M]->NewLevels4;
      }
    if (Startpoint1 != 0x00000000 ||
        Startpoint2 != 0x00000000 ||
        Startpoint3 != 0x00000000 ||
        Startpoint4 != 0x00)                                                               /* Selected files contain new levels ? */
    {
      CurrentLevel = 1;
      LevelMask = 0x00000001;
      for (M = 0 ; M < 32 && !Found ; M ++)
        if ((Startpoint1 & LevelMask) == LevelMask)                                                /* Search for the lowest level */
          Found = TRUE;
        else
        {
          LevelMask *= 2;                                                                                           /* Shift left */
          CurrentLevel ++;
        }
      LevelMask = 0x00000001;
      for (M = 0 ; M < 32 && !Found ; M ++)
        if ((Startpoint2 & LevelMask) == LevelMask)
          Found = TRUE;
        else
        {
          LevelMask *= 2;
          CurrentLevel ++;
        }
      LevelMask = 0x00000001;
      for (M = 0 ; M < 32 && !Found ; M ++)
        if ((Startpoint3 & LevelMask) == LevelMask)
          Found = TRUE;
        else
        {
          LevelMask *= 2;
          CurrentLevel ++;
        }
      LevelMask = 0x00000001;
      for (M = 0 ; M < 3 && !Found ; M ++)
        if ((Startpoint4 & LevelMask) == LevelMask)
          Found = TRUE;
        else
        {
          LevelMask *= 2;
          CurrentLevel ++;
        }
    }
    else                                                                                                /* No levels in the files */
      CurrentLevel = 1;
    PrintLevel ();
    PrintLevelPagers (0);
  }
}
 
boolean _fastcall HandleStartGame (boolean KeyInput)

/**********************************************************************************************************************************/
/* Pre   : 'KeyInput' is TRUE if this routine was called because of a keypress.                                                   */
/* Post  : The mouse pointer was at the startgame item. This item has been highlited. If the mouse button was pressed, then this  */
/*         routine returns FALSE (which effectively means: start the game), otherwise TRUE.                                       */
/* Import: HideMouse, ShowMouse, PrintStart, UnselectPreviousField.                                                               */
/**********************************************************************************************************************************/

{
  HideMouse ();
  UnselectPreviousField (STARTFIELD);
  PrintStart (TRUE);
  CurrentField = STARTFIELD;
  ShowMouse ();
  return ((!Mouse.Left || Mouse.LeftStillPressed) && !KeyInput);
}

int main (int argc, char **argv)

/**********************************************************************************************************************************/
/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> MAIN ROUTINE <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
/* Import: ResetMouse, ReadConfig, PrintDifficulties, PrintPlayTypes, PrintDeathmatch, PrintLevel, PrintLevelPagers,              */
/*         PrintWadFiles, PrintPagers, ShowMouse, MouseStatus, HandleDifficulty, HandleDeathmatch, HandleLevel, HandleNextPage,   */
/*         HandlePreviousPage, HideMouse, DeAllocateAll, PrintStart, PrintRdPrev, HandleStartGame, HandleReadPrev, GetWadInfo,    */
/*         WriteWadInfo, PrintAutomatic, HandleAutomatic, HandlePlayType, UnselectPreviousField, InitVideo, PrintV2Deathmatch,    */
/*         PrintRespawnMonsters, PrintNoMonsters, PrintFastMonsters, HandleV2Deathmatch, HandleRespawnMonsters, HandleNoMonsters, */
/*         HandleFastMonsters, SortFiles, RescanFiles, ToggleFileSort, Bye, PrText.                                               */
/**********************************************************************************************************************************/

{
  FILE    *Fp;
  struct   find_t      FileBlock;
  struct   WadInfo_s   ReadInfo;                                                            /* Entry as read from the WADINFOFILE */
  char     DrivePath[_MAX_DIR];                                                                   /* Current directory of a drive */
  char     NextArgument;                                                                   /* Argument number on the command line */
  char     Ch;
  char     SepChar;                                                                    /* Seperation char in START.*; '\n' or ' ' */
  boolean  SepCharNeeded;
  boolean  More;
  boolean  FirstWad;                                                                  /* First WAD file to include in START.BAT ? */
  boolean  AutoFound;                                                                  /* Files given as AUTOINCLUDE paremeters ? */
  boolean  DriveChanged;                                                          /* Handling other drive than the 'home' drive ? */
  boolean  CLineError  = FALSE;                                                                 /* Nonsense on the command line ? */
  boolean  WarpDone    = FALSE;                                                    /* TRUE if WARP has been included in START.BAT */
  unsigned int Key;

  free (getcwd (CurPath, _MAX_DIR - 1));                                                             /* Collect current directory */
  CurDrive = ToNumber (CurPath[0]);                                                                       /* Derive current drive */
  UseMouse = ResetMouse ();                                                                       /* Force mouse to re-initialize */
  strcpy (ConfigFile, DEFAULTCONFIGFILE);
  NextArgument = 0;                                                                              /* Handle command line arguments */
  if (argc > 3)                                                                                 /* (Max number of arguments is 2) */
    CLineError = TRUE;
  while (++ NextArgument < argc)
    if (!stricmp (argv[NextArgument], RESCAN1) || !stricmp (argv[NextArgument], RESCAN2))              /* Handle any -r parameter */
      Rescan = TRUE;
    else
      if (argv[NextArgument][0] == OTHERCONFIGFILE)                                                 /* Handle any +file parameter */
      {
        strcpy (ConfigFile, argv[NextArgument] + 1);
        ConfigChange = TRUE;
      }
      else
        CLineError = TRUE;
  if (CLineError)
    Bye (RETURNERROR, "Usage: EASYWAD [%s] [%cconfig]\n", RESCAN1, OTHERCONFIGFILE);
  ReadConfig ();                                                                                 /* Find CONFIGFILE and handle it */
  if (!(Fp = fopen (InfoFile, "r")))                                                               /* Test if the InfoFile exists */
    Rescan = TRUE;                                                                                    /* No, it should be created */
  else
    fclose (Fp);
  TotalWads = 0;
  if (Rescan)
    DoNotSearch = FALSE;                                                 /* Override if there is no infofile or if '-R' was given */
  _dos_setdrive (CurDrive, &Dummy);                                             /* Start from 'home' location (for partial paths) */
  chdir (CurPath);
  if (!DoNotSearch)                                                                            /* Search all WADDIR directories ? */
  {
    printf ("Searching for WAD files ... ");
    for (N = 0 ; N < TotalWadDirs ; N ++)                                                         /* Handle all given directories */
    {
      DriveChanged = FALSE;
      if (WadDir[N]->Drive)                                                                      /* Change drive if one was given */
      {
        _dos_setdrive (WadDir[N]->Drive, &Dummy);
        _dos_getdrive (&Dummy);
        if (Dummy != WadDir[N]->Drive)                                /* Check that the change has been made and report any error */
          Bye (RETURNERROR, "ERROR - Drive of WADDIR does not exist\n");
        DriveChanged = TRUE;
      }
      free (getcwd (DrivePath, _MAX_DIR - 1));                                         /* Collect current directory of this drive */
      if (strcmp (WadDir[N]->Name, DEFAULTWADDIR))                                   /* Change directory if the entry was not "." */
        if (chdir (WadDir[N]->Name))                                          /* Report the error if the directory does not exist */
          Bye (RETURNERROR, "ERROR - Directory of WADDIR does not exist\n");
      More = !_dos_findfirst (WADFILE, ANYATTR, &FileBlock);                                         /* Look-ahead for a WAD file */
      while (TotalWads < MAXWADS && More)                                               /* Handle all WAD files in this directory */
      {
        if ((strcmp (WadDir[N]->Name, DEFAULTWADDIR) || strcmp (FileBlock.name, MAINWAD))
            && FileBlock.size >= sizeof (struct WadHeader_s))                    /* Exclude the main DOOM WAD and too small files */
        {
          if ((WadInfo[TotalWads] = ((struct WadInfo_s far *)_fmalloc ((size_t)sizeof (struct WadInfo_s)))) == NOMEM)
            Bye (RETURNERROR, "FATAL ERROR - Out of memory\n");
          strcpy (WadInfo[TotalWads]->OrigName, FileBlock.name);                                                 /* Copy filename */
          strncpy (WadInfo[TotalWads]->Name, FileBlock.name, strlen (FileBlock.name) - 4);        /* Copy name, without extension */
          for (M = strlen (FileBlock.name) - 4 ; M < 8 ; M ++)                                        /* Fill out to 8 characters */
            WadInfo[TotalWads]->Name[M] = ' ';
          WadInfo[TotalWads]->Name[M] = '\0';                                                           /* Make it a valid string */
          WadInfo[TotalWads]->Drive = WadDir[N]->Drive;
          strcpy (WadInfo[TotalWads]->Path, WadDir[N]->Name);
          for (M = 0 ; M < MAXINFOLEN ; M ++)
            WadInfo[TotalWads]->Info[M] = ' ';                                                     /* Initialize the other fields */
          WadInfo[TotalWads]->Info[M] = '\0';
          WadInfo[TotalWads]->Selected = FALSE;
          if (Rescan)
            GetWadInfo (TotalWads, FALSE);                                                          /* Read out the WAD directory */
          TotalWads ++;
        }
        More = !_dos_findnext (&FileBlock);
      }
      if (WadDir[N]->DoSubDirs)
      {
        More = !_dos_findfirst ("*.*", SUBATTR, &FileBlock);                                           /* Look for subdirectories */
        while (More)
        {
          while ((!(FileBlock.attrib & _A_SUBDIR) || (FileBlock.name[0] == '.')) && More)                      /* Not '.' or '..' */
            More = !_dos_findnext (&FileBlock);
          if (More)
          {
            if (TotalWadDirs == MAXWADDIRS)
              Bye (RETURNERROR, "ERROR - Too many WADDIR entries, max is %d\n", MAXWADDIRS);
            if ((WadDir[TotalWadDirs] = ((struct WadDir_s far *)_fmalloc ((size_t)sizeof (struct WadDir_s)))) == NOMEM)
              Bye (RETURNERROR, "FATAL ERROR - Out of memory\n");
            WadDir[TotalWadDirs]->Drive = WadDir[N]->Drive;                              /* Add this directory to the WADDIR list */
            sprintf (WadDir[TotalWadDirs]->Name, "%s\\%s", WadDir[N]->Name, FileBlock.name);
            WadDir[TotalWadDirs ++]->DoSubDirs = TRUE;                                                         /* Signal: recurse */
            More = !_dos_findnext (&FileBlock);
          }
        }
      }
      chdir (DrivePath);                                                         /* Return to the current directory of this drive */
      if (DriveChanged)
        _dos_setdrive (CurDrive, &Dummy);                                                                 /* Return to home drive */
    }
  }
  _dos_setdrive (CurDrive, &Dummy);                                                                    /* Return to home location */
   chdir (CurPath);
  if (Rescan)
  {
    printf ("Writing WADINFOFILE ... ");
    WriteWadInfo (InfoFile);
  }
  if (!(Fp = fopen (InfoFile, "r")))                                            /* Now handle the InfoFile, which should be found */
    Bye (RETURNERROR, "ERROR - Drive read error\n");
  fscanf (Fp, "%s", S);                                                             /* Read-ahead: first part is the drive number */
  while (!feof (Fp))
  {
    ReadInfo.Drive = atoi (S);                                                                               /* Convert to number */
    fscanf (Fp, "%s", ReadInfo.Path);                                                             /* Second part is the directory */
    fscanf (Fp, "%s ", ReadInfo.OrigName);                                                         /* Third part is the  filename */
    More = TRUE;
    for (M = 0 ; M < MAXINFOLEN && More ; M ++)                                   /* Last part is the info. It may contain spaces */
    {
      fscanf (Fp, "%c", &ReadInfo.Info[M]);
      if (ReadInfo.Info[M] == '\n')                                                              /* String shorter than maximum ? */
        More = FALSE;
    }
    while (More)                                                                                     /* End of line not yet found */
    {
      fscanf (Fp, "%c", &Ch);
      if (Ch == '\n')                                                                                /* Read rest of the line out */
        More = FALSE;
    }
    ReadInfo.Info[M] = '\0';
    ConvertFullName (&ReadInfo);                                                          /* Convert full to short name if wanted */
    if (DoNotSearch)
    {
      if ((WadInfo[TotalWads] = ((struct WadInfo_s far *)_fmalloc ((size_t)sizeof (struct WadInfo_s)))) == NOMEM)
        Bye (RETURNERROR, "FATAL ERROR - Out of memory\n");
      strcpy (WadInfo[TotalWads]->OrigName, ReadInfo.OrigName);                                                  /* Copy filename */
      strncpy (WadInfo[TotalWads]->Name, ReadInfo.OrigName, strlen (ReadInfo.OrigName) - 4);      /* Copy name, without extension */
      for (M = strlen (ReadInfo.OrigName) - 4 ; M < 8 ; M ++)                                         /* Fill out to 8 characters */
        WadInfo[TotalWads]->Name[M] = ' ';
      WadInfo[TotalWads]->Name[M] = '\0';
      WadInfo[TotalWads]->Drive = ReadInfo.Drive;
      strcpy (WadInfo[TotalWads]->Path, ReadInfo.Path);
      strcpy (WadInfo[TotalWads]->Info, ReadInfo.Info);
      WadInfo[TotalWads]->Selected = FALSE;
      TotalWads ++;
    }
    else
    {
      More = TRUE;
      for (M = 0 ; M < TotalWads && More; M ++)                                          /* Match against all read WAD file names */
        if (WadInfo[M]->Drive == ReadInfo.Drive)
          if (!stricmp (WadInfo[M]->Path, ReadInfo.Path))
            if (!stricmp (WadInfo[M]->OrigName, ReadInfo.OrigName))
            {
              More = FALSE;
              strcpy (WadInfo[M]->Info, ReadInfo.Info);                                    /* Copy info field to correct WAD file */
            }
    }
    fscanf (Fp, "%s", S);
  }
  fclose (Fp);
  if (TotalWads == 0)                              /* Abort the program if no WAD files were found (other than the main DOOM WAD) */
  {
    remove (InfoFile);
    Bye (RETURNERROR, "No WAD files found!\n");
  }
  SortFiles ();                                                                       /* Sort the WAD files if that was requested */
  N = -1;
  while (++ N < TotalAutoInc)                                                                   /* Handle all AUTOINCLUDE entries */
  {
    AutoFound = FALSE;
    for (M = 0 ; M < TotalWads && !AutoFound ; M ++)                                         /* AUTOINCLUDE file ? Then select it */
      if ((WadInfo[M]->Drive == AutoInc[N]->Drive)
           && (!strcmp (WadInfo[M]->Path, AutoInc[N]->Path))
           && (!strcmp (WadInfo[M]->OrigName, AutoInc[N]->OrigName)))
      {
        WadInfo[M]->Selected = TRUE;
        AutoFound = TRUE;
      }
  }
  CurrentPage = 0;
  LastPage = TotalWads / PAGE;

  InitVideo ();                                                /* Open a VGA screen of 640x480x16 (80x30 characters in text mode) */
  ScreenOpen = TRUE;                                                                                  /* Signal: in graphics mode */
  PrText (-1, 1, 1, LWHITE, "DOOM ][ EasyWAD2 v1.01 - (C) 94-95 ThunderWare Research Center");                 /* Draw the screen */
  PrText (-1, 1, 72, DWHITE, "F1 = HELP");
  PrText (-1, 11, 64, LWHITE, "PAGE  1 OF %2d", LastPage + 1);
  PrintDifficulties (0);
  PrintPlayTypes (0);
  PrintLevel ();
  PrintDeathmatch (FALSE);
  PrintV2Deathmatch (FALSE);
  PrintRespawnMonsters (FALSE);
  PrintNoMonsters (FALSE);
  PrintFastMonsters (FALSE);
  PrintWadFiles ();
  PrintPagers (0);
  PrintLevelPagers (0);
  PrintRdPrev (FALSE);
  PrintAutomatic (FALSE);
  PrintStart (FALSE);

  if (UseMouse)
  {
    ShowMouse ();                                                                                       /* Show the mouse pointer */
    MouseStatus ();                                                                                   /* Get current mouse status */
    Mouse.OldXx = Mouse.Xx;                                                                 /* Initialize all non-hardware fields */
    Mouse.OldYy = Mouse.Yy;
    Mouse.CoChange = TRUE;
    Mouse.LeftStillPressed = FALSE;
    CurrentField = NOFIELD;                                                                  /* Signal: check FIELD type directly */
  }
  else
  {
    CurrentField = FILEFIELD;                                                        /* Hi-lighting is only done in the FILEFIELD */
    PreviousWad = 1;
    PrText (-1, 13, 1, LRED, WadInfo[0]->Name);                                                    /* Hi-light the first WAD file */
  }
  More = TRUE;
  while (More)                                                                               /* Or: while not [START] pressed ... */
  {
    if (_bios_keybrd (_KEYBRD_READY))                                                                            /* Key pressed ? */
    {
      Key = _bios_keybrd (_KEYBRD_READ);                                                                          /* Read the key */
      if ((Key & 0xFF00) == KEY_PAGEDOWN)
        HandlePreviousPage (TRUE);
      else
        if ((Key & 0xFF00) == KEY_PAGEUP)
          HandleNextPage (TRUE);
        else
          if (!UseMouse                                                                       /* Only available if no mouse found */
              && ((Key & 0xFF00) == KEY_CURSLEFT
               || (Key & 0xFF00) == KEY_CURSRIGHT
               || (Key & 0xFF00) == KEY_CURSUP
               || (Key & 0xFF00) == KEY_CURSDOWN
               || (Key & 0xFF00) == KEY_SELECTFILE))
            HandleFile (Key & 0xFF00);
          else
            if ((Key & 0xFF00) == KEY_DELETEWAD)
              DeleteWadFile ();
            else
              if ((Key & 0xFF00) == KEY_TOGGLESORT)
                ToggleFileSort ();
              else
                if ((Key & 0xFF00) == KEY_RESCAN)
                  RescanFiles ();
                else
                  if ((Key & 0xFF00) == KEY_HELP)
                    GiveHelp ();
                  else
                    if ((Key & 0xFF00) == KEY_TOGGLEFULL)
                      ToggleFullName ();
                    else
                      switch (toupper ((char)(Key & 0x00FF)))
                      {
                        case KEY_ABORT        : Bye (RETURNABORT, "Program aborted - Thank you for using DOOM ][ EasyWAD2\n");
                        case KEY_STARTGAME    : More = HandleStartGame (TRUE);
                                                break;
                        case KEY_DIFFICULTY   : HandleDifficulty (TRUE);
                                                break;
                        case KEY_LEVELUP      : HandleNextLevel (TRUE);
                                                break;
                        case KEY_LEVELDOWN    : HandlePreviousLevel (TRUE);
                                                break;
                        case KEY_DEATHMATCH   : HandleDeathmatch (TRUE);
                                                break;
                        case KEY_DEATHMATCHV2 : HandleV2Deathmatch (TRUE);
                                                break;
                        case KEY_NOMONSTERS   : HandleNoMonsters (TRUE);
                                                break;
                        case KEY_RESPAWNMONST : HandleRespawnMonsters (TRUE);
                                                break;
                        case KEY_FASTMONSTERS : HandleFastMonsters (TRUE);
                                                break;
                        case KEY_NODES        :
                        case KEY_COMPORT      :
                        case KEY_PLAYTYPE     : HandlePlayType (TRUE, toupper ((char)(Key & 0x00FF)));
                                                break;
                        case KEY_AUTO         : HandleAutomatic (TRUE);
                                                break;
                        case KEY_READPREVIOUS : HandleReadPrev (TRUE);
                      }
    }
    if (UseMouse && (Mouse.CoChange || Mouse.Left))                                        /* Mouse moved or left buttonpressed ? */
    {
      if (Mouse.Yy > 11)                                              /* Find out which FIELD is pointed to and handle that field */
        HandleFile (0x0000);                                                                                    /* Signal: no key */
      else
        if (Mouse.Yy > 1 && Mouse.Yy < 7 && Mouse.Xx > 26 && Mouse.Xx < 51)
          HandleDifficulty (FALSE);
        else
          if (Mouse.Yy > 1 && Mouse.Yy < 6 && Mouse.Xx > 53)
            HandlePlayType (FALSE, 0x00);
          else
            if (Mouse.Yy == 7 && Mouse.Xx > 53)
              HandleDeathmatch (FALSE);
            else
              if (Mouse.Yy == 8 && Mouse.Xx > 53)
                HandleV2Deathmatch (FALSE);
              else
                if (Mouse.Yy == 8 && Mouse.Xx > 26 && Mouse.Xx < 51)
                  HandleNoMonsters (FALSE);
                else
                  if (Mouse.Yy == 9 && Mouse.Xx > 26 && Mouse.Xx < 51)
                    HandleRespawnMonsters (FALSE);
                  else
                    if (Mouse.Yy == 10 && Mouse.Xx > 26 && Mouse.Xx < 51)
                      HandleFastMonsters (FALSE);
                    else
                      if (Mouse.Yy == 2 && Mouse.Xx > 1 && Mouse.Xx < 5)
                        HandlePreviousLevel (FALSE);
                      else
                        if (Mouse.Yy == 2 && Mouse.Xx > 12 && Mouse.Xx < 16)
                          HandleNextLevel (FALSE);
                        else
                          if (Mouse.Yy == 10 && Mouse.Xx > 76)
                            HandleNextPage (FALSE);
                          else
                            if (Mouse.Yy == 10 && Mouse.Xx > 58 && Mouse.Xx < 62)
                              HandlePreviousPage (FALSE);
                            else
                              if (Mouse.Yy == 8 && Mouse.Xx < 15)
                                More = HandleStartGame (FALSE);
                              else
                                if (Mouse.Yy == 9 && Mouse.Xx < 15)
                                  HandleAutomatic (FALSE);
                                else
                                  if (Mouse.Yy == 10 && Mouse.Xx < 15)
                                    HandleReadPrev (FALSE);
                                  else
                                    if (CurrentField != NOFIELD)                                       /* No FIELD was pointed to */
                                    {                        /* If previously a FIELD WAS pointed to, then un-highlite that field */
                                      HideMouse ();
                                      UnselectPreviousField (NOFIELD);
                                      ShowMouse ();
                                      CurrentField = NOFIELD;
                                    }
      Mouse.OldXx = Mouse.Xx;
      Mouse.OldYy = Mouse.Yy;
    }
    if (UseMouse)
    {
      MouseStatus ();
      if (Mouse.Right)
      {
        HideMouse ();
        Bye (RETURNABORT, "Program aborted - Thank you for using DOOM ][ EasyWAD2\n");
      }
    }
  }
  HideMouse ();
  _setvideomode (_DEFAULTMODE);                                                                               /* Close the screen */
  ScreenOpen = FALSE;                                                                                   /* Signal: in normal mode */
  
  if (!(Fp = fopen (BATFILE, "w")))                                                                           /* Open "START.BAT" */
    Bye (RETURNERROR, "ERROR - Cannot create file, disk full ?\n");
  fprintf (Fp, "@ECHO OFF\n");
  if (DoomDrive)
    fprintf (Fp, "%c:\n", ToName (DoomDrive));
  if (strcmp (DoomDirectory, DEFAULTWADDIR))
    fprintf (Fp, "CD %s\n", DoomDirectory);
  if (!NoAutoReturn)
    fprintf (Fp, "ECHO.| ");
  switch (PlayTypeActive)                                                                /* Print the correct command to the file */
  {
    case 1 : fprintf (Fp, "%s", STARTALONE);
             break;
    case 2 : fprintf (Fp, "%s", IpxDriver);
             break;
    case 3 : fprintf (Fp, "%s", SerDriver);
  }
  fprintf (Fp, " @%s\\%s\n", CurPath, RESPONSEFILE);                                     /* CurPath also contains the drive: part */
  if (DoomDrive)
    fprintf (Fp, "%c:\n", ToName (CurDrive));
  if (strcmp (DoomDirectory, DEFAULTWADDIR))
    fprintf (Fp, "CD %s\n", CurPath + 2);
  fclose (Fp);
  if (!(Fp = fopen (RESPONSEFILE, "w")))                                                                      /* Open "START.OPT" */
    Bye (RETURNERROR, "ERROR - Cannot create file, disk full ?\n");
  SepChar = '\n';
  SepCharNeeded = TRUE;
  switch (PlayTypeActive)
  {
    case 1 : SepCharNeeded = FALSE;
             break;
    case 2 : if (!OtherIpxDriver)
               fprintf (Fp, "%s%c%d%c%d", NUMPLAYERS, SepChar, NumNodesActive, SepChar, NetworkSocket);
             break;
    case 3 : if (!OtherSerDriver)
               fprintf (Fp, "%s%d", COMPORT, CommPortActive);
  }
  if (DifficultyActive != DEFAULTDIFFICULTY)                                                            /* Different difficulty ? */
  {
    if (SepCharNeeded)
      fprintf (Fp, "%c", SepChar);
    SepCharNeeded = TRUE;
    if (!WarpDone)
    {
      fprintf (Fp, "%s%c%02d%c", GOTOANYTHING, SepChar, CurrentLevel, SepChar);
      WarpDone = TRUE;
    }
    fprintf (Fp, "%s%c%d", SKILL, SepChar, DifficultyActive);                                                      /* Then add it */
  }
  if (DeathmatchOn != DEFAULTDMATCH)                                                     /* DEATHMATCH wanted (and implemented) ? */
  {
    if (SepCharNeeded)
      fprintf (Fp, "%c", SepChar);
    SepCharNeeded = TRUE;
    fprintf (Fp, "%s", DMATCH);
  }
  if (DeathmatchV2On != DEFAULTDMATCHV2)                                                              /* DEATHMATCH v2.0 wanted ? */
  {
    if (SepCharNeeded)
      fprintf (Fp, "%c", SepChar);
    SepCharNeeded = TRUE;
    fprintf (Fp, "%s%c%s", DMATCH, SepChar, DMATCHV2);
  }
  if (NoMonstersOn != DEFAULTNOMONSTERS)                                                                   /* NOMONSTERS wanted ? */
  {
    if (SepCharNeeded)
      fprintf (Fp, "%c", SepChar);
    SepCharNeeded = TRUE;
    if (!WarpDone)
    {
      fprintf (Fp, "%s%c%02d%c", GOTOANYTHING, SepChar, CurrentLevel, SepChar);
      WarpDone = TRUE;
    }
    fprintf (Fp, "%s", NOMONSTERS);
  }
  if (RespMonstersOn != DEFAULTRESPAWN)                                                                       /* RESPAWN wanted ? */
  {
    if (SepCharNeeded)
      fprintf (Fp, "%c", SepChar);
    SepCharNeeded = TRUE;
    if (!WarpDone)
    {
      fprintf (Fp, "%s%c%02d%c", GOTOANYTHING, SepChar, CurrentLevel, SepChar);
      WarpDone = TRUE;
    }
    fprintf (Fp, "%s", RESPAWNMONSTERS);
  }
  if (FastMonstersOn != DEFAULTFASTMONST)                                                                /* FASTMONSTERS wanted ? */
  {
    if (SepCharNeeded)
      fprintf (Fp, "%c", SepChar);
    SepCharNeeded = TRUE;
    if (!WarpDone)
    {
      fprintf (Fp, "%s%c%02d%c", GOTOANYTHING, SepChar, CurrentLevel, SepChar);
      WarpDone = TRUE;
    }
    fprintf (Fp, "%s", FASTMONSTERS);
  }
  if (CurrentLevel != DEFAULTLEVEL)                                                                 /* Different starting level ? */
  {
    if (SepCharNeeded)
      fprintf (Fp, "%c", SepChar);
    SepCharNeeded = TRUE;
    fprintf (Fp, "%s%c%02d", GOTOANYTHING, SepChar, CurrentLevel);
  }
  for (M = 0 ; M < MAXADDSWITCHES ; M ++)                                                          /* Now add the direct switches */
    if (ExtCom[M].InUse)
    {
      if (SepCharNeeded)
        fprintf (Fp, "%c", SepChar);
      SepCharNeeded = TRUE;
      fprintf (Fp, ExtCom[M].Command);
    }
  FirstWad = TRUE;
  strcat (DoomDirectory, "\\");
  for (M = 0 ; M < TotalWads ; M ++)                                                                /* Add each selected WAD file */
    if (WadInfo[M]->Selected)
    {
      if (FirstWad)                                                                       /* Before adding the first, add "-FILE" */
      {
        FirstWad = FALSE;
        if (SepCharNeeded)
          fprintf (Fp, "%c", SepChar);
        fprintf (Fp, "%s", INCFILE);
      }
      if (WadInfo[M]->Drive && WadInfo[M]->Drive != DoomDrive)
        fprintf (Fp, "%c%c:", SepChar, ToName (WadInfo[M]->Drive));                                     /* Add the drive if found */
      else
        fprintf (Fp, "%c", SepChar);
      if (strcmp (WadInfo[M]->Path, DEFAULTWADDIR))
        if (strncmp (WadInfo[M]->Path, DoomDirectory, strlen (DoomDirectory)))                  /* Subdir of the DOOM directory ? */
          fprintf (Fp, "%s\\", WadInfo[M]->Path);                                                          /* Add the path if not */
        else
          if (WadInfo[M]->Drive == DoomDrive)                                                              /* Drive matches too ? */
            fprintf (Fp, "%s\\", WadInfo[M]->Path + strlen (DoomDirectory));                           /* Cut DOOM directory part */
          else
            fprintf (Fp, "%s\\", WadInfo[M]->Path);                                                        /* Add the path if not */
      fprintf (Fp, "%s", WadInfo[M]->OrigName);                                                               /* Add the filename */
    }
  fprintf (Fp, "\n");                                                                                          /* Add the newline */
  fclose (Fp);                                                                 /* Done; free all memory and shut down the program */
  DeAllocateAll ();
  exit (RETURNSTART);
}
