home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the DOOM Programming Gurus / Tricks_of_the_Doom_Programming_Gurus.iso / bonus / utils / easywad2 / easywad2.c next >
Encoding:
C/C++ Source or Header  |  1995-02-18  |  195.0 KB  |  3,551 lines

  1. /**********************************************************************************************************************************/
  2. /* File         : EASYWAD2.C                                                                                                      */
  3. /* Executable   : EASYWAD2.EXE                                                                                                    */
  4. /* Helpfile     : EASYWAD2.CFG                                                                                                    */
  5. /* Doc file     : EASYWAD2.DOC                                                                                                    */
  6. /* Version num  : 1.01                                                                                                            */
  7. /* Last changed : 18-02-1995  12:37                                                                                               */
  8. /* Update count : 2                                                                                                               */
  9. /* OS type      : PC (DOS)                                                                                                        */
  10. /* Description  : Menu handler for multiple WAD files for DOOM (Trademark of Id Software)                                         */
  11. /* Compiler     : Microsoft (R) Quick C Compiler Version 2.50                                                                     */
  12. /* Linker       : Microsoft (R) QuickC Linker Version 4.10                                                                        */
  13. /* QCL attribs  : /AC /G2 /Ox /F 2000 /link GRAPHICS.LIB (Compact model, fully optimized 80286 code, Stacksize 8K)                */
  14. /* Other        : WM.BAT    : start file                                                                                          */
  15. /*                START.BAT : the result                                                                                          */
  16. /*                START.OPT : response file                                                                                       */
  17. /* Remarks      : Credits go to Brendon Wyber & Raphael Quinet (Doom Editor Utilities) for WadHeader and WadDirectory structures. */
  18. /*                                                                                                                                */
  19. /*                                   By M. van der Heide of ThunderWare Research Center                                           */
  20. /**********************************************************************************************************************************/
  21.  
  22. #include   <bios.h>
  23. #include   <ctype.h>
  24. #include   <direct.h>
  25. #include   <dos.h>
  26. #include   <graph.h>
  27. #include   <malloc.h>
  28. #include   <stdarg.h>
  29. #include   <stdio.h>
  30. #include   <stdlib.h>
  31. #include   <string.h>
  32.  
  33. #pragma    check_stack        (off)
  34. #pragma    check_pointer      (off)
  35.  
  36. #ifndef    TRUE
  37. typedef    char               boolean;
  38. #define    TRUE               1
  39. #define    FALSE              0
  40. #endif
  41.  
  42. #define    DBLACK             0                                             /* Define names for the standard (VGA) palette colors */
  43. #define    DBLUE              1
  44. #define    DGREEN             2
  45. #define    DCYAN              3
  46. #define    DRED               4
  47. #define    DMAGENTA           5
  48. #define    DYELLOW            6
  49. #define    DWHITE             7
  50. #define    LBLACK             8
  51. #define    LBLUE              9
  52. #define    LGREEN             10
  53. #define    LCYAN              11
  54. #define    LRED               12
  55. #define    LMAGENTA           13
  56. #define    LYELLOW            14
  57. #define    LWHITE             15
  58.  
  59. #define    ANYATTR            _A_NORMAL|_A_RDONLY|_A_HIDDEN|_A_SYSTEM|_A_ARCH                                  /* File attributes */
  60. #define    SUBATTR            _A_NORMAL|_A_RDONLY|_A_HIDDEN|_A_SYSTEM|_A_ARCH|_A_SUBDIR                /* Subdirectory attributes */
  61. #define    MAXFNAME           9
  62. #define    MAXWADS            1000                                              /* Max number of WAD files the program can handle */
  63. #define    MAXAUTOINCLUDE     5                                        /* Maximum number of autoinclude-WAD files in a CONFIGFILE */
  64. #define    MAXWADDIRS         400                                  /* Max number of WAD file directories a CONFIGFILE may contain */
  65. #define    PAGE               54                                                             /* Max number of WAD files on screen */
  66. #define    WADHEIGHT          18                                                            /* Max number of filenames vertically */
  67. #define    WADWIDTH           27                                          /* Max positions taken for a filename+info horizontally */
  68. #define    MAXINFOLEN         16                                                           /* Max length of a WAD file info field */
  69. #define    ENTRYLEN           8                                        /* Length of the name of a 'directory' entry in a WAD file */
  70. #define    NOMEM              (void far *)0                                                            /* Memory allocation error */
  71.  
  72. #define    DEFAULTCONFIGFILE  "EASYWAD2.CFG"
  73. #define    OTHERCONFIGFILE    '+'                             /* Used on the command line: use other config file than EASYWAD.CFG */
  74. #define    COMMENT            '#'                      /* All characters in a line following this one are ignored in a CONFIGFILE */
  75. #define    WADFILE            "*.WAD"
  76. #define    BATFILE            "START.BAT"                                                        /* This file is built at the end */
  77. #define    RESPONSEFILE       "START.OPT"                                                                        /* Response file */
  78. #define    DOSUB1             "/S"                                        /* Used in CONFIGFILE in a WADDIR entry: do subdirs too */
  79. #define    DOSUB2             "-S"
  80. #define    RESCAN1            "/R"                                              /* Used on the command line: rebuild WAD InfoFile */
  81. #define    RESCAN2            "-R"
  82.  
  83. #define    MAINWAD            "DOOM2.WAD"                                                                        /* Main WAD file */
  84.  
  85. #define    STARTALONE         "DOOM2"                                                       /* Define the start commands for DOOM */
  86. #define    STARTIPX           "IPXSETUP"
  87. #define    STARTLINK          "SERSETUP"
  88. #define    DMATCH             "-DEATHMATCH"                                             /* Define the command parameters for DOOM */
  89. #define    INCFILE            "-FILE"
  90. #define    GOTOANYTHING       "-WARP"
  91. #define    SKILL              "-SKILL"
  92. #define    NUMPLAYERS         "-NODES"
  93. #define    COMPORT            "-COM"
  94. #define    NOMONSTERS         "-NOMONSTERS"
  95. #define    RESPAWNMONSTERS    "-RESPAWN"
  96. #define    FASTMONSTERS       "-FAST"
  97. #define    DMATCHV2           "-ALTDEATH"
  98. #define    NWSOCKET           "-SOCKET"
  99.  
  100. #define    NOFIELD            0                                                                /* Number the FIELDs on the screen */
  101. #define    FILEFIELD          1 
  102. #define    DIFFICULTYFIELD    3 
  103. #define    PLAYTYPEFIELD      4 
  104. #define    LEVELFIELD         5 
  105. #define    DEATHMATCHFIELD    6 
  106. #define    PAGERFIELD         7 
  107. #define    RDPREVFIELD        8 
  108. #define    STARTFIELD         9 
  109. #define    AUTOMATICFIELD     10 
  110. #define    DEATHMATCHV2FIELD  11 
  111. #define    RESPAWNFIELD       12 
  112. #define    NOMONSTERSFIELD    13 
  113. #define    FASTMONSTERSFIELD  14 
  114.  
  115. #define    DEFAULTVERSION     666                                                                   /* This effectively means 1.0 */
  116. #define    DEFAULTDIFFICULTY  3 
  117. #define    DEFAULTPLAYTYPE    1 
  118. #define    DEFAULTLEVEL       1 
  119. #define    DEFAULTDMATCH      FALSE 
  120. #define    DEFAULTDMATCHV2    FALSE 
  121. #define    DEFAULTRESPAWN     FALSE
  122. #define    DEFAULTNOMONSTERS  FALSE
  123. #define    DEFAULTFASTMONST   FALSE
  124. #define    DEFAULTSOCKET      0
  125. #define    DEFAULTNODES       2
  126. #define    DEFAULTCOMPORT     1
  127. #define    DEFAULTWADDIR      "."                                                                            /* Current directory */
  128. #define    DEFAULTINFOFILE    "WADS.DSC"
  129.  
  130.  
  131. #define    KEY_LEVELUP        '.'                                                     /* Keyboard equivalents of mouse selections */
  132. #define    KEY_LEVELDOWN      ','
  133. #define    KEY_DIFFICULTY     'S'                                                                                      /* (Skill) */
  134. #define    KEY_PLAYTYPE       'T'
  135. #define    KEY_NODES          'N'
  136. #define    KEY_COMPORT        'C'
  137. #define    KEY_DEATHMATCH     'D'
  138. #define    KEY_DEATHMATCHV2   'V'
  139. #define    KEY_AUTO           'A'
  140. #define    KEY_READPREVIOUS   'R'
  141. #define    KEY_NOMONSTERS     'M'
  142. #define    KEY_RESPAWNMONST   'P'
  143. #define    KEY_FASTMONSTERS   'F'
  144. #define    KEY_ABORT          0x1B                                                                                       /* [ESC] */
  145. #define    KEY_STARTGAME      0x0D                                                                                    /* [RETURN] */
  146. #define    KEY_PAGEUP         0x4900
  147. #define    KEY_PAGEDOWN       0x5100
  148. #define    KEY_DELETEWAD      0x5300                                                                                     /* [DEL] */
  149. #define    KEY_HELP           0x3B00                                                                                      /* [F1] */
  150. #define    KEY_RESCAN         0x3F00                                                                                      /* [F5] */
  151. #define    KEY_TOGGLEFULL     0x4100                                                                                      /* [F7] */
  152. #define    KEY_TOGGLESORT     0x4200                                                                                      /* [F8] */
  153.  
  154. #define    KEY_CURSLEFT       0x4B00
  155. #define    KEY_CURSRIGHT      0x4D00
  156. #define    KEY_CURSUP         0x4800
  157. #define    KEY_CURSDOWN       0x5000
  158. #define    KEY_SELECTFILE     0x3900                                                                                   /* [SPACE] */
  159.  
  160. #define    RETURNERROR        1                                                                              /* Define exit codes */
  161. #define    RETURNABORT        1
  162. #define    RETURNSTART        0
  163.  
  164. #define    NUMLEVEL           99                                                          /* Define the number of possible levels */
  165. #define    NUMDIFFICULTY      5
  166. #define    NUMPLAYTYPE        3
  167.  
  168. #define    NUMOPTIONS         17                                                       /* Define the number of CONFIGFILE options */
  169.  
  170. #define    MAXIGNORE          12                                         /* Define number of WAD 'directory' identifiers per type */
  171. #define    MAXLEVELS          1
  172. #define    MAXCOLORS          2
  173. #define    MAXDEMOS           1
  174. #define    MAXSPRITES         92
  175. #define    MAXSOUNDS          2
  176. #define    MAXMUSIC           3
  177. #define    MAXGRAPHS          0
  178. #define    MAXADDSWITCHES     5
  179.  
  180. #define    NEWCOLORS          0x01                             /* Define bits for the 'NewStuff' field in the 'wadinfo' structure */
  181. #define    NEWDEMOS           0x02
  182. #define    NEWSOUNDS          0x04
  183. #define    NEWMUSIC           0x08
  184. #define    NEWSPRITES         0x10
  185. #define    NEWGRAPHS          0x20
  186.  
  187. #define    ToNumber(_Drv)    (toupper ((_Drv)) - 'A' + 1)                                   /* Convert drive name to drive number */
  188. #define    ToName(_Drv)      ((_Drv) + 'A' - 1)                                             /* Convert drive number to drive name */
  189.  
  190. struct     Mouse_s                                                                                           /* Define mouse info */
  191. {
  192.   int      OldXx;                                                                /* Coordinates stored from previous status check */
  193.   int      OldYy;
  194.   int      Xx;                                                                                                          /* 0 - 79 */
  195.   int      Yy;                                                                                                          /* 0 - 29 */
  196.   boolean  CoChange;                                        /* TRUE if coordinates have changed (mouse moved to a next character) */
  197.   boolean  Left;                                                                /* Status of the 3 mouse buttons; TRUE if pressed */
  198.   boolean  Middle;
  199.   boolean  Right;
  200.   boolean  LeftStillPressed;                                 /* TRUE if the left button was also pressed in previous status check */
  201. }          Mouse;
  202.  
  203. struct     WadDir_s                                                                             /* Define info for a WADDIR entry */
  204. {
  205.   char     Drive;                                                                         /* 1 = A, etc., 0 means: no drive given */
  206.   char     Name[_MAX_DIR];
  207.   boolean  DoSubDirs;                                                    /* TRUE if the subdirectories should be searched as well */
  208. };
  209.  
  210. struct     WadInfo_s                                         /* Define info for a WADFILE entry (also used for AUTOINCLUDE files) */
  211. {
  212.   char     Drive;                                                                         /* 1 = A, etc., 0 means: no drive given */
  213.   char     Path[_MAX_DIR];
  214.   char     OrigName[MAXFNAME + _MAX_EXT - 1];                                                /* The original name, with extension */
  215.   char     Name[MAXFNAME];                                                  /* Filled out, no extension (as this is always *.WAD) */
  216.   char     Info[MAXINFOLEN + 1];                                                           /* Info as found in a WADINFOFILE file */
  217.   char     NewStuff;                                                 /* Each bit represents an identifier type (demo, sound, etc) */
  218.   unsigned long  NewLevels1;                                                              /* Each bit represents a level: 01 - 32 */
  219.   unsigned long  NewLevels2;                                                                                           /* 33 - 64 */
  220.   unsigned long  NewLevels3;                                                                                           /* 65 - 96 */
  221.   unsigned short NewLevels4;                                                                                           /* 97 - 99 */
  222.   boolean  Selected;                                                                     /* TRUE if the WAD is selected on screen */
  223. };
  224.  
  225. struct     WadHeader_s                                                                        /* The first 12 bytes of a WAD file */
  226. {
  227.   char     Type[4];                                                          /* "PWAD" for a patch WAD, "IWAD" for an initial WAD */
  228.   long     DirSize;                                                                   /* Number of entries in the WAD 'directory' */
  229.   long     DirStart;                                       /* Pointer to the location (offset) of the 'directory' in the WAD file */
  230. };
  231.  
  232. struct     WadDirectory_s                                                                  /* A 'directory' entry in the WAD file */
  233. {
  234.   long     Start;                                                            /* Pointer to the data of this entry in the WAD file */
  235.   long     Size;                                                                                   /* Length in bytes of the data */
  236.   char     Name[ENTRYLEN];                                                                     /* Identifier (name) of this entry */
  237. };
  238.  
  239. struct     ExtCom_s                                                /* The commands that are available for the ADDSWITCHES keyword */
  240. {
  241.   char    *Command;                                                                                     /* The name of the switch */
  242.   boolean  InUse;                                                              /* TRUE if this switch was given in the CONFIGFILE */
  243. };
  244.  
  245. struct     ConfOp_s                                                                     /* The commands available in a CONFIGFILE */
  246. {
  247.   char    *OptionName;
  248.   enum
  249.   {
  250.     OPT_DOOMDIR,
  251.     OPT_WADDIR,
  252.     OPT_AUTOINC,
  253.     OPT_ADDSWIT,
  254.     OPT_PLAYTYP,
  255.     OPT_SET,
  256.     OPT_NUM,
  257.     OPT_STRING,
  258.     OPT_FILE,
  259.     OPT_SORTFIL
  260.   }        OptionType;
  261.   void far *DataPtr;
  262. };
  263.  
  264. struct     WadDir_s  far *WadDir[MAXWADDIRS];
  265. struct     WadInfo_s far *WadInfo[MAXWADS];
  266. struct     WadInfo_s far *AutoInc[MAXAUTOINCLUDE];
  267.  
  268. char       CurPath[_MAX_DIR];                                                           /* Current directory (preceded by drive:) */
  269. char       ConfigFile[_MAX_PATH];                                                                   /* Filename of the CONFIGFILE */
  270. char       InfoFile[_MAX_PATH];                                             /* Filename of WADINFOFILE as found in the CONFIGFILE */
  271. char       DoomDirectory[_MAX_PATH];                                        /* The main DOOM directory as found in the CONFIGFILE */
  272. char       IpxDriver[256];                                                     /* Different IPX driver as found in the CONFIGFILE */
  273. char       SerDriver[256];                                                     /* Different SER driver as found in the CONFIGFILE */
  274. char       S[256];                                                                                          /* All-purpose string */
  275. char       CurrentField;                                                                      /* Current FIELD type (see defines) */
  276. char       SelectionChange;                                                      /* Used in file FIELD; TRUE if selection toggled */
  277. char       CurrentPage;                                                                                /* Current file FIELD page */
  278. char       LastPage;                                                                                      /* Last file FIELD page */
  279. int        CurDrive;                                                                                             /* Current drive */
  280. int        DoomDrive;                                                                             /* Drive of main DOOM directory */
  281. int        TotalWads;                                                                          /* Total number of found WAD files */
  282. int        TotalWadDirs;                                                 /* Total number of read WADDIR entries in the CONFIGFILE */
  283. int        TotalAutoInc;                                              /* Total number of read AUTOINCLUDE entries in a CONFIGFILE */
  284. int        M;
  285. int        N;
  286. int        Dummy;                                                                               /* Used in _dos_setdrive function */
  287. boolean    OtherSerDriver;                                             /* TRUE if a different SERDRIVER was found in a CONFIGFILE */
  288. boolean    OtherIpxDriver;                                             /* TRUE if a different IPXDRIVER was found in a CONFIGFILE */
  289. boolean    UseMouse;                                                                            /* TRUE if a mouse has been found */
  290. boolean    MouseHidden;                                                        /* TRUE if the mouse pointer is temporarely hidden */
  291. boolean    Rescan           = FALSE;                                                /* TRUE if '-R' was given on the command line */
  292. boolean    ConfigChange     = FALSE;                              /* TRUE if a different CONFIGFILE was given on the command line */
  293. boolean    ScreenOpen       = FALSE;
  294. boolean    DoNotSearch      = FALSE;                                              /* TRUE if NOSEARCH was found in the CONFIGFILE */
  295. boolean    SortWadFiles     = FALSE;                                             /* TRUE if SORTFILES was found in the CONFIGFILE */
  296. boolean    SortByName       = TRUE;                                                          /* TRUE for "NAME", FALSE for "INFO" */
  297. boolean    NoFullName       = FALSE;                                            /* TRUE if NOFULLNAME was found in the CONFIGFILE */
  298. boolean    NoAutoReturn     = FALSE;                                          /* TRUE if NOAUTORETURN was found in the CONFIGFILE */
  299.  
  300. int        CurrentSelected  = 0;                                                           /* Current pointed WAD file, 0 if none */
  301. int        PreviousWad      = 0;                                                          /* Previous pointed WAD file, 0 if none */
  302. int        DifficultyActive = DEFAULTDIFFICULTY;
  303. int        PlayTypeActive   = DEFAULTPLAYTYPE;
  304. int        NumNodesActive   = DEFAULTNODES;
  305. int        NetworkSocket    = DEFAULTSOCKET;
  306. int        CommPortActive   = DEFAULTCOMPORT;
  307. int        CurrentLevel     = DEFAULTLEVEL;
  308. boolean    DeathmatchOn     = DEFAULTDMATCH;
  309. boolean    DeathmatchV2On   = DEFAULTDMATCHV2;
  310. boolean    RespMonstersOn   = DEFAULTRESPAWN;
  311. boolean    NoMonstersOn     = DEFAULTNOMONSTERS;
  312. boolean    FastMonstersOn   = DEFAULTFASTMONST;
  313.  
  314. char      *Difficulties[]   = {"I'm too young to die ", "Hey, not too rough   ", "Hurt me plenty       ", "Ultra-Violence!      ",
  315.                                "NIGHTMARE            "};
  316. char      *PlayTypes[]      = {"Alone                ", "IPX-compatible       ", "Serial link          "};
  317. char      *NumberNodes      =  "Number of players    ";
  318. char      *CommPort         =  "COM port             ";
  319. char      *Deathmatch       =  "DEATHMATCH!          ";
  320. char      *DeathmatchV2     =  "DEATHMATCH! V2.0     ";
  321. char      *NoMonsters       =  "No Monsters          ";
  322. char      *RespawnMonsters  =  "Respawn monsters     ";
  323. char      *FastMonsters     =  "Fast monsters        ";
  324. char      *Boxes[]          = {"( ) ",                  "(\x07) "};
  325. char      *PreviousPage     =  "<<<";
  326. char      *NextPage         =  ">>>";
  327. char      *PreviousLevel    =  "<<<";
  328. char      *NextLevel        =  ">>>";
  329. char      *StartGame        =  "( START DOOM2 )";
  330. char      *Automatic        =  "( AUTO SELECT )";
  331. char      *ReadPrevious     =  "(READ PREVIOUS)";
  332. char      *NoLevel          =  "-         ";
  333.  
  334. char      *IdLevelNames[]   = {"     Entryway      ", "    Underhalls     ", "    The Gantlet    ", "     The Focus     ",
  335.                                " The Waste Tunnels ", "    The Crusher    ", "    Dead Simple    ", " Tricks and Traps  ",
  336.                                "      The Pit      ", "  Refueling Base   ", "  Circle of Death  ", "    The Factory    ",
  337.                                "     Downtown      ", "  The Inmost Dens  ", "  Industrial Zone  ", "      Suburbs      ",
  338.                                "     Tenements     ", "   The Courtyard   ", "    The Citadel    ", "      Gotcha!      ",
  339.                                "      Nirvana      ", "   The Catacombs   ", "   Barrels o'Fun   ", "     The Chasm     ",
  340.                                "    Bloodfalls     ", "The Abandoned Mines", "   Monster Condo   ", " The Spirit World  ",
  341.                                "  The Living End   ", "    Icon of Sin    ", "    Wolfenstein    ", "      Grosse       "};
  342.  
  343. char      *IdLevels[]       = {"MAP", "M"};
  344. char      *IdIgnore[]       = {"THINGS",   "LINEDEFS", "SIDEDEFS", "VERTEXES", "SEGS", "SSECTORS", "NODES", "SECTORS", "REJECT",
  345.                                "BLOCKMAP", "INFOPACK", "PLATFORM"};                           /* Last 2 are NOT from Id Software! */
  346. char      *IdColors[]       = {"PLAYPAL", "COLORMAP", "c", "palette"};
  347. char      *IdDemos[]        = {"DEMO", "d", "demos"};
  348. char      *IdSprites[]      = {"SARG", "TROO", "BOSS", "PLAY", "POSS", "SPOS", "SKUL", "HEAD", "CYBR", "SPID", "CHG",  "SAW",
  349.                                "PIS",  "PBU",  "PSH",  "BAL",  "PUF",  "BLU",  "MIS",  "TFO",  "PUN",  "SHT",  "PLS",  "BFG",
  350.                                "BFS",  "BFE",  "POL",  "CAND", "CBRA", "SHOT", "MGUN", "LAUN", "CSAW", "CLIP", "SHEL", "ROCK",
  351.                                "STIM", "MEDI", "ARM",  "BAR",  "BPAK", "BROK", "AMMO", "SBOX", "ELEC", "BKEY", "YKEY", "RKEY",
  352.                                "SUIT", "PVIS", "BEXP", "PMAP", "PIN",  "BON",  "SOUL", "COL",  "FSKU", "CEYE", "TRE",  "SMI",
  353.                                "BSKU", "RSKU", "YSKU", "PLAS", "BFUG", "CELL", "PSTR", "CELP", "GOR",  "TGRN", "TBLU", "SMRT",
  354.                                "SMBT", "SMGT", "BOS2", "BSPI", "CPOS", "FATB", "FATT", "FBXP", "FIRE", "HDB",  "KEEN", "MEGA",
  355.                                "PAIN", "SGN2", "SHT2", "SKEL", "SSWV", "TLMP", "TLP2", "VILE", "p", "sprites"};
  356. char      *IdSounds[]       = {"DS", "DP", "s", "sounds"};
  357. char      *IdMusic[]        = {"D_", "GENMIDI", "DMXGUSC", "m", "music"};
  358. char      *IdGraphics[]     = {"g", "graphics"};
  359.  
  360. struct    ConfOp_s ConfOp[] = {{"DOOMDIR",     OPT_DOOMDIR,  NOMEM},
  361.                                {"WADDIR",      OPT_WADDIR,   NOMEM},
  362.                                {"WADINFOFILE", OPT_FILE,     InfoFile},
  363.                                {"SETSKILL",    OPT_NUM,     &DifficultyActive},
  364.                                {"DEATHMATCH",  OPT_SET,     &DeathmatchOn},
  365.                                {"AUTOINCLUDE", OPT_AUTOINC,  NOMEM},
  366.                                {"NOSEARCH",    OPT_SET,     &DoNotSearch},
  367.                                {"SETCOMPORT",  OPT_NUM,     &CommPortActive},
  368.                                {"SETNODES",    OPT_NUM,     &NumNodesActive},
  369.                                {"SETPLAYTYPE", OPT_PLAYTYP,  NOMEM},
  370.                                {"ADDSWITCHES", OPT_ADDSWIT,  NOMEM},
  371.                                {"SORTFILES",   OPT_SORTFIL,  NOMEM},
  372.                                {"SETSOCKET",   OPT_NUM,     &NetworkSocket},
  373.                                {"IPXDRIVER",   OPT_STRING,   IpxDriver},
  374.                                {"SERDRIVER",   OPT_STRING,   SerDriver},
  375.                                {"NOFULLNAME",  OPT_SET,     &NoFullName},
  376.                                {"NOAUTORETURN",OPT_SET,     &NoAutoReturn}};
  377.  
  378. struct    ExtCom_s ExtCom[] = {{"-NOJOY",   FALSE},
  379.                                {"-NOMOUSE", FALSE},
  380.                                {"-NOMUSIC", FALSE},
  381.                                {"-NOSFX",   FALSE},
  382.                                {"-NOSOUND", FALSE}};
  383.  
  384. int ResetMouse (void)
  385.  
  386. /**********************************************************************************************************************************/
  387. /* Pre   : None.                                                                                                                  */
  388. /* Post  : The mouse driver has been reset. If no mouse was found, then 0 is returned. Anything else means success.               */
  389. /* Import: None.                                                                                                                  */
  390. /**********************************************************************************************************************************/
  391.  
  392. {
  393.   union REGS Regs;
  394.  
  395.   Regs.x.ax = 0x0000;
  396.   int86 (0x33, &Regs, &Regs);
  397.   return (Regs.x.ax);
  398. }
  399.  
  400. void ShowMouse (void)
  401.  
  402. /**********************************************************************************************************************************/
  403. /* Pre   : (global) 'UseMouse' is TRUE if a mouse has been detected.                                                              */
  404. /* Post  : The mouse pointer is made visable.                                                                                     */
  405. /* Import: None.                                                                                                                  */
  406. /**********************************************************************************************************************************/
  407.  
  408. {
  409.   union REGS Regs;
  410.  
  411.   if (UseMouse)
  412.   {
  413.     Regs.x.ax = 0x0001;
  414.     int86 (0x33, &Regs, &Regs);
  415.   }
  416. }
  417.  
  418. void HideMouse (void)
  419.  
  420. /**********************************************************************************************************************************/
  421. /* Pre   : (global) 'UseMouse' is TRUE if a mouse has been detected.                                                              */
  422. /* Post  : The mouse pointer is made invisable.                                                                                   */
  423. /* Import: None.                                                                                                                  */
  424. /**********************************************************************************************************************************/
  425.  
  426. {
  427.   union REGS Regs;
  428.  
  429.   if (UseMouse)
  430.   {
  431.     Regs.x.ax = 0x0002;
  432.     int86 (0x33, &Regs, &Regs);
  433.   }
  434. }
  435.  
  436. void MouseStatus (void)
  437.  
  438. /**********************************************************************************************************************************/
  439. /* Pre   : None.                                                                                                                  */
  440. /* Post  : The mouse driver has been read, which returns the status of the buttons and the x and y coordinates of the mouse.      */
  441. /*         All this information is stored in the 'Mouse' structure.                                                               */
  442. /* Import: None.                                                                                                                  */
  443. /**********************************************************************************************************************************/
  444.  
  445. {
  446.   union REGS Regs;
  447.  
  448.   Mouse.LeftStillPressed = Mouse.Left;
  449.   Regs.x.ax      = 0x0003;
  450.   int86 (0x33, &Regs, &Regs);
  451.   Mouse.Left     = (Regs.x.bx & 0x0001);                                                 /* Store the status of the mouse buttons */
  452.   Mouse.Right    = (Regs.x.bx & 0x0002);
  453.   Mouse.Middle   = (Regs.x.bx & 0x0004);
  454.   Mouse.Xx       =  Regs.x.cx / 0x0008;                                     /* Convert pixel coordinates to character coordinates */
  455.   Mouse.Yy       =  Regs.x.dx / 0x0010;
  456.   Mouse.CoChange = (Mouse.OldXx != Mouse.Xx || Mouse.OldYy != Mouse.Yy);
  457.   if (!Mouse.Left)
  458.     Mouse.LeftStillPressed = FALSE;
  459. }
  460.  
  461. void DeAllocateAll (void)
  462.  
  463. /**********************************************************************************************************************************/
  464. /* Pre   : None.                                                                                                                  */
  465. /* Post  : If any WadDirs were initialized yet, then the memory is deallocated completely.                                        */
  466. /*         If any WadInfos were initialized yet, then the memory is deallocated completely.                                       */
  467. /*         If any AutoIncs were initialized yet, then the memory is deallocated completely.                                       */
  468. /* Import: None.                                                                                                                  */
  469. /**********************************************************************************************************************************/
  470.  
  471. {
  472.   fcloseall ();
  473.   flushall ();
  474.   while (-- TotalWadDirs >= 0)
  475.     _ffree (WadDir[TotalWadDirs]);
  476.   while (-- TotalWads >= 0)
  477.     _ffree (WadInfo[TotalWads]);
  478.   while (-- TotalAutoInc >= 0)
  479.     _ffree (AutoInc[TotalAutoInc]);
  480. }
  481.  
  482. void Bye (int ReturnType, char *Message, ...)
  483.  
  484. /**********************************************************************************************************************************/
  485. /* Pre   : 'ReturnType' holds the exit code, 'Message' holds the error message.                                                   */
  486. /* Post  : The error message has been printed, all memory is freed and the program has been aborted.                              */
  487. /* Import: DeAllocateAll.                                                                                                         */
  488. /**********************************************************************************************************************************/
  489.  
  490. {
  491.   va_list Args;
  492.  
  493.   if (ScreenOpen)                                                                                     /* Still in graphics mode ? */
  494.     _setvideomode (_DEFAULTMODE);                                                                        /* Then close the screen */
  495.   va_start (Args, Message);
  496.   vfprintf (stderr, Message, Args);                                                        /* Print the (formatted) error message */
  497.   va_end (Args);
  498.   _dos_setdrive (CurDrive, &Dummy);                                                         /* Return to home drive and directory */
  499.   chdir (CurPath);
  500.   DeAllocateAll ();
  501.   exit (ReturnType);
  502. }
  503.  
  504. void PrText (short UseBox, short Y0, short X0, unsigned char Color, char *Msg, ...)
  505.  
  506. /**********************************************************************************************************************************/
  507. /* Pre   : 'Y0' and 'X0' hold the coordinates, 'Color' holds the (VGA) color and 'Msg' holds the message to print.                */
  508. /*         UseBox < 0: no selection box is printed first;                                                                         */
  509. /*         UseBox = 0: an empty selection box is printed first;                                                                   */
  510. /*         UseBox > 0: a filled selection box is printed first;                                                                   */
  511. /*         If Y0 is 0, then the coordinates are not used; the text is written directly after the previous.                        */
  512. /* Post  : If the coordinates were non-zero, then the message is printed at these coordinates in the given color. The coordinates */
  513. /*         are first converted to the pixel coordinates according to the used font.                                               */
  514. /* Import: None.                                                                                                                  */
  515. /**********************************************************************************************************************************/
  516.  
  517. {
  518.   char     Message[80];
  519.   va_list  Args;
  520.  
  521.   va_start (Args, Msg);
  522.   vsprintf (Message, Msg, Args);                                                           /* Convert the message into one string */
  523.   va_end (Args);
  524.   if (Y0 != 0)
  525.     _settextposition (Y0, X0);
  526.   _settextcolor (Color);
  527.   if (UseBox == 0)
  528.     _outtext (Boxes[0]);
  529.   if (UseBox > 0)
  530.     _outtext (Boxes[1]);
  531.   _outtext (Message);
  532. }
  533.  
  534. void InitVideo (void)
  535.  
  536. /**********************************************************************************************************************************/
  537. /* Pre   : None.                                                                                                                  */
  538. /* Post  : A VGA display of 640 x 480 x 16 colors has been opened and the screen has been cleared.                                */
  539. /* Import: Bye.                                                                                                                   */
  540. /**********************************************************************************************************************************/
  541.  
  542. {
  543.   if (!_setvideomode (_VRES16COLOR))
  544.     Bye (RETURNERROR, "ERROR - You need a VGA videocard for this utility\n");
  545.   _clearscreen (_GCLEARSCREEN);
  546. }
  547.  
  548. char *TestName (char *OldName)
  549.  
  550. /**********************************************************************************************************************************/
  551. /* Pre   : 'OldName' holds the name to be tested.                                                                                 */
  552. /* Post  : The name is tested. The return value is a string of the (adapted) input with the following specifications:             */
  553. /*         - Each part (subdirname) has at most 8 characters, possibly followed by a '.' and at most 3 characters.                */
  554. /*         - Each of the characters is valid to the DOS system.                                                                   */
  555. /*         - Trailing backslashes have been cut, except when it was the only path character.                                      */
  556. /* Import: Bye.                                                                                                                   */
  557. /**********************************************************************************************************************************/
  558.  
  559. {
  560.   char    Level[_MAX_PATH];
  561.   char    NewName[_MAX_PATH];
  562.   char    Part[MAXFNAME];
  563.   int     LvlCnt;
  564.   int     Ex;
  565.   int     PathLenOldName;
  566.   int     CharCnt;
  567.   int     PartChar;
  568.   boolean PointDone;
  569.   boolean NError          = FALSE;
  570.   boolean DriveFound      = FALSE;
  571.   boolean Ready           = FALSE;
  572.  
  573.   PathLenOldName = strlen (OldName);
  574.   CharCnt = -1;
  575.   NewName[0] = '\0';
  576.   while (!Ready && !NError)
  577.   {
  578.     LvlCnt = 0;
  579.     while ((++ CharCnt < PathLenOldName) && OldName[CharCnt] != '\\' && OldName[CharCnt] != ':')
  580.       Level[LvlCnt ++] = OldName[CharCnt];
  581.     Level[LvlCnt] = '\0';
  582.     if (OldName[CharCnt] == ':')                                                                         /* Preceded by drive: ?  */
  583.       if (DriveFound)                                                                                   /* Already a drive found! */
  584.         NError = TRUE;
  585.       else
  586.       {
  587.         DriveFound = TRUE;
  588.         strncpy (NewName, OldName, CharCnt + 1);
  589.         NewName[CharCnt + 1] = '\0';
  590.         if (CharCnt != 1 || toupper (OldName[0]) < 'A' || toupper (OldName[0]) > 'Z')                      /* Test drive validity */
  591.           Bye (RETURNERROR, "\nERROR - Invalid drivename %s\n", NewName);
  592.         if (CharCnt == PathLenOldName - 1)
  593.           Ready = TRUE;                                                                               /* Only a driveletter given */
  594.         else
  595.           if (CharCnt == PathLenOldName - 2 && OldName[CharCnt + 1] == '\\')
  596.           {
  597.             Ready = TRUE;                                                                 /* Exceptional case: only drive:\ given */
  598.             strcpy (NewName, OldName);
  599.           }
  600.       }
  601.     else
  602.     {
  603.       if (CharCnt == PathLenOldName)                                                                      /* Handling last part ? */
  604.         Ready = TRUE;
  605.       if (CharCnt == PathLenOldName - 1 && OldName[CharCnt] == '\\')
  606.         Ready = TRUE;                                                                                     /* Ended with backslash */
  607.       PointDone = FALSE;
  608.       for (PartChar = 0 ; PartChar < strlen (Level) ; PartChar ++)
  609.         switch (Level[PartChar])
  610.         {
  611.           case '.'     : if (!PointDone)
  612.                          {
  613.                            strncpy (Part, Level, PartChar < 8 ? PartChar : 8);
  614.                            Part[PartChar < 8 ? PartChar : 8] = '\0';                                         /* Cut >8 characters */
  615.                            strcat (NewName, Part);
  616.                            strcat (NewName, ".");                                                                  /* Add the '.' */
  617.                            Ex = PartChar + 1;
  618.                            PointDone = TRUE;
  619.                          }
  620.                          else
  621.                            if (strcmp (Level, ".."))                                                          /* Exceptional case */
  622.                              NError = TRUE;
  623.                          break;
  624.           case ';'     :
  625.           case ','     :
  626.           case '\''    :
  627.           case '/'     :
  628.           case '('     :
  629.           case ')'     :
  630.           case '['     :
  631.           case ']'     :                                      /* Characters '>', '<', '|' '"' and '\' have already been taken out */
  632.           case '='     : NError = TRUE;                                                                     /* All bad characters */
  633.                          break;
  634.         }
  635.       if (!PointDone)                                                                       /* Finish filenames without extension */
  636.       {
  637.         strncpy (Part, Level, PartChar < 8 ? PartChar : 8);
  638.         Part[PartChar < 8 ? PartChar : 8] = '\0';
  639.         strcat (NewName, Part);
  640.         PointDone = TRUE;
  641.       }
  642.       else                                                                       /* This also deals with the second point in '..' */
  643.       {
  644.         strncpy (Part, Level + Ex, PartChar - Ex < 3 ? PartChar - Ex : 3);                                   /* Cut >3 characters */
  645.         Part[PartChar - Ex < 3 ? PartChar - Ex : 3] = '\0';
  646.         strcat (NewName, Part);
  647.       }
  648.       if (!Ready)
  649.         strcat (NewName, "\\");                                                                       /* Add the subdir character */
  650.     }
  651.   }
  652.   if (NError)                                                                                            /* Report bad characters */
  653.     Bye (RETURNERROR, "\nERROR - Invalid name %s\n", OldName);
  654.   if (!strlen (NewName) && OldName[0] == '\\')                                                              /* Input was root dir */
  655.     return ("\\");                                                                         /* Which should be treated differently */
  656.   else
  657.     return (NewName);                                                                                 /* Return (modified) string */
  658. }
  659.  
  660. void _fastcall GetWadInfo (int WadNumber, boolean GoThere)
  661.  
  662. /**********************************************************************************************************************************/
  663. /* Pre   : 'WadNumber' holds the WAD file number in memory that should be checked.                                                */
  664. /*         'GoThere' is TRUE if the routine should first go to the required drive and directory.                                  */
  665. /* Post  : The WAD directory of the file has been found and read out. The structure 'wadinfo' has been updated.                   */
  666. /* Import: Bye.                                                                                                                   */
  667. /**********************************************************************************************************************************/
  668.  
  669. {
  670.   FILE                  *Fp;
  671.   struct WadDirectory_s  WadDirectory;
  672.   struct WadHeader_s     WadHeader;
  673.   char                   Identifier[9];
  674.   char                   DrivePath[_MAX_DIR];
  675.   int  register          O;
  676.   int  register          P;
  677.   long register          Entries;
  678.   boolean                More;
  679.  
  680.   if (GoThere)                                                                                 /* Jump to the directory if needed */
  681.   {
  682.     _dos_setdrive (WadInfo[WadNumber]->Drive, &Dummy);
  683.     free (getcwd (DrivePath, _MAX_DIR - 1));                                                         /* Collect current directory */
  684.     chdir (WadInfo[WadNumber]->Path);
  685.   }
  686.   if (!(Fp = fopen (WadInfo[WadNumber]->OrigName, "rb")))
  687.     Bye (RETURNERROR, "\nERROR - Error opening file %s\n", WadInfo[WadNumber]->OrigName);
  688.   if (fread (&WadHeader, 1, sizeof (struct WadHeader_s), Fp) != sizeof (struct WadHeader_s))               /* Read the WAD header */
  689.     Bye (RETURNERROR, "\nERROR - Error reading file %s\n", WadInfo[WadNumber]->OrigName);
  690.   if (strnicmp (WadHeader.Type, "PWAD", 4) && strnicmp (WadHeader.Type, "IWAD", 4))                         /* Is it a WAD file ? */
  691.     Bye (RETURNERROR, "\nERROR - File %s is not a WAD file\n", WadInfo[WadNumber]->OrigName);
  692.   if (fseek (Fp, WadHeader.DirStart, SEEK_SET))                                     /* Go to the WAD 'directory' part of the file */
  693.     Bye (RETURNERROR, "\nERROR - Error reading file %s\n", WadInfo[WadNumber]->OrigName);
  694.   WadInfo[WadNumber]->NewStuff = 0x00;                                                                       /* Clear all entries */
  695.   WadInfo[WadNumber]->NewLevels1 = 0x00000000;
  696.   WadInfo[WadNumber]->NewLevels2 = 0x00000000;
  697.   WadInfo[WadNumber]->NewLevels3 = 0x00000000;
  698.   WadInfo[WadNumber]->NewLevels4 = 0x00;
  699.   Entries = -1;                                                                              /* Count all WAD 'directory' entries */
  700.   while (++ Entries < WadHeader.DirSize)                                     /* The number of entries was found in the WAD header */
  701.   {
  702.     if (fread (&WadDirectory, 1, sizeof (struct WadDirectory_s), Fp) != sizeof (struct WadDirectory_s))          /* Read an entry */
  703.       Bye (RETURNERROR, "\nERROR - Error reading file %s\n", WadInfo[WadNumber]->OrigName);
  704.     for (O = 0 ; O < ENTRYLEN ; O ++)                                                       /* Fill the identifier to 8 positions */
  705.       Identifier[O] = WadDirectory.Name[O];
  706.     Identifier[ENTRYLEN] = '\0';                                                                          /* And make it a string */
  707.     More = TRUE;                                                            /* Now test it against all types and signal successes */
  708.     for (O = 0 ; O < MAXIGNORE && More ; O ++)
  709.       if (!strnicmp (Identifier, IdIgnore[O], strlen (IdIgnore[O])))
  710.         More = FALSE;
  711.     if (More)
  712.       for (O = 0 ; O < MAXCOLORS && More ; O ++)
  713.         if (!strnicmp (Identifier, IdColors[O], strlen (IdColors[O])))
  714.         {
  715.           More = FALSE;
  716.           WadInfo[WadNumber]->NewStuff |= NEWCOLORS;
  717.         }
  718.     if (More)
  719.       for (O = 0 ; O < MAXDEMOS && More ; O ++)
  720.         if (!strnicmp (Identifier, IdDemos[O], strlen (IdDemos[O])))
  721.         {
  722.           More = FALSE;
  723.           WadInfo[WadNumber]->NewStuff |= NEWDEMOS;
  724.         }
  725.     if (More)
  726.       for (O = 0 ; O < MAXLEVELS && More ; O ++)
  727.         if (!strnicmp (Identifier, IdLevels[O], strlen (IdLevels[O])))
  728.         {
  729.           More = FALSE;
  730.           P = atoi (Identifier + strlen (IdLevels[O]));
  731.           if (P <= 32)
  732.             WadInfo[WadNumber]->NewLevels1 |= ((unsigned long)1 << (P - 1));
  733.           else
  734.             if (P <= 64)
  735.               WadInfo[WadNumber]->NewLevels2 |= ((unsigned long)1 << (P - 33));
  736.             else
  737.               if (P <= 96)
  738.                 WadInfo[WadNumber]->NewLevels3 |= ((unsigned long)1 << (P - 65));
  739.               else
  740.                 WadInfo[WadNumber]->NewLevels4 |= ((unsigned short)1 << (P - 97));
  741.         }
  742.     if (More)
  743.       for (O = 0 ; O < MAXSPRITES && More ; O ++)
  744.         if (!strnicmp (Identifier, IdSprites[O], strlen (IdSprites[O])))
  745.         {
  746.           More = FALSE;
  747.           WadInfo[WadNumber]->NewStuff |= NEWSPRITES;
  748.         }
  749.     if (More)
  750.       for (O = 0 ; O < MAXSOUNDS && More ; O ++)
  751.         if (!strnicmp (Identifier, IdSounds[O], strlen (IdSounds[O])))
  752.         {
  753.           More = FALSE;
  754.           WadInfo[WadNumber]->NewStuff |= NEWSOUNDS;
  755.         }
  756.     if (More)
  757.       for (O = 0 ; O < MAXMUSIC && More ; O ++)
  758.         if (!strnicmp (Identifier, IdMusic[O], strlen (IdMusic[O])))
  759.         {
  760.           More = FALSE;
  761.           WadInfo[WadNumber]->NewStuff |= NEWMUSIC;
  762.         }
  763.     if (More && WadDirectory.Start != 0x00000000 && Identifier[0] != '\0')       /* All other identifiers are counted as graphics */
  764.       WadInfo[WadNumber]->NewStuff |= NEWGRAPHS;
  765.   }
  766.   fclose (Fp);
  767.   if (GoThere)
  768.   {
  769.     chdir (DrivePath);
  770.     _dos_setdrive (CurDrive, &Dummy);                                                                  /* Return to home location */
  771.   }
  772. }
  773.  
  774. void _fastcall WriteWadInfo (char *FileName)
  775.  
  776. /**********************************************************************************************************************************/
  777. /* Pre   : 'FileName' holds the name of the file (which is the same as set in 'InfoFile').                                        */
  778. /* Post  : All found WAD files are considered. All files have their fields 'NewStuff' and 'NewLevels' initialized. This info is   */
  779. /*         converted into readable text and written to the file, together with the path information of the WAD file.              */
  780. /* Import: Bye.                                                                                                                   */
  781. /**********************************************************************************************************************************/
  782.  
  783. {
  784.   FILE          *Fp;
  785.   char register  Memory;
  786.   char register  P;
  787.   char register  Q;
  788.   char           T[81];
  789.   int  register  WadNumber;
  790.   boolean        First;
  791.   boolean        New;
  792.   boolean        More;
  793.  
  794.   if (!(Fp = fopen (FileName, "w")))
  795.     Bye (RETURNERROR, "\nERROR - Cannot write WAD info file\n");
  796.   for (WadNumber = 0 ; WadNumber < TotalWads ; WadNumber ++)
  797.   {
  798.     More = FALSE;
  799.     S[0] = '\0';
  800.     fprintf (Fp, "%d %s %s ", WadInfo[WadNumber]->Drive, WadInfo[WadNumber]->Path, WadInfo[WadNumber]->OrigName);
  801.     More = (WadInfo[WadNumber]->NewLevels1 != 0x00000000 ||
  802.             WadInfo[WadNumber]->NewLevels2 != 0x00000000 ||
  803.             WadInfo[WadNumber]->NewLevels3 != 0x00000000 ||
  804.             WadInfo[WadNumber]->NewLevels4 != 0x00);                                 /* Are their any patch levels in this file ? */
  805.     if (More)                                                                                                      /* Skip if not */
  806.     {
  807.       Memory = -1;
  808.       First = TRUE;
  809.       for (Q = 0 ; Q < 32 ; Q ++)                                                                        /* Handle all level-bits */
  810.         if (WadInfo[WadNumber]->NewLevels1 & ((unsigned long)1 << Q))
  811.         {
  812.           if (Memory == -1)
  813.             if (First)
  814.             {
  815.               sprintf (T, "%s%02d", IdLevels[MAXLEVELS], (int)Q + 1);
  816.               strcat (S, T);
  817.               First = FALSE;
  818.               Memory = Q + 1;
  819.               New = FALSE;
  820.             }
  821.             else
  822.             {
  823.               sprintf (T, ",%02d", (int)Q + 1);
  824.               strcat (S, T);
  825.               Memory = Q + 1;
  826.               New = FALSE;
  827.             }
  828.           else
  829.           {
  830.             Memory ++;
  831.             New = TRUE;
  832.           }
  833.         }
  834.         else
  835.         {
  836.           if (Memory > 0 && New)
  837.           {
  838.             sprintf (T, "-%02d", (int)Memory);
  839.             strcat (S, T);
  840.           }
  841.           Memory = -1;
  842.         }
  843.       for (Q = 0 ; Q < 32 ; Q ++)
  844.         if (WadInfo[WadNumber]->NewLevels2 & ((unsigned long)1 << Q))
  845.         {
  846.           if (Memory == -1)
  847.             if (First)
  848.             {
  849.               sprintf (T, "%s%02d", IdLevels[MAXLEVELS], (int)Q + 33);
  850.               strcat (S, T);
  851.               First = FALSE;
  852.               Memory = Q + 33;
  853.               New = FALSE;
  854.             }
  855.             else
  856.             {
  857.               sprintf (T, ",%02d", (int)Q + 33);
  858.               strcat (S, T);
  859.               Memory = Q + 33;
  860.               New = FALSE;
  861.             }
  862.           else
  863.           {
  864.             Memory ++;
  865.             New = TRUE;
  866.           }
  867.         }
  868.         else
  869.         {
  870.           if (Memory > 0 && New)
  871.           {
  872.             sprintf (T, "-%02d", (int)Memory);
  873.             strcat (S, T);
  874.           }
  875.           Memory = -1;
  876.         }
  877.       for (Q = 0 ; Q < 32 ; Q ++)
  878.         if (WadInfo[WadNumber]->NewLevels3 & ((unsigned long)1 << Q))
  879.         {
  880.           if (Memory == -1)
  881.             if (First)
  882.             {
  883.               sprintf (T, "%s%02d", IdLevels[MAXLEVELS], (int)Q + 65);
  884.               strcat (S, T);
  885.               First = FALSE;
  886.               Memory = Q + 65;
  887.               New = FALSE;
  888.             }
  889.             else
  890.             {
  891.               sprintf (T, ",%02d", (int)Q + 65);
  892.               strcat (S, T);
  893.               Memory = Q + 65;
  894.               New = FALSE;
  895.             }
  896.           else
  897.           {
  898.             Memory ++;
  899.             New = TRUE;
  900.           }
  901.         }
  902.         else
  903.         {
  904.           if (Memory > 0 && New)
  905.           {
  906.             sprintf (T, "-%02d", (int)Memory);
  907.             strcat (S, T);
  908.           }
  909.           Memory = -1;
  910.         }
  911.       for (Q = 0 ; Q < 3 ; Q ++)
  912.         if (WadInfo[WadNumber]->NewLevels4 & ((unsigned short)1 << Q))
  913.         {
  914.           if (Memory == -1)
  915.             if (First)
  916.             {
  917.               sprintf (T, "%s%02d", IdLevels[NUMLEVEL], (int)Q + 97);
  918.               strcat (S, T);
  919.               First = FALSE;
  920.               Memory = Q + 97;
  921.               New = FALSE;
  922.             }
  923.             else
  924.             {
  925.               sprintf (T, ",%02d", (int)Q + 97);
  926.               strcat (S, T);
  927.               Memory = Q + 97;
  928.               New = FALSE;
  929.             }
  930.           else
  931.           {
  932.             Memory ++;
  933.             New = TRUE;
  934.           }
  935.         }
  936.         else
  937.         {
  938.           if (Memory > 0 && New)
  939.           {
  940.             sprintf (T, "-%02d", (int)Memory);
  941.             strcat (S, T);
  942.           }
  943.           Memory = -1;
  944.         }
  945.       if (Memory > 0 && New)
  946.       {
  947.         sprintf (T, "-%02d", (int)Memory);
  948.         strcat (S, T);
  949.       }
  950.       sprintf (T, "%-10s", S);
  951.       strcpy (S, T);
  952.     }                                                                                           /* No new levels in this WAD file */
  953.     else                                                                 /* If only one type was found (for example, only sounds) */
  954.       if (!(WadInfo[WadNumber]->NewStuff ^ NEWCOLORS))                   /* Then use the complete word ('sounds' in this example) */
  955.         sprintf (S, "%s", IdColors[MAXCOLORS + 1]);
  956.       else
  957.         if (!(WadInfo[WadNumber]->NewStuff ^ NEWDEMOS))
  958.           sprintf (S, "%s", IdDemos[MAXDEMOS + 1]);
  959.         else
  960.           if (!(WadInfo[WadNumber]->NewStuff ^ NEWSOUNDS))
  961.             sprintf (S, "%s", IdSounds[MAXSOUNDS + 1]);
  962.           else
  963.             if (!(WadInfo[WadNumber]->NewStuff ^ NEWMUSIC))
  964.               sprintf (S, "%s", IdMusic[MAXMUSIC + 1]);
  965.             else
  966.               if (!(WadInfo[WadNumber]->NewStuff ^ NEWSPRITES))
  967.                 sprintf (S, "%s", IdSprites[MAXSPRITES + 1]);
  968.               else
  969.                 if (!(WadInfo[WadNumber]->NewStuff ^ NEWGRAPHS))
  970.                   sprintf (S, "%s", IdGraphics[MAXGRAPHS + 1]);
  971.                 else
  972.                 {
  973.                   More = TRUE;                                                                       /* More than one type found */
  974.                   strcpy (S, NoLevel);                                                                /* Clear level string-part */
  975.                 }
  976.     if (More)                                                                               /* Levels found or more types than 1 */
  977.     {
  978.       if (WadInfo[WadNumber]->NewStuff & NEWCOLORS)                                  /* Print one character to indicate the type */
  979.         strcat (S, IdColors[MAXCOLORS]);
  980.       if (WadInfo[WadNumber]->NewStuff & NEWDEMOS)
  981.         strcat (S, IdDemos[MAXDEMOS]);
  982.       if (WadInfo[WadNumber]->NewStuff & NEWSOUNDS)
  983.         strcat (S, IdSounds[MAXSOUNDS]);
  984.       if (WadInfo[WadNumber]->NewStuff & NEWMUSIC)
  985.         strcat (S, IdMusic[MAXMUSIC]);
  986.       if (WadInfo[WadNumber]->NewStuff & NEWSPRITES)
  987.         strcat (S, IdSprites[MAXSPRITES]);
  988.       if (WadInfo[WadNumber]->NewStuff & NEWGRAPHS)
  989.         strcat (S, IdGraphics[MAXGRAPHS]);
  990.     }
  991.     sprintf (WadInfo[WadNumber]->Info, "%-16s", S);                                                                 /* MAXINFOLEN */
  992.     fprintf (Fp, "%-16s\n", S);
  993.   }
  994.   fclose (Fp);
  995. }
  996.  
  997. int _fastcall NextString (FILE *Fp, char *String)
  998.  
  999. /**********************************************************************************************************************************/
  1000. /* Pre   : 'Fp' points to the open CONFIGFILE, 'String' holds the address of the string to be read.                               */
  1001. /* Post  : Any string is read. If it started with a '#' (COMMENT character), then the rest of the line has been ignored. The      */
  1002. /*         function does not return before a string was read WITHOUT this character or EOF has been reached. The result of the    */
  1003. /*         function is the length of the read string, or 0 if EOF was encountered.                                                */
  1004. /* Import: Bye.                                                                                                                   */
  1005. /**********************************************************************************************************************************/
  1006.  
  1007. {
  1008.   char    Ch;
  1009.   char    Cnt;
  1010.   boolean Ready = FALSE;
  1011.   boolean SkipSpaces;
  1012.  
  1013.   String[0] = '\0';
  1014.   while (!Ready)                                                                            /* Read until a valid string is found */
  1015.   {
  1016.     SkipSpaces = TRUE;
  1017.     while (SkipSpaces)
  1018.     {
  1019.       fscanf (Fp, "%c", &Ch);                                                                 /* Read until no white-spaces found */
  1020.       if (feof (Fp))
  1021.       {
  1022.         String[0] = '\0';                                                                                         /* Or until EOF */
  1023.         return (0);
  1024.       }
  1025.       SkipSpaces = isspace (Ch);
  1026.     }
  1027.     if (Ch == COMMENT)                                                              /* First character is the COMMENT character ? */
  1028.       do
  1029.       {
  1030.         fscanf (Fp, "%c", &Ch);                                                                   /* Ignore until end of the line */
  1031.         if (feof (Fp))
  1032.         {
  1033.           String[0] = '\0';                                                                                       /* Or until EOF */
  1034.           return (0);
  1035.         }
  1036.       }
  1037.       while (Ch != '\n');
  1038.     else
  1039.       Ready = TRUE;
  1040.   }
  1041.   Cnt = 0;
  1042.   Ready = FALSE;
  1043.   while (!Ready)
  1044.   {
  1045.     while (!isspace (Ch) && Ch != '"')                                                             /* Trap quoted argument(part)s */
  1046.     {
  1047.       String[Cnt ++] = Ch;
  1048.       fscanf (Fp, "%c", &Ch);                                                                     /* Read until first white-space */
  1049.       if (feof (Fp))
  1050.       {
  1051.         String[Cnt] = '\0';                                                                                       /* Or until EOF */
  1052.         return (Cnt);
  1053.       }
  1054.     }
  1055.     if (Ch == '"')                                                                                          /* Handle quoted part */
  1056.     {
  1057.       do
  1058.       {
  1059.         fscanf (Fp, "%c", &Ch);
  1060.         if (feof (Fp))
  1061.           Bye (RETURNERROR, "ERROR - Unexpected end of configuration file\n");
  1062.         if (Ch == '\n')
  1063.           Bye (RETURNERROR, "ERROR - Unexpected end of line in configuration file\n");
  1064.         String[Cnt ++] = Ch;
  1065.       }
  1066.       while (Ch != '"');
  1067.       Cnt --;
  1068.       fscanf (Fp, "%c", &Ch);                                                                                 /* Read first after */
  1069.       if (feof (Fp))
  1070.       {
  1071.         String[Cnt] = '\0';                                                                                       /* Or until EOF */
  1072.         return (Cnt);
  1073.       }
  1074.     }
  1075.     else
  1076.       Ready = TRUE;
  1077.   }
  1078.   String[Cnt] = '\0';
  1079.   return (Cnt);
  1080. }
  1081.  
  1082. void _fastcall HandleOptWaddir (char *Item)
  1083.  
  1084. /**********************************************************************************************************************************/
  1085. /* Pre   : 'Item' holds an operand that has been read following an 'WADDIR' option.                                               */
  1086. /* Post  : If 'Item' holds '/S' (or '-S'), then the previously declared WadDir is flagged 'DoSubDirs'. Otherwise, 'Item' holds a  */
  1087. /*         filename, that is included in the WadDir list. If any error occurs, then no return is made.                            */
  1088. /* Import: TestName, Bye.                                                                                                         */
  1089. /**********************************************************************************************************************************/
  1090.  
  1091. {
  1092.   int register DriveNo;
  1093.  
  1094.   if (!stricmp (Item, DOSUB1) || !stricmp (Item, DOSUB2))
  1095.     if (TotalWadDirs == 0)
  1096.       Bye (RETURNERROR, "ERROR - Badly placed switch %s in WADDIR field\n", DOSUB1);
  1097.     else
  1098.       WadDir[TotalWadDirs - 1]->DoSubDirs = TRUE;
  1099.   else
  1100.   {
  1101.     if (TotalWadDirs == MAXWADDIRS)
  1102.       Bye (RETURNERROR, "ERROR - Too many WADDIR entries\n");
  1103.     if ((WadDir[TotalWadDirs] = ((struct WadDir_s far *)_fmalloc ((size_t)sizeof (struct WadDir_s)))) == NOMEM)
  1104.       Bye (RETURNERROR, "FATAL ERROR - Out of memory\n");
  1105.     strcpy (S, TestName (Item));
  1106.     if (S[1] == ':')                                                                                      /* Preceded by drive: ? */
  1107.     {
  1108.       if (strlen (S) == 2)                                                                             /* Just a drive, no path ? */
  1109.         Bye (RETURNERROR, "ERROR - Missing path for WADDIR in configuration file\n");
  1110.       DriveNo = ToNumber (S[0]);
  1111.       if (DriveNo != CurDrive)                                                                                   /* A new drive ? */
  1112.         WadDir[TotalWadDirs]->Drive = DriveNo;                                                              /* Store drive number */
  1113.       else
  1114.         WadDir[TotalWadDirs]->Drive = 0;                                                            /* Signal: no new drive given */
  1115.       strcpy (WadDir[TotalWadDirs]->Name, strupr (S + 2));                                                          /* Store path */
  1116.     }
  1117.     else
  1118.     {
  1119.       WadDir[TotalWadDirs]->Drive = 0;                                                                  /* Signal: no drive given */
  1120.       strcpy (WadDir[TotalWadDirs]->Name, strupr (S));
  1121.     }
  1122.     WadDir[TotalWadDirs]->DoSubDirs = FALSE;
  1123.     TotalWadDirs ++;
  1124.   }
  1125. }
  1126.  
  1127. void _fastcall HandleOptDoomdir (char *Item)
  1128.  
  1129. /**********************************************************************************************************************************/
  1130. /* Pre   : 'Item' holds the operand that has been read following a 'DOOMDIR' option.                                              */
  1131. /* Post  : The (global) variables 'DoomDrive' and 'DoomDirectory' have been initialized.                                          */
  1132. /* Import: TestName, Bye.                                                                                                         */
  1133. /**********************************************************************************************************************************/
  1134.  
  1135. {
  1136.   int register DriveNo;
  1137.  
  1138.   strcpy (S, TestName (Item));
  1139.   if (S[1] == ':')                                                                                        /* Preceded by drive: ? */
  1140.   {
  1141.     if (strlen (S) == 2)                                                                               /* Just a drive, no path ? */
  1142.       Bye (RETURNERROR, "ERROR - Missing path for DOOMDIR in configuration file\n");
  1143.     DriveNo = ToNumber (S[0]);
  1144.     if (DriveNo != CurDrive)                                                                                     /* A new drive ? */
  1145.       DoomDrive = DriveNo;                                                                                  /* Store drive number */
  1146.     else
  1147.       DoomDrive = 0;                                                                                /* Signal: no new drive given */
  1148.     strcpy (DoomDirectory, strupr (S + 2));                                                                         /* Store path */
  1149.   }
  1150.   else
  1151.   {
  1152.     DoomDrive = 0;                                                                                      /* Signal: no drive given */
  1153.     strcpy (DoomDirectory, strupr (S));
  1154.   }
  1155. }
  1156.  
  1157. void _fastcall HandleOptAutoinc (char *Item)
  1158.  
  1159. /**********************************************************************************************************************************/
  1160. /* Pre   : 'Item' holds a filename that has been read following an 'AUTOINCLUDE' option.                                          */
  1161. /* Post  : The filename is include in the AutoInc list. If any error occurs, then no return is made.                              */
  1162. /* Import: TestName, Bye.                                                                                                         */
  1163. /**********************************************************************************************************************************/
  1164.  
  1165. {
  1166.   int register DriveNo;
  1167.   int register Index;
  1168.  
  1169.   if (TotalAutoInc == MAXAUTOINCLUDE)
  1170.     Bye (RETURNERROR, "ERROR - Too many AUTOINCLUDE entries\n");
  1171.   if ((AutoInc[TotalAutoInc] = ((struct WadInfo_s far *)_fmalloc ((size_t)sizeof (struct WadInfo_s)))) == NOMEM)
  1172.     Bye (RETURNERROR, "FATAL ERROR - Out of memory\n");
  1173.   strcpy (S, TestName (Item));
  1174.   if (S[1] == ':')                                                                                        /* Preceded by drive: ? */
  1175.   {
  1176.     if (strlen (S) == 2)                                                                               /* Just a drive, no path ? */
  1177.       Bye (RETURNERROR, "ERROR - Missing path for AUTOINCLUDE in configuration file\n");
  1178.     DriveNo = ToNumber (S[0]);
  1179.     if (DriveNo != CurDrive)                                                                                     /* A new drive ? */
  1180.       AutoInc[TotalAutoInc]->Drive = DriveNo;                                                               /* Store drive number */
  1181.     else
  1182.       AutoInc[TotalAutoInc]->Drive = 0;                                                             /* Signal: no new drive given */
  1183.     strcpy (AutoInc[TotalAutoInc]->Path, strupr (S + 2));                                                           /* Store path */
  1184.   }
  1185.   else
  1186.   {
  1187.     AutoInc[TotalAutoInc]->Drive = 0;                                                                   /* Signal: no drive given */
  1188.     strcpy (AutoInc[TotalAutoInc]->Path, strupr (S));
  1189.   }
  1190.   Index = strlen (AutoInc[TotalAutoInc]->Path) - 1;
  1191.   while (AutoInc[TotalAutoInc]->Path[Index] != '\\' && Index > 0)
  1192.     Index --;
  1193.   if (Index == 0)
  1194.   {
  1195.     strcpy (AutoInc[TotalAutoInc]->OrigName, AutoInc[TotalAutoInc]->Path);                                   /* No preceding path */
  1196.     strcpy (AutoInc[TotalAutoInc]->Path, DEFAULTWADDIR);
  1197.   }
  1198.   else
  1199.   {
  1200.     strcpy (AutoInc[TotalAutoInc]->OrigName, AutoInc[TotalAutoInc]->Path + Index + 1);          /* Copy over last part (filename) */
  1201.     AutoInc[TotalAutoInc]->Path[Index] = '\0';                                                          /* Cut filename from path */
  1202.   }
  1203.   TotalAutoInc ++;
  1204. }
  1205.  
  1206. void _fastcall HandleOptAddSwitches (char *Item)
  1207.  
  1208. /**********************************************************************************************************************************/
  1209. /* Pre   : 'Item' holds the operand that has been read following an 'ADDSWITCHES' option.                                         */
  1210. /* Post  : If 'Item' holds a valid switch, then the 'InUse' flag of that switch is set. Otherwise no return is made.              */
  1211. /* Import: Bye.                                                                                                                   */
  1212. /**********************************************************************************************************************************/
  1213.  
  1214. {
  1215.   int register Index;
  1216.  
  1217.   if (Item[0] == '-')                                                    /* Remember that all switches start with a '-' character */
  1218.   {
  1219.     Index = 0;
  1220.     while (Index < MAXADDSWITCHES && stricmp (Item, ExtCom[Index].Command))         /* Test the name against all allowed switches */
  1221.       Index ++;
  1222.     if (Index == MAXADDSWITCHES)
  1223.       Bye (RETURNERROR, "ERROR - Switch %s not supported in ADDSWITCHES\n", Item);
  1224.     ExtCom[Index].InUse = TRUE;
  1225.   }
  1226.   else
  1227.     Bye (RETURNERROR, "ERROR - Unrecognised ADDSWITCHES %s in configuration file\n", Item);
  1228. }
  1229.  
  1230. void ReadConfig (void)
  1231.  
  1232. /**********************************************************************************************************************************/
  1233. /* Pre   : The (global) 'ConfigFile' should be initialized.                                                                       */
  1234. /* Post  : If a configuration file is found, then it has been read out. Only 8 keywords are recognised:                           */
  1235. /*         - DOOMDIR, after which the name of the main DOOM directory should be given.                                            */
  1236. /*         - WADDIR, after which a maximum of 400 WAD directories may be given; If an entry '/S' or '-S' is encountered, then all */
  1237. /*           subdirectories of the previously declared directory will also be used as WADDIRs.                                    */
  1238. /*         - WADINFOFILE, after which a WAD info file may be given. All simple errors are reported.                               */
  1239. /*         - SERDRIVER, after which a new serial driver should be given (in stead of SERSETUP);                                   */
  1240. /*         - IPXDRIVER, after which a new network driver should be given (in stead of IPXSETUP);                                  */
  1241. /*         - SETSKILL, after which the default skill (1-5) must be given.                                                         */
  1242. /*         - DEATHMATCH (no parameters). This means that deathmatch will be set as default.                                       */
  1243. /*         - AUTOINCLUDE, after which a maximum of 5 WAD files (complete with (partial) path) may be given. These files will then */
  1244. /*           automatically be selected when starting. If the file is not crossed when reading the directories (WADDIRs), then the */
  1245. /*           file is just not selected (no error will be generated).                                                              */
  1246. /*         - NOSEARCH (no parameters). If this keyword is given, then the program will not search all given WADDIR directories,   */
  1247. /*           it will use the WADINFOFILE instead and use all named entries instead. (This should be used with caution!)           */
  1248. /*         - SETCOMPORT, after which the default COM port (1-4) must be given for null-modem link.                                */
  1249. /*         - SETNODES, after which the default number of players (2-4) must be given for IPX link.                                */
  1250. /*         - SETSOCKET, after which a network socket (0-255) must be given.                                                       */
  1251. /*         - SETPLAYTYPE, after which one of the keywords "ALONE", "IPX" or "SERIAL" must be given.                               */
  1252. /*         - ADDSWITCHES, after which all the direct DOOM switches should be typed.                                               */
  1253. /*         - SORTFILES, after which one of the keywords "NAME" or "INFO" must be given.                                           */
  1254. /*         - NOFULLNAME (no parameters). Will stop using "music" i.s.o. "-         m".                                            */
  1255. /*         - NOAUTORETURN (no parameters). If this option is given, then no <CR> is passed to DOOM at startup.                    */
  1256. /*                                                                                                                                */
  1257. /*         If one (or all) are not found, then they are initialized with the defaults.                                            */
  1258. /*                                                                                                                                */
  1259. /*         If a character '#' is encountered, then the rest of this line is ignored (comment).                                    */
  1260. /* Import: NextString, HandleOptWaddir, HandleOptAutoinc, HandleOptAddSwitches, HandleOptDoomdir, Bye.                            */
  1261. /**********************************************************************************************************************************/
  1262.  
  1263. {
  1264.   FILE    *Fp;
  1265.   char     Item[256];
  1266.   char     ContinueOption;
  1267.   boolean  Handled;
  1268.   
  1269.   TotalWadDirs = 0;                                                                                    /* Initialize all counters */
  1270.   TotalAutoInc = 0;
  1271.   strcpy (DoomDirectory, DEFAULTWADDIR);                                                  /* Use the current directory as default */
  1272.   strcpy (InfoFile, DEFAULTINFOFILE);                                                           /* Initialize a default file name */
  1273.   strcpy (IpxDriver, STARTIPX);
  1274.   strcpy (SerDriver, STARTLINK);
  1275.   if (Fp = fopen (ConfigFile, "r"))                                                            /* Skip if no CONFIGFILE was found */
  1276.   {
  1277.     NextString (Fp, Item);                                                          /* Read-ahead first string: must be a keyword */
  1278.     while (!feof (Fp))
  1279.     {
  1280.       Handled = FALSE;
  1281.       for (M = 0 ; !Handled && M < NUMOPTIONS ; M ++)
  1282.         if (!stricmp (Item, ConfOp[M].OptionName))
  1283.         {
  1284.           if (ConfOp[M].OptionType != OPT_SET)                                         /* Option takes one (or more) operand(s) ? */
  1285.           {
  1286.             NextString (Fp, Item);                                                                            /* Read the operand */
  1287.             if (Item[0] == '\0')
  1288.               Bye (RETURNERROR, "ERROR - Missing operand after switch %s\n", ConfOp[M].OptionName);
  1289.           }
  1290.           ContinueOption = 0;                                                      /* Signal: no option has more than one operand */
  1291.           switch (ConfOp[M].OptionType)
  1292.           {
  1293.             case OPT_DOOMDIR : HandleOptDoomdir (Item);
  1294.                                break;
  1295.             case OPT_FILE    : strcpy (S, TestName (Item));
  1296.                                if (S[1] == ':' && S[2] == '\0')                                             /* Only a drive given */
  1297.                                  Bye (RETURNERROR, "ERROR - Missing pathname after %s in switch %s\n", S, ConfOp[M].OptionName);
  1298.                                strcpy ((char *)ConfOp[M].DataPtr, S);
  1299.                                break;
  1300.             case OPT_NUM     : for (N = 0 ; N < strlen (Item) ; N ++)
  1301.                                  if (strlen (Item) > 3 || !isdigit (Item[N]))
  1302.                                    Bye (RETURNERROR, "ERROR - Invalid number %s after switch %s\n", Item, ConfOp[M].OptionName);
  1303.                                *((int *)ConfOp[M].DataPtr) = atoi (Item);
  1304.                                break;
  1305.             case OPT_SET     : *((char *)ConfOp[M].DataPtr) = TRUE;
  1306.                                break;
  1307.             case OPT_STRING  : strcpy ((char *)ConfOp[M].DataPtr, Item);
  1308.                                break;
  1309.             case OPT_WADDIR  : ContinueOption = 1;
  1310.                                HandleOptWaddir (Item);
  1311.                                break;
  1312.             case OPT_AUTOINC : ContinueOption = 2;
  1313.                                HandleOptAutoinc (Item);
  1314.                                break;
  1315.             case OPT_ADDSWIT : ContinueOption = 3;
  1316.                                HandleOptAddSwitches (Item);
  1317.                                break;
  1318.             case OPT_PLAYTYP : if (!stricmp (Item, "ALONE"))
  1319.                                  PlayTypeActive = 1;
  1320.                                else
  1321.                                  if (!stricmp (Item, "IPX"))
  1322.                                    PlayTypeActive = 2;
  1323.                                  else
  1324.                                    if (!stricmp (Item, "SERIAL"))
  1325.                                      PlayTypeActive = 3;
  1326.                                    else
  1327.                                      Bye (RETURNERROR, "ERROR - Unknown playtype %s\n", Item);
  1328.                                break;
  1329.             case OPT_SORTFIL : SortWadFiles = TRUE;
  1330.                                if (!stricmp (Item, "NAME") || !stricmp (Item, "INFO"))
  1331.                                  SortByName = !stricmp (Item, "NAME");
  1332.                                else
  1333.                                  Bye (RETURNERROR, "ERROR - Unknown sort criteria %s after keyword SORTFILES\n", Item);
  1334.                                break;
  1335.           }
  1336.           Handled = TRUE;
  1337.         }
  1338.       if (!Handled)                                                                       /* Read item is not one of the keywords */
  1339.         switch (ContinueOption)
  1340.         {
  1341.           case 0 : Bye (RETURNERROR, "ERROR - Unknown keyword %s in configuration file\n", Item);
  1342.           case 1 : HandleOptWaddir (Item);
  1343.                    break;
  1344.           case 2 : HandleOptAutoinc (Item);
  1345.                    break;
  1346.           case 3 : HandleOptAddSwitches (Item);
  1347.         }
  1348.       NextString (Fp, Item);                                                                                  /* Read next option */
  1349.     }
  1350.     fclose (Fp);
  1351.   }
  1352.   else
  1353.     if (ConfigChange)                                                             /* It is an error if a different file was given */
  1354.       Bye (RETURNERROR, "ERROR - configuration file not found\n");
  1355.   OtherSerDriver = strcmp (SerDriver, STARTLINK);
  1356.   OtherIpxDriver = strcmp (IpxDriver, STARTIPX);
  1357.   if (TotalWadDirs == 0)                                                               /* No CONFIGFILE or WADDIR entries found ? */
  1358.   {
  1359.     if ((WadDir[0] = ((struct WadDir_s far *)_fmalloc ((size_t)sizeof (struct WadDir_s)))) == NOMEM)
  1360.       Bye (RETURNERROR, "FATAL ERROR - Out of memory\n");
  1361.     WadDir[0]->Drive = 0;                                                               /* Then use the DOOM directory as default */
  1362.     strcpy (WadDir[0]->Name, DoomDirectory);
  1363.     TotalWadDirs = 1;
  1364.   }
  1365.   if (DifficultyActive == 0 || DifficultyActive > 5)                                     /* Filter out nonsense numerical options */
  1366.     Bye (RETURNERROR, "ERROR - Invalid skill number %d\n", DifficultyActive);
  1367.   if (CommPortActive == 0 || CommPortActive > 4)
  1368.     Bye (RETURNERROR, "ERROR - Invalid COM port number %d\n", CommPortActive);
  1369.   if (NumNodesActive < 2 || NumNodesActive > 4)
  1370.     Bye (RETURNERROR, "ERROR - You cannot play network DOOM with %d player(s)\n", NumNodesActive);
  1371.   if (DeathmatchOn)
  1372.   {
  1373.     DeathmatchOn = FALSE;                                                    /* Select the better deathmatch version if available */
  1374.     DeathmatchV2On = TRUE;
  1375.   }
  1376. }
  1377.  
  1378. void _fastcall PrintDifficulties (int HighLite)
  1379.  
  1380. /**********************************************************************************************************************************/
  1381. /* Pre   : 'HighLite' contains the pointed difficulty, or 0 if none was pointed to.                                               */
  1382. /* Post  : The difficulties have been printed. The active difficulty has a checked box, all others have empty boxes. The pointed  */
  1383. /*         difficulty is printed in a different color.                                                                            */
  1384. /* Import: PrText.                                                                                                                */
  1385. /**********************************************************************************************************************************/
  1386.  
  1387. {
  1388.   for (M = 0 ; M < NUMDIFFICULTY ; M ++)
  1389.     if (M == HighLite - 1)
  1390.       PrText ((M == DifficultyActive - 1), 3 + M, 28, LRED, Difficulties[M]);
  1391.     else
  1392.       PrText ((M == DifficultyActive - 1), 3 + M, 28, LMAGENTA, Difficulties[M]);
  1393. }
  1394.  
  1395. void _fastcall PrintPlayTypes (int HighLite)
  1396.  
  1397. /**********************************************************************************************************************************/
  1398. /* Pre   : 'HighLite' contains the pointed playtype, or 0 if none was pointed to.                                                 */
  1399. /* Post  : The playtypes have been printed. The active playtype has a checked box, all others have empty boxes. The pointed       */
  1400. /*         playtype is printed in a different color.                                                                              */
  1401. /* Import: PrText.                                                                                                                */
  1402. /**********************************************************************************************************************************/
  1403.  
  1404. {
  1405.   for (M = 0 ; M < NUMPLAYTYPE ; M ++)
  1406.     if (M == HighLite - 1)
  1407.       PrText ((M == PlayTypeActive - 1), 3 + M, 55, LRED, PlayTypes[M]);
  1408.     else
  1409.       PrText ((M == PlayTypeActive - 1), 3 + M, 55, LMAGENTA, PlayTypes[M]);
  1410.   switch (PlayTypeActive)
  1411.   {
  1412.     case 1: PrText (-1, 6, 55, DBLACK, "                     ");                                                         /* Alone */
  1413.             break;
  1414.     case 2: if (!OtherIpxDriver)                                                                                /* IPX compatible */
  1415.               if (HighLite == 4)
  1416.                 PrText (-1, 6, 55, LRED, "(%c) %s", NumNodesActive + '0', NumberNodes);
  1417.               else
  1418.                 PrText (-1, 6, 55, LMAGENTA, "(%c) %s", NumNodesActive + '0', NumberNodes);
  1419.             else
  1420.               PrText (-1, 6, 55, LBLACK, "(%c) %s", NumNodesActive + '0', NumberNodes);
  1421.             break;
  1422.     case 3: if (!OtherSerDriver)                                                                               /* Null-modem link */
  1423.               if (HighLite == 4)
  1424.                 PrText (-1, 6, 55, LRED, "(%c) %s", CommPortActive + '0', CommPort);
  1425.               else
  1426.                 PrText (-1, 6, 55, LMAGENTA, "(%c) %s", CommPortActive + '0', CommPort);
  1427.             else
  1428.               PrText (-1, 6, 55, LBLACK, "(%c) %s", CommPortActive + '0', CommPort);
  1429.   }
  1430. }
  1431.  
  1432. void _fastcall PrintRespawnMonsters (boolean HighLite)
  1433.  
  1434. /**********************************************************************************************************************************/
  1435. /* Pre   : 'HighLite' is TRUE if this item was pointed to.                                                                        */
  1436. /* Post  : The RESPAWN text has been printed, with a checked box before it if it was selected, or empty otherwise.                */
  1437. /*         If 'HighLite' was TRUE, then the text is printed in a different color.                                                 */
  1438. /* Import: PrText.                                                                                                                */
  1439. /**********************************************************************************************************************************/
  1440.  
  1441. {
  1442.   if (HighLite)
  1443.     PrText (RespMonstersOn, 10, 28, LRED, RespawnMonsters);
  1444.   else
  1445.     PrText (RespMonstersOn, 10, 28, LMAGENTA, RespawnMonsters);
  1446. }
  1447.  
  1448. void _fastcall PrintNoMonsters (boolean HighLite)
  1449.  
  1450. /**********************************************************************************************************************************/
  1451. /* Pre   : 'HighLite' is TRUE if this item was pointed to.                                                                        */
  1452. /* Post  : The NOMONSTERS text has been printed, with a checked box before it if it was selected, or empty otherwise.             */
  1453. /*         If 'HighLite' was TRUE, then the text is printed in a different color.                                                 */
  1454. /* Import: PrText.                                                                                                                */
  1455. /**********************************************************************************************************************************/
  1456.  
  1457. {
  1458.   if (HighLite)
  1459.     PrText (NoMonstersOn, 9, 28, LRED, NoMonsters);
  1460.   else
  1461.     PrText (NoMonstersOn, 9, 28, LMAGENTA, NoMonsters);
  1462. }
  1463.  
  1464. void _fastcall PrintFastMonsters (boolean HighLite)
  1465.  
  1466. /**********************************************************************************************************************************/
  1467. /* Pre   : 'HighLite' is TRUE if this item was pointed to.                                                                        */
  1468. /* Post  : The FASTMONSTERS text has been printed, with a checked box before it if it was selected, or empty otherwise.           */
  1469. /*         If 'HighLite' was TRUE, then the text is printed in a different color.                                                 */
  1470. /* Import: PrText.                                                                                                                */
  1471. /**********************************************************************************************************************************/
  1472.  
  1473. {
  1474.   if (HighLite)
  1475.     PrText (FastMonstersOn, 11, 28, LRED, FastMonsters);
  1476.   else
  1477.     PrText (FastMonstersOn, 11, 28, LMAGENTA, FastMonsters);
  1478. }
  1479.  
  1480. void _fastcall PrintDeathmatch (boolean HighLite)
  1481.  
  1482. /**********************************************************************************************************************************/
  1483. /* Pre   : 'HighLite' is TRUE if this item was pointed to.                                                                        */
  1484. /* Post  : The DEATHMATCH text has been printed, with a checked box before it if it was selected, or empty otherwise.             */
  1485. /*         If 'HighLite' was TRUE, then the text is printed in a different color.                                                 */
  1486. /* Import: PrText.                                                                                                                */
  1487. /**********************************************************************************************************************************/
  1488.  
  1489. {
  1490.   if (HighLite)
  1491.     PrText (DeathmatchOn, 8, 55, LRED, Deathmatch);
  1492.   else
  1493.     PrText (DeathmatchOn, 8, 55, LMAGENTA, Deathmatch);
  1494. }
  1495.  
  1496. void _fastcall PrintV2Deathmatch (boolean HighLite)
  1497.  
  1498. /**********************************************************************************************************************************/
  1499. /* Pre   : 'HighLite' is TRUE if this item was pointed to.                                                                        */
  1500. /* Post  : The DEATHMATCH V2 text has been printed, with a checked box before it if it was selected, or empty otherwise.          */
  1501. /*         If 'HighLite' was TRUE, then the text is printed in a different color.                                                 */
  1502. /* Import: PrText.                                                                                                                */
  1503. /**********************************************************************************************************************************/
  1504.  
  1505. {
  1506.   if (HighLite)
  1507.     PrText (DeathmatchV2On, 9, 55, LRED, DeathmatchV2);
  1508.   else
  1509.     PrText (DeathmatchV2On, 9, 55, LMAGENTA, DeathmatchV2);
  1510. }
  1511.  
  1512. void _fastcall PrintLevelPagers (int HighLite)
  1513.  
  1514. /**********************************************************************************************************************************/
  1515. /* Pre   : 'HighLite' is 1 for left, 2 for right or 0 for no pager.                                                               */
  1516. /* Post  : The pagers have been printed. The 'HighLite' pager in a different color.                                               */
  1517. /* Import: PrText.                                                                                                                */
  1518. /**********************************************************************************************************************************/
  1519.  
  1520. {
  1521.   if (CurrentLevel > 1)                                                                            /* Are there previous levels ? */
  1522.     if (HighLite == 1)                                                                 /* Print pager for 'previous' level (left) */
  1523.       PrText (-1, 3, 3, LRED, PreviousLevel);
  1524.     else
  1525.       PrText (-1, 3, 3, LWHITE, PreviousLevel);
  1526.   else
  1527.     PrText (-1, 3, 3, LBLACK, PreviousLevel);
  1528.   if (CurrentLevel < NUMLEVEL)                                                                         /* Are there next levels ? */
  1529.     if (HighLite == 2)                                                                    /* Print pager for 'next' level (right) */
  1530.       PrText (-1, 3, 14, LRED, NextLevel);
  1531.     else
  1532.       PrText (-1, 3, 14, LWHITE, NextLevel);
  1533.   else
  1534.     PrText (-1, 3, 14, LBLACK, NextLevel);
  1535. }
  1536.  
  1537. void _fastcall PrintLevel (void)
  1538.  
  1539. /**********************************************************************************************************************************/
  1540. /* Pre   : None.                                                                                                                  */
  1541. /* Post  : The current level is printed, with the level name below it.                                                            */
  1542. /* Import: PrText.                                                                                                                */
  1543. /**********************************************************************************************************************************/
  1544.  
  1545. {
  1546.   PrText (-1, 3, 7, DCYAN, "MAP %02d", CurrentLevel);
  1547.   if (CurrentLevel <= 32)
  1548.     PrText (-1, 5, 1, DWHITE, IdLevelNames[CurrentLevel - 1]);
  1549.   else
  1550.     PrText (-1, 5, 1, DWHITE, "                   ");
  1551. }
  1552.  
  1553. void _fastcall PrintWadFiles (void)
  1554.  
  1555. /**********************************************************************************************************************************/
  1556. /* Pre   : None.                                                                                                                  */
  1557. /* Post  : The active page with WAD files has been printed (as determined by 'CurrentPage') has been printed. All selected WAD    */
  1558. /*         files are printed in a different color, any unused part of the page has been cleared from the screen. After each name  */
  1559. /*         is the read info printed. Highliting of a pointed wadfile is not handled here.                                         */
  1560. /* Import: PrText.                                                                                                                */
  1561. /**********************************************************************************************************************************/
  1562.  
  1563. {
  1564.   short register PositionX;
  1565.   short register PositionY;
  1566.  
  1567.   for (M = CurrentPage * PAGE ; M < (CurrentPage + 1) * PAGE ; M += WADHEIGHT)                              /* Handle each column */
  1568.     for (N = M ; N < M + WADHEIGHT ; N ++)                                                       /* Handle each row in the column */
  1569.     {
  1570.       PositionY = 13 + (N % WADHEIGHT);
  1571.       PositionX = ((M - CurrentPage * PAGE) / WADHEIGHT) * WADWIDTH + 1;
  1572.       if (N < TotalWads)                                                                              /* WAD file number exists ? */
  1573.       {
  1574.         if (WadInfo[N]->Selected)
  1575.           PrText (-1, PositionY, PositionX, DGREEN, WadInfo[N]->Name);                                          /* Print filename */
  1576.         else
  1577.           PrText (-1, PositionY, PositionX, DWHITE, WadInfo[N]->Name);
  1578.         PrText (-1, PositionY, PositionX + MAXFNAME, DCYAN, WadInfo[N]->Info);                                      /* Print info */
  1579.       }
  1580.       else                                                                             /* WAD file number after the last WAD file */
  1581.         PrText (-1, PositionY, PositionX, DBLACK, "                         ");                         /* Clear this screen part */
  1582.     }
  1583. }
  1584.  
  1585. void _fastcall PrintPagers (int HighLite)
  1586.  
  1587. /**********************************************************************************************************************************/
  1588. /* Pre   : 'HighLite' is 1 for left, 2 for right or 0 for no pager.                                                               */
  1589. /* Post  : The pagers have been printed. The 'HighLite' pager in a different color.                                               */
  1590. /* Import: PrText.                                                                                                                */
  1591. /**********************************************************************************************************************************/
  1592.  
  1593. {
  1594.   if (CurrentPage > 0)                                                                              /* Are there previous pages ? */
  1595.     if (HighLite == 1)                                                                  /* Print pager for 'previous' page (left) */
  1596.       PrText (-1, 11, 60, LRED, PreviousPage);
  1597.     else
  1598.       PrText (-1, 11, 60, LWHITE, PreviousPage);
  1599.   else
  1600.     PrText (-1, 11, 60, LBLACK, PreviousPage);
  1601.   if (CurrentPage < LastPage)                                                                           /* Are there next pages ? */
  1602.     if (HighLite == 2)                                                                     /* Print pager for 'next' page (right) */
  1603.       PrText (-1, 11, 78, LRED, NextPage);
  1604.     else
  1605.       PrText (-1, 11, 78, LWHITE, NextPage);
  1606.   else
  1607.     PrText (-1, 11, 78, LBLACK, NextPage);
  1608. }
  1609.  
  1610. void _fastcall PrintRdPrev (boolean HighLite)
  1611.  
  1612. /**********************************************************************************************************************************/
  1613. /* Pre   : 'HighLite' is TRUE if this item is selected.                                                                           */
  1614. /* Post  : The read previous text has been printed. If 'HighLite' was TRUE, than in a different color.                            */
  1615. /* Import: PrText.                                                                                                                */
  1616. /**********************************************************************************************************************************/
  1617.  
  1618. {
  1619.   if (HighLite)
  1620.     PrText (-1, 11, 1, LRED, ReadPrevious);
  1621.   else
  1622.     PrText (-1, 11, 1, LMAGENTA, ReadPrevious);
  1623. }
  1624.  
  1625. void _fastcall PrintAutomatic (boolean HighLite)
  1626.  
  1627. /**********************************************************************************************************************************/
  1628. /* Pre   : 'HighLite' is TRUE if this item is selected.                                                                           */
  1629. /* Post  : The automatic text has been printed. If 'HighLite' was TRUE, than in a different color.                                */
  1630. /* Import: PrText.                                                                                                                */
  1631. /**********************************************************************************************************************************/
  1632.  
  1633. {
  1634.   if (HighLite)
  1635.     PrText (-1, 10, 1, LRED, Automatic);
  1636.   else
  1637.     PrText (-1, 10, 1, LMAGENTA, Automatic);
  1638. }
  1639.  
  1640. void _fastcall PrintStart (boolean HighLite)
  1641.  
  1642. /**********************************************************************************************************************************/
  1643. /* Pre   : 'HighLite' is TRUE if this item is selected.                                                                           */
  1644. /* Post  : The start text has been printed. If 'HighLite' was TRUE, than in a different color.                                    */
  1645. /* Import: PrText.                                                                                                                */
  1646. /**********************************************************************************************************************************/
  1647.  
  1648. {
  1649.   if (HighLite)
  1650.     PrText (-1, 9, 1, LRED, StartGame);
  1651.   else
  1652.     PrText (-1, 9, 1, LMAGENTA, StartGame);
  1653. }
  1654.  
  1655. void _fastcall UnselectPreviousField (char SkipFieldNum)
  1656.  
  1657. /**********************************************************************************************************************************/
  1658. /* Pre   : 'SkipFieldNum' holds the field number that should NOT be unselected.                                                   */
  1659. /* Post  : The previous selected field has been unselected, if one was pointed to and it was not 'SkipFieldNum'.                  */
  1660. /* Import: PrintDifficulties, PrintPlayTypes, PrintLevelPagers, PrintDeathmatch, PrintPagers, PrintRdPrev, PrintStart,            */
  1661. /*         PrintAutomatic, PrintDeathmatchV2, PrintRespawnMonsters, PrintNoMonsters, PrintFastMonsters, PrText, HideMouse,        */
  1662. /*         ShowMouse.                                                                                                             */
  1663. /**********************************************************************************************************************************/
  1664.  
  1665. {
  1666.   short register PositionX;
  1667.   short register PositionY;
  1668.   int   register OldWadNumber;
  1669.  
  1670.   if (CurrentField != NOFIELD && CurrentField != SkipFieldNum)
  1671.   {
  1672.     switch (CurrentField)
  1673.     {
  1674.       case DIFFICULTYFIELD   : PrintDifficulties (0);
  1675.                                break;
  1676.       case PLAYTYPEFIELD     : PrintPlayTypes (0);
  1677.                                break;
  1678.       case LEVELFIELD        : PrintLevelPagers (0);
  1679.                                break;
  1680.       case DEATHMATCHFIELD   : PrintDeathmatch (FALSE);
  1681.                                break;
  1682.       case DEATHMATCHV2FIELD : PrintV2Deathmatch (FALSE);
  1683.                                break;
  1684.       case RESPAWNFIELD      : PrintRespawnMonsters (FALSE);
  1685.                                break;
  1686.       case NOMONSTERSFIELD   : PrintNoMonsters (FALSE);
  1687.                                break;
  1688.       case FASTMONSTERSFIELD : PrintFastMonsters (FALSE);
  1689.                                break;
  1690.       case PAGERFIELD        : PrintPagers (0);
  1691.                                break;
  1692.       case RDPREVFIELD       : PrintRdPrev (FALSE);
  1693.                                break;
  1694.       case STARTFIELD        : PrintStart (FALSE);
  1695.                                break;
  1696.       case AUTOMATICFIELD    : PrintAutomatic (FALSE);
  1697.                                break;
  1698.       case FILEFIELD         : PositionY = 13 + ((PreviousWad - 1) % WADHEIGHT);           /* Location of previously selected WAD */
  1699.                                PositionX = ((PreviousWad - 1) / WADHEIGHT) * WADWIDTH + 1;
  1700.                                OldWadNumber = CurrentPage * PAGE + PreviousWad - 1;                         /* Number of that WAD */
  1701.                                HideMouse ();
  1702.                                if (WadInfo[OldWadNumber]->Selected)
  1703.                                  PrText (-1, PositionY, PositionX, DGREEN, WadInfo[OldWadNumber]->Name);
  1704.                                else
  1705.                                  PrText (-1, PositionY, PositionX, DWHITE, WadInfo[OldWadNumber]->Name);
  1706.                                ShowMouse ();
  1707.     }
  1708.   }
  1709. }
  1710.  
  1711. boolean WaitForConfirmation (void)
  1712.  
  1713. /**********************************************************************************************************************************/
  1714. /* Pre   : None.                                                                                                                  */
  1715. /* Post  : The user must press a key. If this key is 'Y', then TRUE is returned, otherwise FALSE.                                 */
  1716. /* Import: None.                                                                                                                  */
  1717. /**********************************************************************************************************************************/
  1718.  
  1719. {
  1720.   unsigned int Key;
  1721.  
  1722.   while (!_bios_keybrd (_KEYBRD_READY))                                                                         /* Wait for a key */
  1723.     ;
  1724.   Key = _bios_keybrd (_KEYBRD_READ) & 0x00FF;                                                                 /* Read pressed key */
  1725.   return (toupper ((char)Key) == 'Y');                                                                /* Return TRUE if it is 'Y' */
  1726. }
  1727.  
  1728. boolean _fastcall Requester (short Y0, short X0, short DY, short DX)
  1729.  
  1730. /**********************************************************************************************************************************/
  1731. /* Pre   : 'Y0' and 'X0' hold the top-left coordinates of the requester that is to be printed. The hight will be 'DY' lines, the  */
  1732. /*         width will be 'DX' columns.                                                                                            */
  1733. /* Post  : The requester has been drawn.                                                                                          */
  1734. /*         Note that the requester is surrounded with an empty zone.                                                              */
  1735. /* Import: HideMouse, UnselectPreviousField, PrText.                                                                              */
  1736. /**********************************************************************************************************************************/
  1737.  
  1738. {
  1739.   HideMouse ();                                                                           /* Clear mousepointer and hi-light bars */
  1740.   UnselectPreviousField (NOFIELD);
  1741.   CurrentField = NOFIELD;
  1742.   Mouse.CoChange = TRUE;                                                                         /* Signal: re-hi-light on return */
  1743.   for (M = 0 ; M < DX ; M ++)
  1744.     PrText (-1, Y0, X0 + M, DBLACK, " ");
  1745.   PrText (-1, Y0 + 1, X0, LRED, " \xC9");
  1746.   for (M = 0 ; M < (DX - 4) ; M ++)
  1747.     PrText (-1, 0, 0, LRED, "\xCD");
  1748.   PrText (-1, 0, 0, LRED, "\xBB ");
  1749.   for (M = 2 ; M < (DY - 2) ; M ++)
  1750.   {
  1751.     PrText (-1, Y0 + M, X0, LRED, " \xBA");
  1752.     for (N = 0 ; N < (DX - 4) ; N ++)
  1753.       PrText (-1, 0, 0, DBLACK, " ");
  1754.     PrText (-1, 0, 0, LRED, "\xBA ");
  1755.   }
  1756.   PrText (-1, Y0 + DY - 2, X0, LRED, " \xC8");
  1757.   for (M = 0 ; M < (DX - 4) ; M ++)
  1758.     PrText (-1, 0, 0, LRED, "\xCD");
  1759.   PrText (-1, 0, 0, LRED, "\xBC ");
  1760.   for (M = 0 ; M < DX ; M ++)
  1761.     PrText (-1, Y0 + DY - 1, X0 + M, DBLACK, " ");
  1762. }
  1763.  
  1764. void GiveHelp (void)
  1765.  
  1766. /**********************************************************************************************************************************/
  1767. /* Pre   : The user pressed [F1].                                                                                                 */
  1768. /* Post  : A requester has been drawn, containing a listing of all available keys (depending whether a mouse has been found).     */
  1769. /*         The user must press a key to return.                                                                                   */
  1770. /* Import: Requester, PrText, WaitForConfirmation, PrintWadFiles, ShowMouse.                                                      */
  1771. /**********************************************************************************************************************************/
  1772.  
  1773. {
  1774.   Requester (14, 2, 16, 78);
  1775.   PrText (-1, 17, 24, DCYAN, "The following keys are available:");
  1776.   PrText (-1, 19, 5, DWHITE, "%c  ", KEY_LEVELUP);
  1777.   PrText (-1, 0, 0, DCYAN, "Level up");
  1778.   PrText (-1, 20, 5, DWHITE, "%c  ", KEY_LEVELDOWN);
  1779.   PrText (-1, 0, 0, DCYAN, "Level down");
  1780.   PrText (-1, 21, 5, DWHITE, "%c  ", KEY_DIFFICULTY);
  1781.   PrText (-1, 0, 0, DCYAN, "Skill");
  1782.   PrText (-1, 22, 5, DWHITE, "%c  ", KEY_PLAYTYPE);
  1783.   PrText (-1, 0, 0, DCYAN, "Playtype");
  1784.   if (!OtherIpxDriver)
  1785.   {
  1786.     PrText (-1, 25, 5, DWHITE, "%c  ", KEY_NODES);
  1787.     PrText (-1, 0, 0, DCYAN, "Number of players");
  1788.   }
  1789.   PrText (-1, 23, 5, DWHITE, "%c  ", KEY_DEATHMATCH);
  1790.   PrText (-1, 0, 0, DCYAN, "Deathmatch");
  1791.   if (!OtherSerDriver)
  1792.   {
  1793.     PrText (-1, 26, 5, DWHITE, "%c  ", KEY_COMPORT);
  1794.     PrText (-1, 0, 0, DCYAN, "COM port");
  1795.   }
  1796.   PrText (-1, 19, 28, DWHITE, "%c       ", KEY_NOMONSTERS);
  1797.   PrText (-1, 0, 0, DCYAN, "No monsters");
  1798.   PrText (-1, 20, 28, DWHITE, "%c       ", KEY_RESPAWNMONST);
  1799.   PrText (-1, 0, 0, DCYAN, "Respawn monsters");
  1800.   PrText (-1, 24, 5, DWHITE, "%c  ", KEY_DEATHMATCHV2);
  1801.   PrText (-1, 0, 0, DCYAN, "Deathmatch v2.0");
  1802.   PrText (-1, 21, 28, DWHITE, "%c       ", KEY_FASTMONSTERS);
  1803.   PrText (-1, 0, 0, DCYAN, "Fast monsters");
  1804.   PrText (-1, 22, 28, DWHITE, "%c       ", KEY_AUTO);
  1805.   PrText (-1, 0, 0, DCYAN, "AUTO SELECT");
  1806.   PrText (-1, 23, 28, DWHITE, "%c       ", KEY_READPREVIOUS);
  1807.   PrText (-1, 0, 0, DCYAN, "READ PREVIOUS");
  1808.   PrText (-1, 24, 28, DWHITE, "[ENTER] ");
  1809.   PrText (-1, 0, 0, DCYAN, "START DOOM!");
  1810.   PrText (-1, 25, 28, DWHITE, "[ESC]   ");
  1811.   PrText (-1, 0, 0, DCYAN, "Abort EasyWAD");
  1812.   PrText (-1, 26, 28, DWHITE, "[DEL]   ");
  1813.   PrText (-1, 0, 0, DCYAN, "Delete WAD");
  1814.   PrText (-1, 19, 55, DWHITE, "[PG UP] ");
  1815.   PrText (-1, 0, 0, DCYAN, "Next WAD page");
  1816.   PrText (-1, 20, 55, DWHITE, "[PG DN] ");
  1817.   PrText (-1, 0, 0, DCYAN, "Previous page");
  1818.   PrText (-1, 21, 55, DWHITE, "[F1]    ");
  1819.   PrText (-1, 0, 0, DCYAN, "This help page");
  1820.   PrText (-1, 22, 55, DWHITE, "[F5]    ");
  1821.   PrText (-1, 0, 0, DCYAN, "Rescan WADDIRs");
  1822.   PrText (-1, 23, 55, DWHITE, "[F7]    ");
  1823.   PrText (-1, 0, 0, DCYAN, "Reset fullname");
  1824.   PrText (-1, 24, 55, DWHITE, "[F8]    ");
  1825.   PrText (-1, 0, 0, DCYAN, "Resort WADs");
  1826.   if (!UseMouse)
  1827.   {
  1828.     PrText (-1, 25, 55, DWHITE, "[CURS]  ");
  1829.     PrText (-1, 0, 0, DCYAN, "Move around");
  1830.     PrText (-1, 26, 55, DWHITE, "[SPACE] ");
  1831.     PrText (-1, 0, 0, DCYAN, "Select WAD");
  1832.   }
  1833.   WaitForConfirmation ();
  1834.   for (M = 15 ; M < 29 ; M ++)                                                                                 /* Erase requester */
  1835.     PrText (-1, M, 3, DBLACK, "                                                                            ");
  1836.   PrintWadFiles ();
  1837.   ShowMouse ();
  1838. }
  1839.  
  1840. void SortFiles (void)
  1841.  
  1842. /**********************************************************************************************************************************/
  1843. /* Pre   : None.                                                                                                                  */
  1844. /* Post  : If (global) 'SortWadFiles' is TRUE, then the WAD files in memory have been sorted according to the sorttype.           */
  1845. /*         If (global) 'SortByName' is TRUE, then they have been sorted by name, otherwise they have been sorted by info field.   */
  1846. /* Import: None.                                                                                                                  */
  1847. /**********************************************************************************************************************************/
  1848.  
  1849. {
  1850.   boolean  More;
  1851.   struct   WadInfo_s far *TmpPtr;                                                           /* Entry as read from the WADINFOFILE */
  1852.  
  1853.   if (SortWadFiles)                                                                            /* SORTFILES given in CONFIGFILE ? */
  1854.   {
  1855.     More = TRUE;
  1856.     for (N = 0 ; N < TotalWads - 1 && More ; N ++)                                                        /* Perform a bubblesort */
  1857.     {
  1858.       More = FALSE;
  1859.       for (M = 0 ; M < TotalWads - 1 ; M ++)
  1860.         if (SortByName)                                                                                   /* Sort by 'Name' field */
  1861.         {
  1862.           if (strcmp (WadInfo[M]->Name, WadInfo[M + 1]->Name) > 0)                                /* Next 'larger' than current ? */
  1863.           {
  1864.             More = TRUE;
  1865.             TmpPtr = WadInfo[M];                                                                        /* Then flip the pointers */
  1866.             WadInfo[M] = WadInfo[M + 1];
  1867.             WadInfo[M + 1] = TmpPtr;
  1868.           }
  1869.         }
  1870.         else                                                                                              /* Sort by 'Info' field */
  1871.         {
  1872.           if (strcmp (WadInfo[M]->Info, WadInfo[M + 1]->Info) > 0)
  1873.           {
  1874.             More = TRUE;
  1875.             TmpPtr = WadInfo[M];
  1876.             WadInfo[M] = WadInfo[M + 1];
  1877.             WadInfo[M + 1] = TmpPtr;
  1878.           }
  1879.         }
  1880.     }
  1881.   }
  1882. }
  1883.  
  1884. void ConvertFullName (struct WadInfo_s far *ConInfo)
  1885.  
  1886. /**********************************************************************************************************************************/
  1887. /* Pre   : 'ConInfo' points to the WadInfo structure that should have its Info field re-examined.                                 */
  1888. /* Post  : If the Info field contained a full name (e.g. 'music'), then it is converted to the short name (e.g. '-         m').   */
  1889. /*         Before returning, the Info field is expanded to contain exactly MAXINFOLEN characters.                                 */
  1890. /* Import: None.                                                                                                                  */
  1891. /**********************************************************************************************************************************/
  1892.  
  1893. {
  1894.   int register Index;
  1895.   int register SLNoLevel;                                                                                       /* StrLen NoLevel */
  1896.  
  1897.   for (Index = strlen (ConInfo->Info) - 1 ; ConInfo->Info[Index] == ' ' && Index > 0 ; Index --)           /* Cut trailing spaces */
  1898.     ConInfo->Info[Index] = '\0';
  1899.   if (NoFullName)                                                                   /* Convert full names to short name if wanted */
  1900.   {
  1901.     if (!strcmp (ConInfo->Info, IdColors[MAXCOLORS + 1]))
  1902.       sprintf (ConInfo->Info, "%s%s", NoLevel, IdColors[MAXCOLORS]);
  1903.     else
  1904.       if (!strcmp (ConInfo->Info, IdDemos[MAXDEMOS + 1]))
  1905.         sprintf (ConInfo->Info, "%s%s", NoLevel, IdDemos[MAXDEMOS]);
  1906.       else
  1907.         if (!strcmp (ConInfo->Info, IdSounds[MAXSOUNDS + 1]))
  1908.           sprintf (ConInfo->Info, "%s%s", NoLevel, IdSounds[MAXSOUNDS]);
  1909.         else
  1910.           if (!strcmp (ConInfo->Info, IdMusic[MAXMUSIC + 1]))
  1911.             sprintf (ConInfo->Info, "%s%s", NoLevel, IdMusic[MAXMUSIC]);
  1912.           else
  1913.             if (!strcmp (ConInfo->Info, IdSprites[MAXSPRITES + 1]))
  1914.               sprintf (ConInfo->Info, "%s%s", NoLevel, IdSprites[MAXSPRITES]);
  1915.             else
  1916.               if (!strcmp (ConInfo->Info, IdGraphics[MAXGRAPHS + 1]))
  1917.                 sprintf (ConInfo->Info, "%s%s", NoLevel, IdGraphics[MAXGRAPHS]);
  1918.   }
  1919.   else
  1920.   {
  1921.     SLNoLevel = strlen (NoLevel);                                                         /* Just to speed things up a little ... */
  1922.     if (!strncmp (ConInfo->Info, NoLevel, SLNoLevel))
  1923.     {
  1924.       if (!strcmp (ConInfo->Info + SLNoLevel, IdColors[MAXCOLORS]))
  1925.         strcpy (ConInfo->Info, IdColors[MAXCOLORS + 1]);
  1926.       else
  1927.         if (!strcmp (ConInfo->Info + SLNoLevel, IdDemos[MAXDEMOS]))
  1928.           strcpy (ConInfo->Info, IdDemos[MAXDEMOS + 1]);
  1929.         else
  1930.           if (!strcmp (ConInfo->Info + SLNoLevel, IdSounds[MAXSOUNDS]))
  1931.             strcpy (ConInfo->Info, IdSounds[MAXSOUNDS + 1]);
  1932.           else
  1933.             if (!strcmp (ConInfo->Info + SLNoLevel, IdMusic[MAXMUSIC]))
  1934.               strcpy (ConInfo->Info, IdMusic[MAXMUSIC + 1]);
  1935.             else
  1936.               if (!strcmp (ConInfo->Info + SLNoLevel, IdSprites[MAXSPRITES]))
  1937.                 strcpy (ConInfo->Info, IdSprites[MAXSPRITES + 1]);
  1938.               else
  1939.                 if (!strcmp (ConInfo->Info + SLNoLevel, IdGraphics[MAXGRAPHS]))
  1940.                   strcpy (ConInfo->Info, IdGraphics[MAXGRAPHS + 1]);
  1941.       }
  1942.     }
  1943.   for (Index = strlen (ConInfo->Info) ; Index < MAXINFOLEN ; Index ++)                              /* Fill info field to maximum */
  1944.     ConInfo->Info[Index] = ' ';
  1945.   ConInfo->Info[Index] = '\0';
  1946. }
  1947.  
  1948. void _fastcall HandleFileSort (boolean Toggle)
  1949.  
  1950. /**********************************************************************************************************************************/
  1951. /* Pre   : 'Toggle' is TRUE if the sort criterium should be toggled (when called from ToggleFileSort), FALSE if not (when called  */
  1952. /*         from RescanFiles). A requester border should have been previously drawn, together with the top text line.              */
  1953. /* Post  : The question is finished in the requester. The user must confirm the question. If confirmed, then the files are sorted */
  1954. /*         according to 'Toggle'. If this was TRUE, then the criterium is toggled from "NAME" to "INFO" first. Afterward the      */
  1955. /*         requester has been removed and the files reprinted (if the sort was done, then page 1 is automatically selected).      */
  1956. /* Import: WaitForConfirmation, PrintWadFiles, ShowMouse, SortFiles, PrText, PrintPagers.                                         */
  1957. /**********************************************************************************************************************************/
  1958.  
  1959. {
  1960.   PrText (-1, 20, 32, LRED, "RESORT THE FILES ?");
  1961.   PrText (-1, 22, 31, DRED, "PRESS <Y> TO CONFIRM");
  1962.   if (!WaitForConfirmation ())                                                                                  /* Acknowledged ? */
  1963.   {                                                                                                            /* Step out if not */
  1964.     for (M = 16 ; M < 25 ; M ++)                                                                               /* Erase requester */
  1965.       PrText (-1, M, 26, DBLACK, "                                ");
  1966.     PrintWadFiles ();
  1967.     ShowMouse ();
  1968.     return;
  1969.   }
  1970.   if (Toggle)
  1971.   {
  1972.     SortWadFiles = TRUE;
  1973.     SortByName = !SortByName;                                                                 /* Toggle between "NAME" and "INFO" */
  1974.   }
  1975.   PrText (-1, 18, 29, DRED, "                        ");
  1976.   PrText (-1, 20, 32, LRED, "    Sorting ...   ");
  1977.   PrText (-1, 22, 31, DRED, "                    ");
  1978.   SortFiles ();
  1979.   for (M = 16 ; M < 25 ; M ++)                                                                                 /* Erase requester */
  1980.     PrText (-1, M, 26, DBLACK, "                                ");
  1981.   CurrentPage = 0;
  1982.   CurrentSelected = 0;
  1983.   PrintPagers (0);
  1984.   PrintWadFiles ();
  1985.   ShowMouse ();
  1986. }
  1987.  
  1988. void ToggleFileSort (void)
  1989.  
  1990. /**********************************************************************************************************************************/
  1991. /* Pre   : The user pressed [F8].                                                                                                 */
  1992. /* Post  : A requester has been drawn to confirm the action. If this was acknowledged, then the file sort criterium has been      */
  1993. /*         toggled, the files have been resorted and reprinted.                                                                   */
  1994. /* Import: Requester, PrText, HandleFileSort.                                                                                     */
  1995. /**********************************************************************************************************************************/
  1996.  
  1997. {
  1998.   Requester (15, 25, 11, 32);
  1999.   PrText (-1, 18, 29, DRED, "ARE YOU SURE YOU WANT TO");
  2000.   HandleFileSort (TRUE);
  2001. }
  2002.  
  2003. void RescanFiles (void)
  2004.  
  2005. /**********************************************************************************************************************************/
  2006. /* Pre   : The user pressed [F5].                                                                                                 */
  2007. /* Post  : A requester has been drawn to confirm the action. If this was acknowledged, then all files that are currently in       */
  2008. /*         memory have been rescanned, and the result has been written to the WADINFOFILE. After this, the user has been asked if */
  2009. /*         the files should be resorted. At the end, all files are reprinted on screen.                                           */
  2010. /* Import: Requester, PrText, HandleFileSort, GetWadInfo, WriteWadInfo, WaitForConfirmation, ShowMouse, ConvertFullName.          */
  2011. /**********************************************************************************************************************************/
  2012.  
  2013. {
  2014.   int register CountWadFiles;
  2015.  
  2016.   Requester (15, 25, 11, 32);
  2017.   PrText (-1, 18, 29, DRED, "ARE YOU SURE YOU WANT TO");
  2018.   PrText (-1, 20, 32, LRED, "RESCAN THE FILES ?");
  2019.   PrText (-1, 22, 31, DRED, "PRESS <Y> TO CONFIRM");
  2020.   if (!WaitForConfirmation ())                                                                                  /* Acknowledged ? */
  2021.   {                                                                                                            /* Step out if not */
  2022.     for (M = 16 ; M < 25 ; M ++)                                                                               /* Erase requester */
  2023.       PrText (-1, M, 26, DBLACK, "                                ");
  2024.     PrintWadFiles ();
  2025.     ShowMouse ();
  2026.     return;
  2027.   }
  2028.   PrText (-1, 18, 29, DRED, "                        ");
  2029.   PrText (-1, 20, 32, LRED, "   Searching ...  ");
  2030.   PrText (-1, 22, 31, DRED, "                    ");
  2031.   for (CountWadFiles = 0 ; CountWadFiles < TotalWads ; CountWadFiles ++)
  2032.     GetWadInfo (CountWadFiles, TRUE);                                                     /* Collect information on each WAD file */
  2033.   PrText (-1, 20, 29, LRED, "Writing WADINFOFILE ... ");
  2034.   WriteWadInfo (InfoFile);                                                                                    /* Write the result */
  2035.   for (CountWadFiles = 0 ; CountWadFiles < TotalWads ; CountWadFiles ++)
  2036.     ConvertFullName (WadInfo[CountWadFiles]);                                      /* Convert full names to short names if wanted */
  2037.   PrText (-1, 20, 29, LRED, "                        ");
  2038.   PrText (-1, 18, 34, DRED, "DO YOU WANT TO");
  2039.   HandleFileSort (FALSE);                                                                         /* Handle sorting of the result */
  2040. }
  2041.  
  2042. void ToggleFullName (void)
  2043.  
  2044. /**********************************************************************************************************************************/
  2045. /* Pre   : The user pressed [F7].                                                                                                 */
  2046. /* Post  : A requester has been drawn to confirm the action. If this was acknowledged, then the full name criterium has been      */
  2047. /*         toggled, the files have been resorted and reprinted.                                                                   */
  2048. /* Import: Requester, PrText, HandleFileSort.                                                                                     */
  2049. /**********************************************************************************************************************************/
  2050.  
  2051. {
  2052.   int register CountWadFiles;
  2053.  
  2054.   Requester (15, 25, 11, 32);
  2055.   PrText (-1, 18, 29, DRED, "ARE YOU SURE YOU WANT TO");
  2056.   PrText (-1, 20, 31, LRED, "CONVERT FULL NAMES ?");
  2057.   PrText (-1, 22, 31, DRED, "PRESS <Y> TO CONFIRM");
  2058.   if (!WaitForConfirmation ())                                                                                  /* Acknowledged ? */
  2059.   {                                                                                                            /* Step out if not */
  2060.     for (M = 16 ; M < 25 ; M ++)                                                                               /* Erase requester */
  2061.       PrText (-1, M, 26, DBLACK, "                                ");
  2062.     PrintWadFiles ();
  2063.     ShowMouse ();
  2064.     return;
  2065.   }
  2066.   PrText (-1, 18, 29, DRED, "                        ");
  2067.   PrText (-1, 20, 31, LRED, "   Converting ...   ");
  2068.   PrText (-1, 22, 31, DRED, "                    ");
  2069.   NoFullName = !NoFullName;                                                                             /* Toggle NoFullName flag */
  2070.   for (CountWadFiles = 0 ; CountWadFiles < TotalWads ; CountWadFiles ++)
  2071.     ConvertFullName (WadInfo[CountWadFiles]);
  2072.   PrText (-1, 20, 31, DRED, "                    ");
  2073.   PrText (-1, 18, 34, DRED, "DO YOU WANT TO");
  2074.   HandleFileSort (FALSE);                                                                         /* Handle sorting of the result */
  2075. }
  2076.  
  2077. void DeleteWadFile (void)
  2078.  
  2079. /**********************************************************************************************************************************/
  2080. /* Pre   : None.                                                                                                                  */
  2081. /* Post  : The user has been asked permission to delete the hi-lighted file. If he/she comfirmed, then the file has been deleted  */
  2082. /*         from disk and memory. If the file is protected on disk, then nothing is done.                                          */
  2083. /* Import: Requester, PrintWadFiles, ShowMouse, UnselectPreviousWad, WaitForConfirmation.                                         */
  2084. /**********************************************************************************************************************************/
  2085.  
  2086. {
  2087.   int register DeleteWadNumber;
  2088.  
  2089.   if (CurrentField != FILEFIELD)                                                       /* Return immediately if no file appointed */
  2090.     return;
  2091.   DeleteWadNumber = CurrentPage * PAGE + CurrentSelected - 1;                                         /* Determine the WAD number */
  2092.   Requester (15, 25, 11, 32);
  2093.   PrText (-1, 18, 29, DRED, "ARE YOU SURE YOU WANT TO");
  2094.   sprintf (S, "DELETE %s", WadInfo[DeleteWadNumber]->Name);
  2095.   for (M = strlen (S) - 1 ; S[M] == ' ' ; M --)
  2096.     S[M] = '\0';
  2097.   strcat (S, " ?");
  2098.   PrText (-1, 20, 40 - (strlen (S) / 2), LRED, S);
  2099.   PrText (-1, 22, 31, DRED, "PRESS <Y> TO CONFIRM");
  2100.   if (!WaitForConfirmation ())                                                                                  /* Acknowledged ? */
  2101.   {                                                                                                            /* Step out if not */
  2102.     for (M = 16 ; M < 25 ; M ++)                                                                               /* Erase requester */
  2103.       PrText (-1, M, 26, DBLACK, "                                ");
  2104.     PrintWadFiles ();
  2105.     ShowMouse ();
  2106.     return;
  2107.   }
  2108.   for (M = 16 ; M < 25 ; M ++)                                                                                 /* Erase requester */
  2109.     PrText (-1, M, 26, DBLACK, "                                       ");
  2110.   if (WadInfo[DeleteWadNumber]->Drive)                                                         /* Build complete path to the file */
  2111.     sprintf (S, "%c:", ToName (WadInfo[DeleteWadNumber]->Drive));
  2112.   else
  2113.     sprintf (S, "%c:", ToName (CurDrive));
  2114.   if (!strcmp (WadInfo[DeleteWadNumber]->Path, DEFAULTWADDIR))
  2115.     strcat (S, CurPath + 2);
  2116.   else
  2117.     strcat (S, WadInfo[DeleteWadNumber]->Path);
  2118.   strcat (S, "\\");
  2119.   strcat (S, WadInfo[DeleteWadNumber]->OrigName);
  2120.   if (_dos_setfileattr (S, _A_NORMAL))                                                    /* Clear any preventing file attributes */
  2121.   {                                                                                  /* Step out if protected from a higher level */
  2122.     PrintWadFiles ();
  2123.     ShowMouse ();
  2124.     return;
  2125.   }
  2126.   if (remove (S) == -1)                                                                          /* Now remove the file from disk */
  2127.   {                                                                                  /* Step out if protected from a higher level */
  2128.     PrintWadFiles ();
  2129.     ShowMouse ();
  2130.     return;
  2131.   }
  2132.   _ffree (WadInfo[DeleteWadNumber]);                                                               /* Remove the file from memory */
  2133.   for (M = DeleteWadNumber ; M < TotalWads ; M ++)                                          /* Move all files thereafter one back */
  2134.     WadInfo[M] = WadInfo[M + 1];
  2135.   TotalWads --;
  2136.   PrintWadFiles ();                                                                                           /* Print result ... */
  2137.   ShowMouse ();
  2138. }
  2139.  
  2140. void _fastcall HandleFile (unsigned int Key)
  2141.  
  2142. /**********************************************************************************************************************************/
  2143. /* Pre   : 'Key' holds the preesed (raw) key code that caused calling this routine; if (global) 'UseMouse' was FALSE.             */
  2144. /*         Considered keys are the cursor keys and space. If 'UseMouse' was TRUE, than 'Key' is always 0x0000 (dummy).            */
  2145. /* Post  : The mouse pointer was at the bottom of the screen. The pointed filename has been highlighted. If the right mousebutton */
  2146. /*         was pressed, then the file is selected, which is shown by a different color. If the file was already selected, then it */
  2147. /*         is now deselected. Selection can only be done once on a button press. To invert the selection, the mouse button must   */
  2148. /*         first be released, and then pressed again.                                                                             */
  2149. /* Import: HideMouse, ShowMouse, UnselectPreviousField.                                                                           */
  2150. /**********************************************************************************************************************************/
  2151.  
  2152. {
  2153.   int register PositionX;
  2154.   int register PositionY;
  2155.   int register OldWadNumber;
  2156.   int register NewWadNumber;
  2157.  
  2158.   UnselectPreviousField (FILEFIELD);
  2159.   PositionY = 13 + ((PreviousWad - 1) % WADHEIGHT);                                        /* Location of previously selected WAD */
  2160.   PositionX = ((PreviousWad - 1) / WADHEIGHT) * WADWIDTH + 1;
  2161.   OldWadNumber = CurrentPage * PAGE + PreviousWad - 1;                                                      /* Number of that WAD */
  2162.   CurrentSelected = 0;                                                                              /* Signal: no file pointed to */
  2163.   if (UseMouse)
  2164.   {
  2165.     if (Mouse.Xx <= 7)                                                                               /* Determine the file column */
  2166.       CurrentSelected = Mouse.Yy - 11;
  2167.     else
  2168.       if (Mouse.Xx >= 27 && Mouse.Xx <= 34)
  2169.         CurrentSelected = Mouse.Yy - 11 + WADHEIGHT;
  2170.       else
  2171.         if (Mouse.Xx >= 54 && Mouse.Xx <= 61)
  2172.           CurrentSelected = Mouse.Yy - 11 + (2 * WADHEIGHT);
  2173.     if (CurrentPage * PAGE + CurrentSelected > TotalWads)                                                    /* Empty screen part */
  2174.       CurrentSelected = -1;
  2175.     MouseHidden = FALSE;
  2176.     if (CurrentField == FILEFIELD && Mouse.CoChange)                       /* Only unhighlite the previous one if the mouse moved */
  2177.     {
  2178.       HideMouse ();
  2179.       MouseHidden = TRUE;                                                                         /* Signal: mouse pointer hidden */
  2180.       if (WadInfo[OldWadNumber]->Selected)
  2181.         PrText (-1, PositionY, PositionX, DGREEN, WadInfo[OldWadNumber]->Name);
  2182.       else
  2183.         PrText (-1, PositionY, PositionX, DWHITE, WadInfo[OldWadNumber]->Name);
  2184.     }
  2185.   }
  2186.   else
  2187.   {
  2188.     switch (Key)
  2189.     {
  2190.       case KEY_CURSLEFT   : if (PreviousWad > WADHEIGHT)                                                 /* Possible to go left ? */
  2191.                               CurrentSelected = PreviousWad - WADHEIGHT;
  2192.                             else                                                          /* Determine the far right and go there */
  2193.                               if ((CurrentPage * PAGE + PreviousWad + 2 * WADHEIGHT) <= TotalWads)
  2194.                                 CurrentSelected = PreviousWad + 2 * WADHEIGHT;                                       /* 3 columns */
  2195.                               else
  2196.                                 if ((CurrentPage * PAGE + PreviousWad + WADHEIGHT) <= TotalWads)                     /* 2 columns */
  2197.                                   CurrentSelected = PreviousWad + WADHEIGHT;
  2198.                                 else                                                           /* Only 1 column; no move possible */
  2199.                                   CurrentSelected = PreviousWad;
  2200.                             break;
  2201.       case KEY_CURSRIGHT  : if ((PreviousWad < 2 * WADHEIGHT) && (CurrentPage * PAGE + PreviousWad + WADHEIGHT) <= TotalWads)
  2202.                               CurrentSelected = PreviousWad + WADHEIGHT;
  2203.                             else
  2204.                               CurrentSelected = PreviousWad % WADHEIGHT;
  2205.                             break;
  2206.       case KEY_CURSUP     : if (((PreviousWad - 1) % WADHEIGHT) > 0)
  2207.                               CurrentSelected = PreviousWad - 1;
  2208.                             else
  2209.                               if ((CurrentPage * PAGE + PreviousWad + WADHEIGHT) <= TotalWads)
  2210.                                 CurrentSelected = PreviousWad - 1 + WADHEIGHT;
  2211.                               else
  2212.                                 CurrentSelected = TotalWads % PAGE;
  2213.                             break;
  2214.       case KEY_CURSDOWN   : if (((PreviousWad - 1) % WADHEIGHT) < (WADHEIGHT - 1))
  2215.                               if ((CurrentPage * PAGE + PreviousWad + 1) <= TotalWads)
  2216.                                 CurrentSelected = PreviousWad + 1;
  2217.                               else
  2218.                                 CurrentSelected = (TotalWads % PAGE) - (TotalWads % WADHEIGHT) + 1;
  2219.                             else
  2220.                               CurrentSelected = PreviousWad + 1 - WADHEIGHT;
  2221.                             break;
  2222.       case KEY_SELECTFILE : CurrentSelected = PreviousWad;
  2223.     }
  2224.     if (Key != KEY_SELECTFILE)                                                                     /* Unhighlite the previous one */
  2225.       if (WadInfo[OldWadNumber]->Selected)
  2226.         PrText (-1, PositionY, PositionX, DGREEN, WadInfo[OldWadNumber]->Name);
  2227.       else
  2228.         PrText (-1, PositionY, PositionX, DWHITE, WadInfo[OldWadNumber]->Name);
  2229.   }
  2230.   NewWadNumber = CurrentPage * PAGE + CurrentSelected - 1;
  2231.   if ((UseMouse && (Mouse.Left && !Mouse.LeftStillPressed && CurrentSelected > 0))                      /* Mouse button pressed ? */
  2232.       || (Key == KEY_SELECTFILE))
  2233.   {
  2234.     WadInfo[NewWadNumber]->Selected = !WadInfo[NewWadNumber]->Selected;                                       /* Invert selection */
  2235.     SelectionChange = TRUE;                                                                               /* Signal: screenchange */
  2236.   }
  2237.   else
  2238.     SelectionChange = FALSE;
  2239.   if (CurrentSelected > 0)                                                                    /* A (valid) new file is pointed to */
  2240.   {
  2241.     if (Mouse.CoChange || SelectionChange || !UseMouse)                                                  /* Color change needed ? */
  2242.     {
  2243.       PositionY = 13 + ((CurrentSelected - 1) % WADHEIGHT);
  2244.       PositionX = ((CurrentSelected - 1) / WADHEIGHT) * WADWIDTH + 1;
  2245.       if (!MouseHidden)                                                                   /* Hide the mouse if not hidden already */
  2246.         HideMouse ();
  2247.       MouseHidden = TRUE;
  2248.       if (WadInfo[NewWadNumber]->Selected)                                                                 /* 'Draw' highlite bar */
  2249.         PrText (-1, PositionY, PositionX, LGREEN, WadInfo[NewWadNumber]->Name);
  2250.       else
  2251.         PrText (-1, PositionY, PositionX, LRED, WadInfo[NewWadNumber]->Name);
  2252.     }
  2253.     CurrentField = FILEFIELD;
  2254.     PreviousWad = CurrentSelected;
  2255.   }
  2256.   else
  2257.     CurrentField = NOFIELD;
  2258.   if (MouseHidden)                                                                         /* Reprint the mouse pointer if needed */
  2259.     ShowMouse ();
  2260. }
  2261.  
  2262. void _fastcall HandleDifficulty (boolean KeyInput)
  2263.  
  2264. /**********************************************************************************************************************************/
  2265. /* Pre   : 'KeyInput' is TRUE if this routine was called because of a keypress.                                                   */
  2266. /* Post  : The mouse pointer was at the difficulty block. The pointed difficulty has been highlited. If the mouse button was      */
  2267. /*         pressed then this difficulty is selected (and the previous automatically de-selected). The new result is reprinted.    */
  2268. /* Import: HideMouse, ShowMouse, PrintDifficulties, UnselectPreviousField.                                                        */
  2269. /**********************************************************************************************************************************/
  2270.  
  2271. {
  2272.   int register MaxDiff;
  2273.  
  2274.   HideMouse ();
  2275.   if (!KeyInput)
  2276.     UnselectPreviousField (DIFFICULTYFIELD);
  2277.   if (Mouse.Left && !Mouse.LeftStillPressed)
  2278.     DifficultyActive = Mouse.Yy - 1;
  2279.   else
  2280.     if (KeyInput)
  2281.       if (++ DifficultyActive > NUMDIFFICULTY)
  2282.         DifficultyActive = 1;
  2283.   if (KeyInput)
  2284.     if (CurrentField == DIFFICULTYFIELD)
  2285.       PrintDifficulties (Mouse.Yy - 1);
  2286.     else
  2287.       PrintDifficulties (0);
  2288.   else
  2289.   {
  2290.     PrintDifficulties (Mouse.Yy - 1);
  2291.     CurrentField = DIFFICULTYFIELD;
  2292.   }
  2293.   ShowMouse ();
  2294. }
  2295.   
  2296. void _fastcall HandlePlayType (boolean KeyInput, char Key)
  2297.  
  2298. /**********************************************************************************************************************************/
  2299. /* Pre   : 'KeyInput' is TRUE if this routine was called because of a keypress.                                                   */
  2300. /*         If it was TRUE, then 'Key' holds the (ASCII) keyvalue, otherwise 'Key' holds 0x00 (dummy).                             */
  2301. /* Post  : The mouse pointer was at the playtype block. The pointed playtype has been highlited. If the mouse button was pressed  */
  2302. /*         then this playtype is selected (and the previous automatically de-selected). The new result is reprinted.              */
  2303. /*         If the mouse pointer was at the fourth line, than the PlayType parameter of the current PlayType is handled.           */
  2304. /* Import: HideMouse, ShowMouse, PrintPlayTypes, UnselectPreviousField.                                                           */
  2305. /**********************************************************************************************************************************/
  2306.  
  2307. {
  2308.   HideMouse ();
  2309.   if (!KeyInput)
  2310.     UnselectPreviousField (PLAYTYPEFIELD);
  2311.   if (KeyInput)
  2312.   {
  2313.     switch (Key)
  2314.     {
  2315.       case KEY_PLAYTYPE : if (++ PlayTypeActive > NUMPLAYTYPE)
  2316.                             PlayTypeActive = 1;
  2317.                           break;
  2318.       case KEY_NODES    : if (PlayTypeActive == 2 && !OtherIpxDriver)                                           /* IPX compatible */
  2319.                             if (++ NumNodesActive == 5)                                             /* Increase number of players */
  2320.                               NumNodesActive = 2;                                                      /* Must be between 2 and 4 */
  2321.                           break;
  2322.       case KEY_COMPORT  : if (PlayTypeActive == 3 && !OtherSerDriver)                                          /* Null-modem link */
  2323.                             if (++ CommPortActive == 5)                                               /* Increase COM port number */
  2324.                               CommPortActive = 1;                                                      /* Must be between 1 and 4 */
  2325.     }
  2326.     if (CurrentField == PLAYTYPEFIELD)
  2327.       PrintPlayTypes (Mouse.Yy - 1);
  2328.     else
  2329.       PrintPlayTypes (0);
  2330.   }
  2331.   else                                                                                                             /* Mouse input */
  2332.   {
  2333.     if (Mouse.Left && !Mouse.LeftStillPressed)
  2334.       if (Mouse.Yy <= NUMPLAYTYPE + 1)                                                                       /* Change PlayType ? */
  2335.         PlayTypeActive = Mouse.Yy - 1;
  2336.       else
  2337.         if (PlayTypeActive == 2 && !OtherIpxDriver)                                                             /* IPX compatible */
  2338.         {
  2339.           if (++ NumNodesActive == 5)                                                               /* Increase number of players */
  2340.             NumNodesActive = 2;                                                                        /* Must be between 2 and 4 */
  2341.         }
  2342.         else
  2343.           if (PlayTypeActive == 3 && !OtherSerDriver)                                                         /* Null-modem link */
  2344.           {
  2345.             if (++ CommPortActive == 5)                                                               /* Increase COM port number */
  2346.               CommPortActive = 1;                                                                      /* Must be between 1 and 4 */
  2347.           }
  2348.     PrintPlayTypes (Mouse.Yy - 1);
  2349.     CurrentField = PLAYTYPEFIELD;
  2350.   }
  2351.   ShowMouse ();
  2352. }
  2353.   
  2354. void _fastcall HandleRespawnMonsters (boolean KeyInput)
  2355.  
  2356. /**********************************************************************************************************************************/
  2357. /* Pre   : 'KeyInput' is TRUE if this routine was called because of a keypress.                                                   */
  2358. /* Post  : The mouse pointer was at the respawn item. This item has been highlited. If the mouse button was pressed, then the     */
  2359. /*         active value is inverted. The new result is reprinted.                                                                 */
  2360. /* Import: HideMouse, ShowMouse, PrintRespawnMonsters, PrintNoMonsters, UnselectPreviousField.                                    */
  2361. /**********************************************************************************************************************************/
  2362.  
  2363. {
  2364.   HideMouse ();
  2365.   if (!KeyInput)
  2366.     UnselectPreviousField (RESPAWNFIELD);
  2367.   if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput)
  2368.     if (RespMonstersOn = !RespMonstersOn)                                                              /* Toggle the RESPAWN item */
  2369.       NoMonstersOn = FALSE;
  2370.   if (KeyInput)
  2371.   {
  2372.     PrintRespawnMonsters ((boolean)(CurrentField == RESPAWNFIELD));
  2373.     PrintNoMonsters ((boolean)(CurrentField == NOMONSTERSFIELD));
  2374.   }
  2375.   else
  2376.   {
  2377.     PrintRespawnMonsters (TRUE);
  2378.     PrintNoMonsters (FALSE);
  2379.     CurrentField = RESPAWNFIELD;
  2380.   }
  2381.   ShowMouse ();
  2382. }
  2383.  
  2384. void _fastcall HandleNoMonsters (boolean KeyInput)
  2385.  
  2386. /**********************************************************************************************************************************/
  2387. /* Pre   : 'KeyInput' is TRUE if this routine was called because of a keypress.                                                   */
  2388. /* Post  : The mouse pointer was at the nomonsters item. This item has been highlited. If the mouse button was pressed, then the  */
  2389. /*         active value is inverted. The new result is reprinted.                                                                 */
  2390. /* Import: HideMouse, ShowMouse, PrintNoMonsters, PrintRespawnMonsters, PrintFastMonsters, UnselectPreviousField.                 */
  2391. /**********************************************************************************************************************************/
  2392.  
  2393. {
  2394.   HideMouse ();
  2395.   if (!KeyInput)
  2396.     UnselectPreviousField (NOMONSTERSFIELD);
  2397.   if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput)
  2398.     if (NoMonstersOn = !NoMonstersOn)                                                               /* Toggle the NOMONSTERS item */
  2399.     {
  2400.       RespMonstersOn = FALSE;                                                                       /* These are contradictionary */
  2401.       FastMonstersOn = FALSE;
  2402.     }
  2403.   if (KeyInput)
  2404.   {
  2405.     PrintNoMonsters ((boolean)(CurrentField == NOMONSTERSFIELD));
  2406.     PrintRespawnMonsters ((boolean)(CurrentField == RESPAWNFIELD));
  2407.     PrintFastMonsters ((boolean)(CurrentField == FASTMONSTERSFIELD));
  2408.   }
  2409.   else
  2410.   {
  2411.     PrintNoMonsters (TRUE);
  2412.     PrintRespawnMonsters (FALSE);
  2413.     PrintFastMonsters (FALSE);
  2414.     CurrentField = NOMONSTERSFIELD;
  2415.   }
  2416.   ShowMouse ();
  2417. }
  2418.  
  2419. void _fastcall HandleFastMonsters (boolean KeyInput)
  2420.  
  2421. /**********************************************************************************************************************************/
  2422. /* Pre   : 'KeyInput' is TRUE if this routine was called because of a keypress.                                                   */
  2423. /* Post  : The mouse pointer was at the fastmonsters item. This item has been highlited. If the mouse button was pressed, then    */
  2424. /*         the active value is inverted. The new result is reprinted.                                                             */
  2425. /* Import: HideMouse, ShowMouse, PrintFastMonsters, PrintNoMonsters, UnselectPreviousField.                                       */
  2426. /**********************************************************************************************************************************/
  2427.  
  2428. {
  2429.   HideMouse ();
  2430.   if (!KeyInput)
  2431.     UnselectPreviousField (FASTMONSTERSFIELD);
  2432.   if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput)
  2433.     if (FastMonstersOn = !FastMonstersOn)                                                         /* Toggle the FASTMONSTERS item */
  2434.       NoMonstersOn = FALSE;                                                                         /* These are contradictionary */
  2435.   if (KeyInput)
  2436.   {
  2437.     PrintFastMonsters ((boolean)(CurrentField == FASTMONSTERSFIELD));
  2438.     PrintNoMonsters ((boolean)(CurrentField == NOMONSTERSFIELD));
  2439.   }
  2440.   else
  2441.   {
  2442.     PrintFastMonsters (TRUE);
  2443.     PrintNoMonsters (FALSE);
  2444.     CurrentField = FASTMONSTERSFIELD;
  2445.   }
  2446.   ShowMouse ();
  2447. }
  2448.  
  2449. void _fastcall HandleDeathmatch (boolean KeyInput)
  2450.  
  2451. /**********************************************************************************************************************************/
  2452. /* Pre   : 'KeyInput' is TRUE if this routine was called because of a keypress.                                                   */
  2453. /* Post  : The mouse pointer was at the deathmatch item. This item has been highlited. If the mouse button was pressed, then the  */
  2454. /*         active value is inverted. The new result is reprinted.                                                                 */
  2455. /* Import: HideMouse, ShowMouse, PrintDeathmatch, UnselectPreviousField, PrintV2Deathmatch.                                       */
  2456. /**********************************************************************************************************************************/
  2457.  
  2458. {
  2459.   HideMouse ();
  2460.   if (!KeyInput)
  2461.     UnselectPreviousField (DEATHMATCHFIELD);
  2462.   if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput)
  2463.     if (DeathmatchOn = !DeathmatchOn)                                                               /* Toggle the DEATHMATCH item */
  2464.       DeathmatchV2On = FALSE;
  2465.   if (KeyInput)
  2466.   {
  2467.     PrintDeathmatch ((boolean)(CurrentField == DEATHMATCHFIELD));
  2468.     PrintV2Deathmatch ((boolean)(CurrentField == DEATHMATCHV2FIELD));
  2469.   }
  2470.   else
  2471.   {
  2472.     PrintDeathmatch (TRUE);
  2473.     PrintV2Deathmatch (FALSE);
  2474.     CurrentField = DEATHMATCHFIELD;
  2475.   }
  2476.   ShowMouse ();
  2477. }
  2478.   
  2479. void _fastcall HandleV2Deathmatch (boolean KeyInput)
  2480.  
  2481. /**********************************************************************************************************************************/
  2482. /* Pre   : 'KeyInput' is TRUE if this routine was called because of a keypress.                                                   */
  2483. /* Post  : The mouse pointer was at the deathmatch item. This item has been highlited. If the mouse button was pressed, then the  */
  2484. /*         active value is inverted. The new result is reprinted.                                                                 */
  2485. /* Import: HideMouse, ShowMouse, PrintDeathmatch, UnselectPreviousField, PrintV2Deathmatch.                                       */
  2486. /**********************************************************************************************************************************/
  2487.  
  2488. {
  2489.   HideMouse ();
  2490.   if (!KeyInput)
  2491.     UnselectPreviousField (DEATHMATCHV2FIELD);
  2492.   if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput)
  2493.     if (DeathmatchV2On = !DeathmatchV2On)
  2494.       DeathmatchOn = FALSE;
  2495.   if (KeyInput)
  2496.   {
  2497.     PrintV2Deathmatch ((boolean)(CurrentField == DEATHMATCHV2FIELD));
  2498.     PrintDeathmatch ((boolean)(CurrentField == DEATHMATCHFIELD));
  2499.   }
  2500.   else
  2501.   {
  2502.     PrintV2Deathmatch (TRUE);
  2503.     PrintDeathmatch (FALSE);
  2504.     CurrentField = DEATHMATCHV2FIELD;
  2505.   }
  2506.   ShowMouse ();
  2507. }
  2508.   
  2509. void _fastcall HandleNextLevel (boolean KeyInput)
  2510.  
  2511. /**********************************************************************************************************************************/
  2512. /* Pre   : 'KeyInput' is TRUE if this routine was called because of a keypress.                                                   */
  2513. /* Post  : The mouse pointer was at the nextlevel item. This item has been highlited. If the mouse button was pressed, then a     */
  2514. /*         test is made if there is a next level. If there is one, then the active level is increased.                            */
  2515. /* Import: HideMouse, ShowMouse, PrintLevel, PrintLevelPagers, UnselectPreviousField.                                             */
  2516. /**********************************************************************************************************************************/
  2517.  
  2518. {
  2519.   if (CurrentLevel == NUMLEVEL)
  2520.     return;
  2521.   HideMouse ();
  2522.   if (!KeyInput)
  2523.     UnselectPreviousField (LEVELFIELD);
  2524.   if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput)
  2525.   {
  2526.     CurrentLevel ++;                                                                                    /* Increase current level */
  2527.     PrintLevel ();
  2528.   }
  2529.   if (!KeyInput)
  2530.   {
  2531.     PrintLevelPagers (2);
  2532.     CurrentField = LEVELFIELD;
  2533.   }
  2534.   else
  2535.     PrintLevelPagers (CurrentField == LEVELFIELD ? 2 : 0);
  2536.   ShowMouse ();
  2537. }
  2538.   
  2539. void _fastcall HandlePreviousLevel (boolean KeyInput)
  2540.  
  2541. /**********************************************************************************************************************************/
  2542. /* Pre   : 'KeyInput' is TRUE if this routine was called because of a keypress.                                                   */
  2543. /* Post  : The mouse pointer was at the previouslevel item. This item has been highlited. If the mouse button was pressed, then a */
  2544. /*         test is made if there is a previous level. If there is one, then the active level is decreased.                        */
  2545. /* Import: HideMouse, ShowMouse, PrintLevel, PrintLevelPagers, UnselectPreviousField.                                             */
  2546. /**********************************************************************************************************************************/
  2547.  
  2548. {
  2549.   if (CurrentLevel == 1)
  2550.     return;
  2551.   HideMouse ();
  2552.   if (!KeyInput)
  2553.     UnselectPreviousField (LEVELFIELD);
  2554.   if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput)
  2555.   {
  2556.     CurrentLevel --;                                                                                    /* Decrease current level */
  2557.     PrintLevel ();
  2558.   }
  2559.   if (!KeyInput)
  2560.   {
  2561.     PrintLevelPagers (1);
  2562.     CurrentField = LEVELFIELD;
  2563.   }
  2564.   else
  2565.     PrintLevelPagers (CurrentField == LEVELFIELD ? 1 : 0);
  2566.   ShowMouse ();
  2567. }
  2568.   
  2569. void _fastcall HandlePreviousPage (boolean KeyInput)
  2570.  
  2571. /**********************************************************************************************************************************/
  2572. /* Pre   : 'KeyInput' is TRUE if this routine is entered because the [PAGE UP] key was pressed.                                   */
  2573. /* Post  : The mouse pointer was at the previouspage item. This item has been highlited. If the mouse button was pressed, then    */
  2574. /*         a test is made if there are previous pages. If not, then the keyboard bell is sound, otherwise the previous page has   */
  2575. /*         been made the current. This new page has been printed.                                                                 */
  2576. /* Import: HideMouse, ShowMouse, PrintWadFiles, PrintPagers, UnselectPreviousField.                                               */
  2577. /**********************************************************************************************************************************/
  2578.  
  2579. {
  2580.   if (CurrentPage == 0)                                                                  /* Step out if there is no previous page */
  2581.     return;
  2582.   HideMouse ();
  2583.   if (!KeyInput)
  2584.     UnselectPreviousField (PAGERFIELD);
  2585.   if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput)
  2586.   {
  2587.     CurrentPage --;                                                                                             /* Go back a page */
  2588.     PrText (-1, 11, 69, LWHITE, "%2d", CurrentPage + 1);
  2589.     PrintWadFiles ();                                                                                      /* Print this new page */
  2590.     if (!UseMouse)
  2591.       PrText (-1, 13 + ((PreviousWad - 1) % WADHEIGHT),
  2592.                   ((PreviousWad - 1) / WADHEIGHT) * WADWIDTH + 1,
  2593.                   LRED, WadInfo[CurrentPage * PAGE + PreviousWad - 1]->Name);                  /* Hi-light 'new current' WAD file */
  2594.   }
  2595.   if (!KeyInput)
  2596.   {
  2597.     PrintPagers (1);
  2598.     CurrentField = PAGERFIELD;
  2599.   }
  2600.   else
  2601.     PrintPagers (CurrentField == PAGERFIELD ? 1 : 0);
  2602.   ShowMouse ();
  2603. }
  2604.  
  2605. void _fastcall HandleNextPage (boolean KeyInput)
  2606.  
  2607. /**********************************************************************************************************************************/
  2608. /* Pre   : 'KeyInput' is TRUE if this routine was called because the user pressed the [PAGE DOWN] key.                            */
  2609. /* Post  : The mouse pointer was at the nextpage item. This item has been highlited. If the mouse button was pressed, then a test */
  2610. /*         is made if there are next pages. If not, then the keyboard bell is sound, otherwise the next page has been made the    */
  2611. /*         current. This new page has been printed.                                                                               */
  2612. /* Import: HideMouse, ShowMouse, PrintWadFiles, PrintPagers, UnselectPreviousField.                                               */
  2613. /**********************************************************************************************************************************/
  2614.  
  2615. {
  2616.   if (CurrentPage == LastPage)                                                               /* Step out if there is no next page */
  2617.     return;
  2618.   HideMouse ();
  2619.   if (!KeyInput)
  2620.     UnselectPreviousField (PAGERFIELD);
  2621.   if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput)
  2622.   {
  2623.     CurrentPage ++;
  2624.     PrText (-1, 11, 69, LWHITE, "%2d", CurrentPage + 1);
  2625.     PrintWadFiles ();
  2626.     if (!UseMouse)
  2627.     {
  2628.       if ((CurrentPage * PAGE + PreviousWad - 1) >= TotalWads)              /* If not a full page, then test that the new pointed */
  2629.         PreviousWad = TotalWads % PAGE;                                    /* WAD file is valid, otherwise set it to the last one */
  2630.       PrText (-1, 13 + ((PreviousWad - 1) % WADHEIGHT),
  2631.                   ((PreviousWad - 1) / WADHEIGHT) * WADWIDTH + 1,
  2632.                   LRED, WadInfo[CurrentPage * PAGE + PreviousWad - 1]->Name);                  /* Hi-light 'new current' WAD file */
  2633.     }
  2634.   }
  2635.   if (!KeyInput)
  2636.   {
  2637.     PrintPagers (2);
  2638.     CurrentField = PAGERFIELD;
  2639.   }
  2640.   else
  2641.     PrintPagers (CurrentField == PAGERFIELD ? 2 : 0);
  2642.   ShowMouse ();
  2643. }
  2644.  
  2645. void _fastcall HandleReadPrev (boolean KeyInput)
  2646.  
  2647. /**********************************************************************************************************************************/
  2648. /* Pre   : 'KeyInput' is TRUE if this routine was called because of a keypress.                                                   */
  2649. /* Post  : The mouse pointer was at the read previous item. This item has been highlited. If the mouse button was pressed, then   */
  2650. /*         the file 'START.BAT' is read. Each item is set to the read value, all read WAD files are selected.                     */
  2651. /* Import: HideMouse, ShowMouse, PrintRdPrev, PrintDifficulties, PrintPlayTypes, PrintDeathmatch, PrintLevel, PrintLevelPagers,   */
  2652. /*         PrintWadFiles, UnselectPreviousField.                                                                                  */
  2653. /**********************************************************************************************************************************/
  2654.  
  2655. {
  2656.   FILE    *Fp;
  2657.   char     FileName[MAXFNAME];
  2658.   char     Directory[_MAX_DIR];
  2659.   char     Tmp[_MAX_DIR];
  2660.   char     DriveNum;
  2661.   boolean  Handled;
  2662.   boolean  Stop;
  2663.  
  2664.   HideMouse ();
  2665.   if (!KeyInput)
  2666.     UnselectPreviousField (RDPREVFIELD);
  2667.   if (!KeyInput)
  2668.   {
  2669.     PrintRdPrev (TRUE);
  2670.     CurrentField = RDPREVFIELD;
  2671.   }
  2672.   if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput)
  2673.   {
  2674.     Stop = FALSE;
  2675.     if (!(Fp = fopen (BATFILE, "r")))                                                                         /* Open "START.BAT" */
  2676.       Stop = TRUE;
  2677.     else
  2678.     {
  2679.       DifficultyActive = DEFAULTDIFFICULTY;
  2680.       PlayTypeActive = DEFAULTPLAYTYPE;
  2681.       CurrentLevel = DEFAULTLEVEL;
  2682.       DeathmatchOn = DEFAULTDMATCH;
  2683.       DeathmatchV2On = DEFAULTDMATCHV2;
  2684.       RespMonstersOn = DEFAULTRESPAWN;
  2685.       NoMonstersOn = DEFAULTNOMONSTERS;
  2686.       FastMonstersOn = DEFAULTFASTMONST;
  2687.       CommPortActive = DEFAULTCOMPORT;
  2688.       NumNodesActive = DEFAULTNODES;
  2689.       for (M = 0 ; M < TotalWads ; M ++)                                                                /* Unselect all WAD files */
  2690.         WadInfo[M]->Selected = FALSE;
  2691.       fscanf (Fp, "%s", S);                                                                             /* First read the command */
  2692.       while (!feof (Fp)
  2693.              && stricmp (S, STARTALONE)
  2694.              && stricmp (S, STARTIPX) && stricmp (S, IpxDriver)
  2695.              && stricmp (S, STARTLINK) && stricmp (S, SerDriver))
  2696.         fscanf (Fp, "%s", S);
  2697.       if (!stricmp (S, STARTALONE))
  2698.         PlayTypeActive = 1;
  2699.       else
  2700.         if (!stricmp (S, STARTIPX) || !stricmp (S, IpxDriver))
  2701.           PlayTypeActive = 2;
  2702.         else
  2703.             if (!stricmp (S, STARTLINK) || !stricmp (S, SerDriver))
  2704.               PlayTypeActive = 3;
  2705.             else
  2706.               Stop = TRUE;                                                                            /* Not a DOOM start command */
  2707.       if (!Stop)
  2708.       {
  2709.         fscanf (Fp, "%s", S);
  2710.         while (!feof (Fp) && !Stop)                                                                      /* Handle each parameter */
  2711.         {
  2712.           Handled = FALSE;
  2713.           if (S[0] == '@')                                                                           /* 'Response' file following */
  2714.           {
  2715.             fclose (Fp);                                                                                     /* Close "START.BAT" */
  2716.             if (!(Fp = fopen (S + 1, "r")))                                                           /* Open the 'Response' file */
  2717.               Stop = TRUE;                                                                     /* Stop if the file does not exist */
  2718.             else
  2719.               fscanf (Fp, "%s", S);                                                                /* Else: read the first string */
  2720.           }
  2721.           if (!feof (Fp) && !stricmp (S, NWSOCKET) && !Stop)                                                   /* Found '-SOCKET' */
  2722.           {
  2723.             fscanf (Fp, "%s", S);                                                           /* '-SOCKET' takes a parameter: 0-255 */
  2724.             if (feof (Fp))
  2725.               Stop = TRUE;
  2726.             for (N = 0 ; N < strlen (S) && !Stop ; N ++)
  2727.               Stop = !isdigit (S[N]);
  2728.             if (!Stop)
  2729.             {
  2730.               NetworkSocket = atoi (S);
  2731.               Handled = TRUE;
  2732.               fscanf (Fp, "%s", S);
  2733.             }
  2734.           }
  2735.           if (!feof (Fp) && !stricmp (S, SKILL) && !Stop)                                                       /* Found '-SKILL' */
  2736.           {
  2737.             fscanf (Fp, "%s", S);                                                              /* '-SKILL' takes a parameter: 1-5 */
  2738.             if (feof (Fp) || strlen (S) != 1 || S[0] < '1' || S[0] > '5')
  2739.               Stop = TRUE;
  2740.             else
  2741.             {
  2742.               DifficultyActive = S[0] - '0';
  2743.               Handled = TRUE;
  2744.               fscanf (Fp, "%s", S);
  2745.             }
  2746.           }
  2747.           if (!feof (Fp) && !stricmp (S, DMATCH) && !Stop)                                                 /* Found '-DEATHMATCH' */
  2748.           {
  2749.             DeathmatchOn = TRUE;
  2750.             Handled = TRUE;
  2751.             fscanf (Fp, "%s", S);
  2752.           }
  2753.           if (!feof (Fp) && !stricmp (S, DMATCHV2) && !Stop)                                                 /* Found '-ALTDEATH' */
  2754.           {
  2755.             DeathmatchV2On = TRUE;
  2756.             Handled = TRUE;
  2757.             fscanf (Fp, "%s", S);
  2758.           }
  2759.           if (!feof (Fp) && !stricmp (S, NOMONSTERS) && !Stop)                                             /* Found '-NOMONSTERS' */
  2760.           {
  2761.             NoMonstersOn = TRUE;
  2762.             Handled = TRUE;
  2763.             fscanf (Fp, "%s", S);
  2764.           }
  2765.           if (!feof (Fp) && !stricmp (S, RESPAWNMONSTERS) && !Stop)                                           /* Found '-RESPAWN' */
  2766.           {
  2767.             RespMonstersOn = TRUE;
  2768.             Handled = TRUE;
  2769.             fscanf (Fp, "%s", S);
  2770.           }
  2771.           if (!feof (Fp) && !stricmp (S, FASTMONSTERS) && !Stop)                                                 /* Found '-FAST' */
  2772.           {
  2773.             FastMonstersOn = TRUE;
  2774.             Handled = TRUE;
  2775.             fscanf (Fp, "%s", S);
  2776.           }
  2777.           if (!feof (Fp) && !stricmp (S, GOTOANYTHING) && !Stop)                                                 /* Found '-WARP' */
  2778.           {
  2779.             fscanf (Fp, "%s", S);                                                             /* '-WARP' takes a parameter: 01-32 */
  2780.             if (feof (Fp))
  2781.               Stop = TRUE;
  2782.             for (N = 0 ; N < strlen (S) && !Stop ; N ++)
  2783.               Stop = !isdigit (S[N]);
  2784.             if (!Stop)
  2785.             {
  2786.               CurrentLevel = atoi (S);
  2787.               Handled = TRUE;
  2788.               fscanf (Fp, "%s", S);
  2789.             }
  2790.           }
  2791.           if (!feof (Fp) && !strnicmp (S, COMPORT, 4) && !Stop)                                                  /* Found '-COM#' */
  2792.           {
  2793.             if (strlen (S) != 5 || S[4] < '1' || S[0] > '4')                       /* COM port number is last char in this string */
  2794.               Stop = TRUE;                                                                     /* Port number must be between 1-4 */
  2795.             else
  2796.             {
  2797.               CommPortActive = S[4] - '0';
  2798.               Handled = TRUE;
  2799.               fscanf (Fp, "%s", S);
  2800.             }
  2801.           }
  2802.           if (!feof (Fp) && !stricmp (S, NUMPLAYERS) && !Stop)                                                  /* Found '-NODES' */
  2803.           {
  2804.             fscanf (Fp, "%s", S);                                                              /* '-NODES' takes a parameter: 2-4 */
  2805.             if (feof (Fp) || strlen (S) != 1 || S[0] < '2' || S[0] > '4')
  2806.               Stop = TRUE;
  2807.             else
  2808.             {
  2809.               NumNodesActive = S[0] - '0';
  2810.               Handled = TRUE;
  2811.               fscanf (Fp, "%s", S);
  2812.             }
  2813.           }
  2814.           if (!feof (Fp) && !stricmp (S, INCFILE) && !Stop)                                                      /* Found '-FILE' */
  2815.           {
  2816.             fscanf (Fp, "%s", S);                                                /* Read-ahead first filename (at least 1 needed) */
  2817.             do                                                                    /* Each following word is a filename, until the */
  2818.             {                                                                      /* next keyword is found or EOF is encountered */
  2819.               if (!feof (Fp) && !Stop)
  2820.               {
  2821.                 DriveNum = DoomDrive;                                                         /* Assume: not preceded by a drive: */
  2822.                 if (S[1] == ':')                                                                            /* Preceded by drive: */
  2823.                   if (toupper (S[0]) < 'A' || toupper (S[0]) > 'Z')
  2824.                     Stop = TRUE;
  2825.                   else
  2826.                   {
  2827.                     DriveNum = ToNumber (S[0]);
  2828.                     for (M = 2 ; M <= strlen (S) ; M ++)                                                             /* Cut drive */
  2829.                       S[M - 2] = S[M];
  2830.                   }
  2831.                 if (!Stop)
  2832.                   if (stricmp (S + strlen (S) - 4, ".WAD"))                                      /* Filename MUST end with '.WAD' */
  2833.                     Stop = TRUE;
  2834.                 if (!Stop)
  2835.                 {
  2836.                   S[strlen (S) - 4] = '\0';                                                   /* Cut the '.WAD' from the filename */
  2837.                   M = strlen (S);
  2838.                   if (!M || S[M - 1] == '\\')                                           /* Ended with a '\' or no filename at all */
  2839.                     Stop = TRUE;
  2840.                   else
  2841.                   {
  2842.                     while (S[-- M] != '\\' && M >= 0)
  2843.                       ;
  2844.                     if (M >= 0)                                                                               /* Preceded by path */
  2845.                     {
  2846.                       if (M == 0)
  2847.                         strcpy (Directory, "\\");                                                      /* Handle root differently */
  2848.                       else
  2849.                       {
  2850.                         strncpy (Directory, strupr (S), M);
  2851.                         Directory[M] = '\0';
  2852.                         if (DriveNum == DoomDrive && Directory[0] != '\\')
  2853.                         {
  2854.                           sprintf (Tmp, "%s\\%s", DoomDirectory, Directory);
  2855.                           strcpy (Directory, Tmp);
  2856.                         }
  2857.                       }
  2858.                       strcpy (FileName, strupr (S) + M + 1);
  2859.                     }
  2860.                     else                                                                                               /* No path */
  2861.                     {
  2862.                       strcpy (FileName, strupr (S));
  2863.                       if (DriveNum == DoomDrive)
  2864.                         strcpy (Directory, DoomDirectory);
  2865.                       else
  2866.                         strcpy (Directory, DEFAULTWADDIR);
  2867.                     }
  2868.                     if (strlen (FileName) > 8)                                        /* Filename contains more than 8 characters */
  2869.                       Stop = TRUE;
  2870.                     else
  2871.                     {
  2872.                       for (M = strlen (FileName) ; M < 8 ; M ++)                             /* Fill out filename to 8 characters */
  2873.                         FileName[M] = ' ';
  2874.                       FileName[M] = '\0';
  2875.                       Handled = FALSE;
  2876.                       for (M = 0 ; M < TotalWads && !Handled; M ++)                  /* Match against all WAD filenames in memory */
  2877.                         if (WadInfo[M]->Drive == DriveNum)
  2878.                           if (!strcmp (WadInfo[M]->Path, Directory))
  2879.                             if (!strcmp (WadInfo[M]->Name, FileName))
  2880.                             {
  2881.                               Handled = TRUE;
  2882.                               WadInfo[M]->Selected = TRUE;                            /* If it matches, than auto-select the file */
  2883.                             }
  2884.                       fscanf (Fp, "%s", S);                                                                          /* Read next */
  2885.                     }
  2886.                   }
  2887.                 }
  2888.               }
  2889.             }
  2890.             while (!feof (Fp) && S[0] != '-' && !Stop);
  2891.             Handled = TRUE;
  2892.           }
  2893.           if (!Handled && !feof (Fp) && !Stop)
  2894.             fscanf (Fp, "%s", S);                                                                           /* Other switch: skip */
  2895.         }
  2896.       }
  2897.       fclose (Fp);
  2898.       if (DeathmatchOn && DeathmatchV2On)                                          /* -DEATHMATCH -ALTDEATH means deathmatch v2.0 */
  2899.         DeathmatchOn = FALSE;
  2900.       if (NoMonstersOn)                                                                              /* Handle contradictionaries */
  2901.       {
  2902.         RespMonstersOn = FALSE;
  2903.         FastMonstersOn = FALSE;
  2904.       }
  2905.       PrintLevel ();
  2906.       PrintLevelPagers (0);
  2907.       PrintDifficulties (0);
  2908.       PrintPlayTypes (0);
  2909.       PrintDeathmatch (FALSE);
  2910.       PrintV2Deathmatch (FALSE);
  2911.       PrintNoMonsters (FALSE);
  2912.       PrintRespawnMonsters (FALSE);
  2913.       PrintFastMonsters (FALSE);
  2914.       PrintWadFiles ();
  2915.     }
  2916.   }
  2917.   ShowMouse ();
  2918. }
  2919.  
  2920. void HandleAutomatic (boolean KeyInput)
  2921.  
  2922. /**********************************************************************************************************************************/
  2923. /* Pre   : 'KeyInput' is TRUE if this routine was called because of a keypress.                                                   */
  2924. /* Post  : The mouse pointer was at the automatic item. This item has been highlited. If the mouse button was pressed, then this  */
  2925. /*         routine reads all selected WAD files and initializes the current level to the lowest ones found.                       */
  2926. /* Import: HideMouse, ShowMouse, PrintAutomatic, PrintLevel, PrintLevelPagers, UnselectPreviousField.                             */
  2927. /**********************************************************************************************************************************/
  2928.  
  2929. {
  2930.   unsigned long  register Startpoint1 = 0x00000000;
  2931.   unsigned long  register Startpoint2 = 0x00000000;
  2932.   unsigned long  register Startpoint3 = 0x00000000;
  2933.   unsigned short register Startpoint4 = 0x00;
  2934.   unsigned long  register LevelMask;
  2935.   boolean                 Found       = FALSE;
  2936.  
  2937.   HideMouse ();
  2938.   if (!KeyInput)
  2939.     UnselectPreviousField (AUTOMATICFIELD);
  2940.   if (!KeyInput)
  2941.   {
  2942.     PrintAutomatic (TRUE);
  2943.     CurrentField = AUTOMATICFIELD;
  2944.   }
  2945.   ShowMouse ();
  2946.   if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput)
  2947.   {
  2948.     for (M = 0 ; M < TotalWads ; M ++)
  2949.       if (WadInfo[M]->Selected)                                       /* Now merge all level information of all selcted WAD files */
  2950.       {
  2951.         GetWadInfo (M, TRUE);
  2952.         Startpoint1 |= WadInfo[M]->NewLevels1;
  2953.         Startpoint2 |= WadInfo[M]->NewLevels2;
  2954.         Startpoint3 |= WadInfo[M]->NewLevels3;
  2955.         Startpoint4 |= WadInfo[M]->NewLevels4;
  2956.       }
  2957.     if (Startpoint1 != 0x00000000 ||
  2958.         Startpoint2 != 0x00000000 ||
  2959.         Startpoint3 != 0x00000000 ||
  2960.         Startpoint4 != 0x00)                                                               /* Selected files contain new levels ? */
  2961.     {
  2962.       CurrentLevel = 1;
  2963.       LevelMask = 0x00000001;
  2964.       for (M = 0 ; M < 32 && !Found ; M ++)
  2965.         if ((Startpoint1 & LevelMask) == LevelMask)                                                /* Search for the lowest level */
  2966.           Found = TRUE;
  2967.         else
  2968.         {
  2969.           LevelMask *= 2;                                                                                           /* Shift left */
  2970.           CurrentLevel ++;
  2971.         }
  2972.       LevelMask = 0x00000001;
  2973.       for (M = 0 ; M < 32 && !Found ; M ++)
  2974.         if ((Startpoint2 & LevelMask) == LevelMask)
  2975.           Found = TRUE;
  2976.         else
  2977.         {
  2978.           LevelMask *= 2;
  2979.           CurrentLevel ++;
  2980.         }
  2981.       LevelMask = 0x00000001;
  2982.       for (M = 0 ; M < 32 && !Found ; M ++)
  2983.         if ((Startpoint3 & LevelMask) == LevelMask)
  2984.           Found = TRUE;
  2985.         else
  2986.         {
  2987.           LevelMask *= 2;
  2988.           CurrentLevel ++;
  2989.         }
  2990.       LevelMask = 0x00000001;
  2991.       for (M = 0 ; M < 3 && !Found ; M ++)
  2992.         if ((Startpoint4 & LevelMask) == LevelMask)
  2993.           Found = TRUE;
  2994.         else
  2995.         {
  2996.           LevelMask *= 2;
  2997.           CurrentLevel ++;
  2998.         }
  2999.     }
  3000.     else                                                                                                /* No levels in the files */
  3001.       CurrentLevel = 1;
  3002.     PrintLevel ();
  3003.     PrintLevelPagers (0);
  3004.   }
  3005. }
  3006.  
  3007. boolean _fastcall HandleStartGame (boolean KeyInput)
  3008.  
  3009. /**********************************************************************************************************************************/
  3010. /* Pre   : 'KeyInput' is TRUE if this routine was called because of a keypress.                                                   */
  3011. /* Post  : The mouse pointer was at the startgame item. This item has been highlited. If the mouse button was pressed, then this  */
  3012. /*         routine returns FALSE (which effectively means: start the game), otherwise TRUE.                                       */
  3013. /* Import: HideMouse, ShowMouse, PrintStart, UnselectPreviousField.                                                               */
  3014. /**********************************************************************************************************************************/
  3015.  
  3016. {
  3017.   HideMouse ();
  3018.   UnselectPreviousField (STARTFIELD);
  3019.   PrintStart (TRUE);
  3020.   CurrentField = STARTFIELD;
  3021.   ShowMouse ();
  3022.   return ((!Mouse.Left || Mouse.LeftStillPressed) && !KeyInput);
  3023. }
  3024.  
  3025. int main (int argc, char **argv)
  3026.  
  3027. /**********************************************************************************************************************************/
  3028. /* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> MAIN ROUTINE <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
  3029. /* Import: ResetMouse, ReadConfig, PrintDifficulties, PrintPlayTypes, PrintDeathmatch, PrintLevel, PrintLevelPagers,              */
  3030. /*         PrintWadFiles, PrintPagers, ShowMouse, MouseStatus, HandleDifficulty, HandleDeathmatch, HandleLevel, HandleNextPage,   */
  3031. /*         HandlePreviousPage, HideMouse, DeAllocateAll, PrintStart, PrintRdPrev, HandleStartGame, HandleReadPrev, GetWadInfo,    */
  3032. /*         WriteWadInfo, PrintAutomatic, HandleAutomatic, HandlePlayType, UnselectPreviousField, InitVideo, PrintV2Deathmatch,    */
  3033. /*         PrintRespawnMonsters, PrintNoMonsters, PrintFastMonsters, HandleV2Deathmatch, HandleRespawnMonsters, HandleNoMonsters, */
  3034. /*         HandleFastMonsters, SortFiles, RescanFiles, ToggleFileSort, Bye, PrText.                                               */
  3035. /**********************************************************************************************************************************/
  3036.  
  3037. {
  3038.   FILE    *Fp;
  3039.   struct   find_t      FileBlock;
  3040.   struct   WadInfo_s   ReadInfo;                                                            /* Entry as read from the WADINFOFILE */
  3041.   char     DrivePath[_MAX_DIR];                                                                   /* Current directory of a drive */
  3042.   char     NextArgument;                                                                   /* Argument number on the command line */
  3043.   char     Ch;
  3044.   char     SepChar;                                                                    /* Seperation char in START.*; '\n' or ' ' */
  3045.   boolean  SepCharNeeded;
  3046.   boolean  More;
  3047.   boolean  FirstWad;                                                                  /* First WAD file to include in START.BAT ? */
  3048.   boolean  AutoFound;                                                                  /* Files given as AUTOINCLUDE paremeters ? */
  3049.   boolean  DriveChanged;                                                          /* Handling other drive than the 'home' drive ? */
  3050.   boolean  CLineError  = FALSE;                                                                 /* Nonsense on the command line ? */
  3051.   boolean  WarpDone    = FALSE;                                                    /* TRUE if WARP has been included in START.BAT */
  3052.   unsigned int Key;
  3053.  
  3054.   free (getcwd (CurPath, _MAX_DIR - 1));                                                             /* Collect current directory */
  3055.   CurDrive = ToNumber (CurPath[0]);                                                                       /* Derive current drive */
  3056.   UseMouse = ResetMouse ();                                                                       /* Force mouse to re-initialize */
  3057.   strcpy (ConfigFile, DEFAULTCONFIGFILE);
  3058.   NextArgument = 0;                                                                              /* Handle command line arguments */
  3059.   if (argc > 3)                                                                                 /* (Max number of arguments is 2) */
  3060.     CLineError = TRUE;
  3061.   while (++ NextArgument < argc)
  3062.     if (!stricmp (argv[NextArgument], RESCAN1) || !stricmp (argv[NextArgument], RESCAN2))              /* Handle any -r parameter */
  3063.       Rescan = TRUE;
  3064.     else
  3065.       if (argv[NextArgument][0] == OTHERCONFIGFILE)                                                 /* Handle any +file parameter */
  3066.       {
  3067.         strcpy (ConfigFile, argv[NextArgument] + 1);
  3068.         ConfigChange = TRUE;
  3069.       }
  3070.       else
  3071.         CLineError = TRUE;
  3072.   if (CLineError)
  3073.     Bye (RETURNERROR, "Usage: EASYWAD [%s] [%cconfig]\n", RESCAN1, OTHERCONFIGFILE);
  3074.   ReadConfig ();                                                                                 /* Find CONFIGFILE and handle it */
  3075.   if (!(Fp = fopen (InfoFile, "r")))                                                               /* Test if the InfoFile exists */
  3076.     Rescan = TRUE;                                                                                    /* No, it should be created */
  3077.   else
  3078.     fclose (Fp);
  3079.   TotalWads = 0;
  3080.   if (Rescan)
  3081.     DoNotSearch = FALSE;                                                 /* Override if there is no infofile or if '-R' was given */
  3082.   _dos_setdrive (CurDrive, &Dummy);                                             /* Start from 'home' location (for partial paths) */
  3083.   chdir (CurPath);
  3084.   if (!DoNotSearch)                                                                            /* Search all WADDIR directories ? */
  3085.   {
  3086.     printf ("Searching for WAD files ... ");
  3087.     for (N = 0 ; N < TotalWadDirs ; N ++)                                                         /* Handle all given directories */
  3088.     {
  3089.       DriveChanged = FALSE;
  3090.       if (WadDir[N]->Drive)                                                                      /* Change drive if one was given */
  3091.       {
  3092.         _dos_setdrive (WadDir[N]->Drive, &Dummy);
  3093.         _dos_getdrive (&Dummy);
  3094.         if (Dummy != WadDir[N]->Drive)                                /* Check that the change has been made and report any error */
  3095.           Bye (RETURNERROR, "ERROR - Drive of WADDIR does not exist\n");
  3096.         DriveChanged = TRUE;
  3097.       }
  3098.       free (getcwd (DrivePath, _MAX_DIR - 1));                                         /* Collect current directory of this drive */
  3099.       if (strcmp (WadDir[N]->Name, DEFAULTWADDIR))                                   /* Change directory if the entry was not "." */
  3100.         if (chdir (WadDir[N]->Name))                                          /* Report the error if the directory does not exist */
  3101.           Bye (RETURNERROR, "ERROR - Directory of WADDIR does not exist\n");
  3102.       More = !_dos_findfirst (WADFILE, ANYATTR, &FileBlock);                                         /* Look-ahead for a WAD file */
  3103.       while (TotalWads < MAXWADS && More)                                               /* Handle all WAD files in this directory */
  3104.       {
  3105.         if ((strcmp (WadDir[N]->Name, DEFAULTWADDIR) || strcmp (FileBlock.name, MAINWAD))
  3106.             && FileBlock.size >= sizeof (struct WadHeader_s))                    /* Exclude the main DOOM WAD and too small files */
  3107.         {
  3108.           if ((WadInfo[TotalWads] = ((struct WadInfo_s far *)_fmalloc ((size_t)sizeof (struct WadInfo_s)))) == NOMEM)
  3109.             Bye (RETURNERROR, "FATAL ERROR - Out of memory\n");
  3110.           strcpy (WadInfo[TotalWads]->OrigName, FileBlock.name);                                                 /* Copy filename */
  3111.           strncpy (WadInfo[TotalWads]->Name, FileBlock.name, strlen (FileBlock.name) - 4);        /* Copy name, without extension */
  3112.           for (M = strlen (FileBlock.name) - 4 ; M < 8 ; M ++)                                        /* Fill out to 8 characters */
  3113.             WadInfo[TotalWads]->Name[M] = ' ';
  3114.           WadInfo[TotalWads]->Name[M] = '\0';                                                           /* Make it a valid string */
  3115.           WadInfo[TotalWads]->Drive = WadDir[N]->Drive;
  3116.           strcpy (WadInfo[TotalWads]->Path, WadDir[N]->Name);
  3117.           for (M = 0 ; M < MAXINFOLEN ; M ++)
  3118.             WadInfo[TotalWads]->Info[M] = ' ';                                                     /* Initialize the other fields */
  3119.           WadInfo[TotalWads]->Info[M] = '\0';
  3120.           WadInfo[TotalWads]->Selected = FALSE;
  3121.           if (Rescan)
  3122.             GetWadInfo (TotalWads, FALSE);                                                          /* Read out the WAD directory */
  3123.           TotalWads ++;
  3124.         }
  3125.         More = !_dos_findnext (&FileBlock);
  3126.       }
  3127.       if (WadDir[N]->DoSubDirs)
  3128.       {
  3129.         More = !_dos_findfirst ("*.*", SUBATTR, &FileBlock);                                           /* Look for subdirectories */
  3130.         while (More)
  3131.         {
  3132.           while ((!(FileBlock.attrib & _A_SUBDIR) || (FileBlock.name[0] == '.')) && More)                      /* Not '.' or '..' */
  3133.             More = !_dos_findnext (&FileBlock);
  3134.           if (More)
  3135.           {
  3136.             if (TotalWadDirs == MAXWADDIRS)
  3137.               Bye (RETURNERROR, "ERROR - Too many WADDIR entries, max is %d\n", MAXWADDIRS);
  3138.             if ((WadDir[TotalWadDirs] = ((struct WadDir_s far *)_fmalloc ((size_t)sizeof (struct WadDir_s)))) == NOMEM)
  3139.               Bye (RETURNERROR, "FATAL ERROR - Out of memory\n");
  3140.             WadDir[TotalWadDirs]->Drive = WadDir[N]->Drive;                              /* Add this directory to the WADDIR list */
  3141.             sprintf (WadDir[TotalWadDirs]->Name, "%s\\%s", WadDir[N]->Name, FileBlock.name);
  3142.             WadDir[TotalWadDirs ++]->DoSubDirs = TRUE;                                                         /* Signal: recurse */
  3143.             More = !_dos_findnext (&FileBlock);
  3144.           }
  3145.         }
  3146.       }
  3147.       chdir (DrivePath);                                                         /* Return to the current directory of this drive */
  3148.       if (DriveChanged)
  3149.         _dos_setdrive (CurDrive, &Dummy);                                                                 /* Return to home drive */
  3150.     }
  3151.   }
  3152.   _dos_setdrive (CurDrive, &Dummy);                                                                    /* Return to home location */
  3153.    chdir (CurPath);
  3154.   if (Rescan)
  3155.   {
  3156.     printf ("Writing WADINFOFILE ... ");
  3157.     WriteWadInfo (InfoFile);
  3158.   }
  3159.   if (!(Fp = fopen (InfoFile, "r")))                                            /* Now handle the InfoFile, which should be found */
  3160.     Bye (RETURNERROR, "ERROR - Drive read error\n");
  3161.   fscanf (Fp, "%s", S);                                                             /* Read-ahead: first part is the drive number */
  3162.   while (!feof (Fp))
  3163.   {
  3164.     ReadInfo.Drive = atoi (S);                                                                               /* Convert to number */
  3165.     fscanf (Fp, "%s", ReadInfo.Path);                                                             /* Second part is the directory */
  3166.     fscanf (Fp, "%s ", ReadInfo.OrigName);                                                         /* Third part is the  filename */
  3167.     More = TRUE;
  3168.     for (M = 0 ; M < MAXINFOLEN && More ; M ++)                                   /* Last part is the info. It may contain spaces */
  3169.     {
  3170.       fscanf (Fp, "%c", &ReadInfo.Info[M]);
  3171.       if (ReadInfo.Info[M] == '\n')                                                              /* String shorter than maximum ? */
  3172.         More = FALSE;
  3173.     }
  3174.     while (More)                                                                                     /* End of line not yet found */
  3175.     {
  3176.       fscanf (Fp, "%c", &Ch);
  3177.       if (Ch == '\n')                                                                                /* Read rest of the line out */
  3178.         More = FALSE;
  3179.     }
  3180.     ReadInfo.Info[M] = '\0';
  3181.     ConvertFullName (&ReadInfo);                                                          /* Convert full to short name if wanted */
  3182.     if (DoNotSearch)
  3183.     {
  3184.       if ((WadInfo[TotalWads] = ((struct WadInfo_s far *)_fmalloc ((size_t)sizeof (struct WadInfo_s)))) == NOMEM)
  3185.         Bye (RETURNERROR, "FATAL ERROR - Out of memory\n");
  3186.       strcpy (WadInfo[TotalWads]->OrigName, ReadInfo.OrigName);                                                  /* Copy filename */
  3187.       strncpy (WadInfo[TotalWads]->Name, ReadInfo.OrigName, strlen (ReadInfo.OrigName) - 4);      /* Copy name, without extension */
  3188.       for (M = strlen (ReadInfo.OrigName) - 4 ; M < 8 ; M ++)                                         /* Fill out to 8 characters */
  3189.         WadInfo[TotalWads]->Name[M] = ' ';
  3190.       WadInfo[TotalWads]->Name[M] = '\0';
  3191.       WadInfo[TotalWads]->Drive = ReadInfo.Drive;
  3192.       strcpy (WadInfo[TotalWads]->Path, ReadInfo.Path);
  3193.       strcpy (WadInfo[TotalWads]->Info, ReadInfo.Info);
  3194.       WadInfo[TotalWads]->Selected = FALSE;
  3195.       TotalWads ++;
  3196.     }
  3197.     else
  3198.     {
  3199.       More = TRUE;
  3200.       for (M = 0 ; M < TotalWads && More; M ++)                                          /* Match against all read WAD file names */
  3201.         if (WadInfo[M]->Drive == ReadInfo.Drive)
  3202.           if (!stricmp (WadInfo[M]->Path, ReadInfo.Path))
  3203.             if (!stricmp (WadInfo[M]->OrigName, ReadInfo.OrigName))
  3204.             {
  3205.               More = FALSE;
  3206.               strcpy (WadInfo[M]->Info, ReadInfo.Info);                                    /* Copy info field to correct WAD file */
  3207.             }
  3208.     }
  3209.     fscanf (Fp, "%s", S);
  3210.   }
  3211.   fclose (Fp);
  3212.   if (TotalWads == 0)                              /* Abort the program if no WAD files were found (other than the main DOOM WAD) */
  3213.   {
  3214.     remove (InfoFile);
  3215.     Bye (RETURNERROR, "No WAD files found!\n");
  3216.   }
  3217.   SortFiles ();                                                                       /* Sort the WAD files if that was requested */
  3218.   N = -1;
  3219.   while (++ N < TotalAutoInc)                                                                   /* Handle all AUTOINCLUDE entries */
  3220.   {
  3221.     AutoFound = FALSE;
  3222.     for (M = 0 ; M < TotalWads && !AutoFound ; M ++)                                         /* AUTOINCLUDE file ? Then select it */
  3223.       if ((WadInfo[M]->Drive == AutoInc[N]->Drive)
  3224.            && (!strcmp (WadInfo[M]->Path, AutoInc[N]->Path))
  3225.            && (!strcmp (WadInfo[M]->OrigName, AutoInc[N]->OrigName)))
  3226.       {
  3227.         WadInfo[M]->Selected = TRUE;
  3228.         AutoFound = TRUE;
  3229.       }
  3230.   }
  3231.   CurrentPage = 0;
  3232.   LastPage = TotalWads / PAGE;
  3233.  
  3234.   InitVideo ();                                                /* Open a VGA screen of 640x480x16 (80x30 characters in text mode) */
  3235.   ScreenOpen = TRUE;                                                                                  /* Signal: in graphics mode */
  3236.   PrText (-1, 1, 1, LWHITE, "DOOM ][ EasyWAD2 v1.01 - (C) 94-95 ThunderWare Research Center");                 /* Draw the screen */
  3237.   PrText (-1, 1, 72, DWHITE, "F1 = HELP");
  3238.   PrText (-1, 11, 64, LWHITE, "PAGE  1 OF %2d", LastPage + 1);
  3239.   PrintDifficulties (0);
  3240.   PrintPlayTypes (0);
  3241.   PrintLevel ();
  3242.   PrintDeathmatch (FALSE);
  3243.   PrintV2Deathmatch (FALSE);
  3244.   PrintRespawnMonsters (FALSE);
  3245.   PrintNoMonsters (FALSE);
  3246.   PrintFastMonsters (FALSE);
  3247.   PrintWadFiles ();
  3248.   PrintPagers (0);
  3249.   PrintLevelPagers (0);
  3250.   PrintRdPrev (FALSE);
  3251.   PrintAutomatic (FALSE);
  3252.   PrintStart (FALSE);
  3253.  
  3254.   if (UseMouse)
  3255.   {
  3256.     ShowMouse ();                                                                                       /* Show the mouse pointer */
  3257.     MouseStatus ();                                                                                   /* Get current mouse status */
  3258.     Mouse.OldXx = Mouse.Xx;                                                                 /* Initialize all non-hardware fields */
  3259.     Mouse.OldYy = Mouse.Yy;
  3260.     Mouse.CoChange = TRUE;
  3261.     Mouse.LeftStillPressed = FALSE;
  3262.     CurrentField = NOFIELD;                                                                  /* Signal: check FIELD type directly */
  3263.   }
  3264.   else
  3265.   {
  3266.     CurrentField = FILEFIELD;                                                        /* Hi-lighting is only done in the FILEFIELD */
  3267.     PreviousWad = 1;
  3268.     PrText (-1, 13, 1, LRED, WadInfo[0]->Name);                                                    /* Hi-light the first WAD file */
  3269.   }
  3270.   More = TRUE;
  3271.   while (More)                                                                               /* Or: while not [START] pressed ... */
  3272.   {
  3273.     if (_bios_keybrd (_KEYBRD_READY))                                                                            /* Key pressed ? */
  3274.     {
  3275.       Key = _bios_keybrd (_KEYBRD_READ);                                                                          /* Read the key */
  3276.       if ((Key & 0xFF00) == KEY_PAGEDOWN)
  3277.         HandlePreviousPage (TRUE);
  3278.       else
  3279.         if ((Key & 0xFF00) == KEY_PAGEUP)
  3280.           HandleNextPage (TRUE);
  3281.         else
  3282.           if (!UseMouse                                                                       /* Only available if no mouse found */
  3283.               && ((Key & 0xFF00) == KEY_CURSLEFT
  3284.                || (Key & 0xFF00) == KEY_CURSRIGHT
  3285.                || (Key & 0xFF00) == KEY_CURSUP
  3286.                || (Key & 0xFF00) == KEY_CURSDOWN
  3287.                || (Key & 0xFF00) == KEY_SELECTFILE))
  3288.             HandleFile (Key & 0xFF00);
  3289.           else
  3290.             if ((Key & 0xFF00) == KEY_DELETEWAD)
  3291.               DeleteWadFile ();
  3292.             else
  3293.               if ((Key & 0xFF00) == KEY_TOGGLESORT)
  3294.                 ToggleFileSort ();
  3295.               else
  3296.                 if ((Key & 0xFF00) == KEY_RESCAN)
  3297.                   RescanFiles ();
  3298.                 else
  3299.                   if ((Key & 0xFF00) == KEY_HELP)
  3300.                     GiveHelp ();
  3301.                   else
  3302.                     if ((Key & 0xFF00) == KEY_TOGGLEFULL)
  3303.                       ToggleFullName ();
  3304.                     else
  3305.                       switch (toupper ((char)(Key & 0x00FF)))
  3306.                       {
  3307.                         case KEY_ABORT        : Bye (RETURNABORT, "Program aborted - Thank you for using DOOM ][ EasyWAD2\n");
  3308.                         case KEY_STARTGAME    : More = HandleStartGame (TRUE);
  3309.                                                 break;
  3310.                         case KEY_DIFFICULTY   : HandleDifficulty (TRUE);
  3311.                                                 break;
  3312.                         case KEY_LEVELUP      : HandleNextLevel (TRUE);
  3313.                                                 break;
  3314.                         case KEY_LEVELDOWN    : HandlePreviousLevel (TRUE);
  3315.                                                 break;
  3316.                         case KEY_DEATHMATCH   : HandleDeathmatch (TRUE);
  3317.                                                 break;
  3318.                         case KEY_DEATHMATCHV2 : HandleV2Deathmatch (TRUE);
  3319.                                                 break;
  3320.                         case KEY_NOMONSTERS   : HandleNoMonsters (TRUE);
  3321.                                                 break;
  3322.                         case KEY_RESPAWNMONST : HandleRespawnMonsters (TRUE);
  3323.                                                 break;
  3324.                         case KEY_FASTMONSTERS : HandleFastMonsters (TRUE);
  3325.                                                 break;
  3326.                         case KEY_NODES        :
  3327.                         case KEY_COMPORT      :
  3328.                         case KEY_PLAYTYPE     : HandlePlayType (TRUE, toupper ((char)(Key & 0x00FF)));
  3329.                                                 break;
  3330.                         case KEY_AUTO         : HandleAutomatic (TRUE);
  3331.                                                 break;
  3332.                         case KEY_READPREVIOUS : HandleReadPrev (TRUE);
  3333.                       }
  3334.     }
  3335.     if (UseMouse && (Mouse.CoChange || Mouse.Left))                                        /* Mouse moved or left buttonpressed ? */
  3336.     {
  3337.       if (Mouse.Yy > 11)                                              /* Find out which FIELD is pointed to and handle that field */
  3338.         HandleFile (0x0000);                                                                                    /* Signal: no key */
  3339.       else
  3340.         if (Mouse.Yy > 1 && Mouse.Yy < 7 && Mouse.Xx > 26 && Mouse.Xx < 51)
  3341.           HandleDifficulty (FALSE);
  3342.         else
  3343.           if (Mouse.Yy > 1 && Mouse.Yy < 6 && Mouse.Xx > 53)
  3344.             HandlePlayType (FALSE, 0x00);
  3345.           else
  3346.             if (Mouse.Yy == 7 && Mouse.Xx > 53)
  3347.               HandleDeathmatch (FALSE);
  3348.             else
  3349.               if (Mouse.Yy == 8 && Mouse.Xx > 53)
  3350.                 HandleV2Deathmatch (FALSE);
  3351.               else
  3352.                 if (Mouse.Yy == 8 && Mouse.Xx > 26 && Mouse.Xx < 51)
  3353.                   HandleNoMonsters (FALSE);
  3354.                 else
  3355.                   if (Mouse.Yy == 9 && Mouse.Xx > 26 && Mouse.Xx < 51)
  3356.                     HandleRespawnMonsters (FALSE);
  3357.                   else
  3358.                     if (Mouse.Yy == 10 && Mouse.Xx > 26 && Mouse.Xx < 51)
  3359.                       HandleFastMonsters (FALSE);
  3360.                     else
  3361.                       if (Mouse.Yy == 2 && Mouse.Xx > 1 && Mouse.Xx < 5)
  3362.                         HandlePreviousLevel (FALSE);
  3363.                       else
  3364.                         if (Mouse.Yy == 2 && Mouse.Xx > 12 && Mouse.Xx < 16)
  3365.                           HandleNextLevel (FALSE);
  3366.                         else
  3367.                           if (Mouse.Yy == 10 && Mouse.Xx > 76)
  3368.                             HandleNextPage (FALSE);
  3369.                           else
  3370.                             if (Mouse.Yy == 10 && Mouse.Xx > 58 && Mouse.Xx < 62)
  3371.                               HandlePreviousPage (FALSE);
  3372.                             else
  3373.                               if (Mouse.Yy == 8 && Mouse.Xx < 15)
  3374.                                 More = HandleStartGame (FALSE);
  3375.                               else
  3376.                                 if (Mouse.Yy == 9 && Mouse.Xx < 15)
  3377.                                   HandleAutomatic (FALSE);
  3378.                                 else
  3379.                                   if (Mouse.Yy == 10 && Mouse.Xx < 15)
  3380.                                     HandleReadPrev (FALSE);
  3381.                                   else
  3382.                                     if (CurrentField != NOFIELD)                                       /* No FIELD was pointed to */
  3383.                                     {                        /* If previously a FIELD WAS pointed to, then un-highlite that field */
  3384.                                       HideMouse ();
  3385.                                       UnselectPreviousField (NOFIELD);
  3386.                                       ShowMouse ();
  3387.                                       CurrentField = NOFIELD;
  3388.                                     }
  3389.       Mouse.OldXx = Mouse.Xx;
  3390.       Mouse.OldYy = Mouse.Yy;
  3391.     }
  3392.     if (UseMouse)
  3393.     {
  3394.       MouseStatus ();
  3395.       if (Mouse.Right)
  3396.       {
  3397.         HideMouse ();
  3398.         Bye (RETURNABORT, "Program aborted - Thank you for using DOOM ][ EasyWAD2\n");
  3399.       }
  3400.     }
  3401.   }
  3402.   HideMouse ();
  3403.   _setvideomode (_DEFAULTMODE);                                                                               /* Close the screen */
  3404.   ScreenOpen = FALSE;                                                                                   /* Signal: in normal mode */
  3405.   
  3406.   if (!(Fp = fopen (BATFILE, "w")))                                                                           /* Open "START.BAT" */
  3407.     Bye (RETURNERROR, "ERROR - Cannot create file, disk full ?\n");
  3408.   fprintf (Fp, "@ECHO OFF\n");
  3409.   if (DoomDrive)
  3410.     fprintf (Fp, "%c:\n", ToName (DoomDrive));
  3411.   if (strcmp (DoomDirectory, DEFAULTWADDIR))
  3412.     fprintf (Fp, "CD %s\n", DoomDirectory);
  3413.   if (!NoAutoReturn)
  3414.     fprintf (Fp, "ECHO.| ");
  3415.   switch (PlayTypeActive)                                                                /* Print the correct command to the file */
  3416.   {
  3417.     case 1 : fprintf (Fp, "%s", STARTALONE);
  3418.              break;
  3419.     case 2 : fprintf (Fp, "%s", IpxDriver);
  3420.              break;
  3421.     case 3 : fprintf (Fp, "%s", SerDriver);
  3422.   }
  3423.   fprintf (Fp, " @%s\\%s\n", CurPath, RESPONSEFILE);                                     /* CurPath also contains the drive: part */
  3424.   if (DoomDrive)
  3425.     fprintf (Fp, "%c:\n", ToName (CurDrive));
  3426.   if (strcmp (DoomDirectory, DEFAULTWADDIR))
  3427.     fprintf (Fp, "CD %s\n", CurPath + 2);
  3428.   fclose (Fp);
  3429.   if (!(Fp = fopen (RESPONSEFILE, "w")))                                                                      /* Open "START.OPT" */
  3430.     Bye (RETURNERROR, "ERROR - Cannot create file, disk full ?\n");
  3431.   SepChar = '\n';
  3432.   SepCharNeeded = TRUE;
  3433.   switch (PlayTypeActive)
  3434.   {
  3435.     case 1 : SepCharNeeded = FALSE;
  3436.              break;
  3437.     case 2 : if (!OtherIpxDriver)
  3438.                fprintf (Fp, "%s%c%d%c%d", NUMPLAYERS, SepChar, NumNodesActive, SepChar, NetworkSocket);
  3439.              break;
  3440.     case 3 : if (!OtherSerDriver)
  3441.                fprintf (Fp, "%s%d", COMPORT, CommPortActive);
  3442.   }
  3443.   if (DifficultyActive != DEFAULTDIFFICULTY)                                                            /* Different difficulty ? */
  3444.   {
  3445.     if (SepCharNeeded)
  3446.       fprintf (Fp, "%c", SepChar);
  3447.     SepCharNeeded = TRUE;
  3448.     if (!WarpDone)
  3449.     {
  3450.       fprintf (Fp, "%s%c%02d%c", GOTOANYTHING, SepChar, CurrentLevel, SepChar);
  3451.       WarpDone = TRUE;
  3452.     }
  3453.     fprintf (Fp, "%s%c%d", SKILL, SepChar, DifficultyActive);                                                      /* Then add it */
  3454.   }
  3455.   if (DeathmatchOn != DEFAULTDMATCH)                                                     /* DEATHMATCH wanted (and implemented) ? */
  3456.   {
  3457.     if (SepCharNeeded)
  3458.       fprintf (Fp, "%c", SepChar);
  3459.     SepCharNeeded = TRUE;
  3460.     fprintf (Fp, "%s", DMATCH);
  3461.   }
  3462.   if (DeathmatchV2On != DEFAULTDMATCHV2)                                                              /* DEATHMATCH v2.0 wanted ? */
  3463.   {
  3464.     if (SepCharNeeded)
  3465.       fprintf (Fp, "%c", SepChar);
  3466.     SepCharNeeded = TRUE;
  3467.     fprintf (Fp, "%s%c%s", DMATCH, SepChar, DMATCHV2);
  3468.   }
  3469.   if (NoMonstersOn != DEFAULTNOMONSTERS)                                                                   /* NOMONSTERS wanted ? */
  3470.   {
  3471.     if (SepCharNeeded)
  3472.       fprintf (Fp, "%c", SepChar);
  3473.     SepCharNeeded = TRUE;
  3474.     if (!WarpDone)
  3475.     {
  3476.       fprintf (Fp, "%s%c%02d%c", GOTOANYTHING, SepChar, CurrentLevel, SepChar);
  3477.       WarpDone = TRUE;
  3478.     }
  3479.     fprintf (Fp, "%s", NOMONSTERS);
  3480.   }
  3481.   if (RespMonstersOn != DEFAULTRESPAWN)                                                                       /* RESPAWN wanted ? */
  3482.   {
  3483.     if (SepCharNeeded)
  3484.       fprintf (Fp, "%c", SepChar);
  3485.     SepCharNeeded = TRUE;
  3486.     if (!WarpDone)
  3487.     {
  3488.       fprintf (Fp, "%s%c%02d%c", GOTOANYTHING, SepChar, CurrentLevel, SepChar);
  3489.       WarpDone = TRUE;
  3490.     }
  3491.     fprintf (Fp, "%s", RESPAWNMONSTERS);
  3492.   }
  3493.   if (FastMonstersOn != DEFAULTFASTMONST)                                                                /* FASTMONSTERS wanted ? */
  3494.   {
  3495.     if (SepCharNeeded)
  3496.       fprintf (Fp, "%c", SepChar);
  3497.     SepCharNeeded = TRUE;
  3498.     if (!WarpDone)
  3499.     {
  3500.       fprintf (Fp, "%s%c%02d%c", GOTOANYTHING, SepChar, CurrentLevel, SepChar);
  3501.       WarpDone = TRUE;
  3502.     }
  3503.     fprintf (Fp, "%s", FASTMONSTERS);
  3504.   }
  3505.   if (CurrentLevel != DEFAULTLEVEL)                                                                 /* Different starting level ? */
  3506.   {
  3507.     if (SepCharNeeded)
  3508.       fprintf (Fp, "%c", SepChar);
  3509.     SepCharNeeded = TRUE;
  3510.     fprintf (Fp, "%s%c%02d", GOTOANYTHING, SepChar, CurrentLevel);
  3511.   }
  3512.   for (M = 0 ; M < MAXADDSWITCHES ; M ++)                                                          /* Now add the direct switches */
  3513.     if (ExtCom[M].InUse)
  3514.     {
  3515.       if (SepCharNeeded)
  3516.         fprintf (Fp, "%c", SepChar);
  3517.       SepCharNeeded = TRUE;
  3518.       fprintf (Fp, ExtCom[M].Command);
  3519.     }
  3520.   FirstWad = TRUE;
  3521.   strcat (DoomDirectory, "\\");
  3522.   for (M = 0 ; M < TotalWads ; M ++)                                                                /* Add each selected WAD file */
  3523.     if (WadInfo[M]->Selected)
  3524.     {
  3525.       if (FirstWad)                                                                       /* Before adding the first, add "-FILE" */
  3526.       {
  3527.         FirstWad = FALSE;
  3528.         if (SepCharNeeded)
  3529.           fprintf (Fp, "%c", SepChar);
  3530.         fprintf (Fp, "%s", INCFILE);
  3531.       }
  3532.       if (WadInfo[M]->Drive && WadInfo[M]->Drive != DoomDrive)
  3533.         fprintf (Fp, "%c%c:", SepChar, ToName (WadInfo[M]->Drive));                                     /* Add the drive if found */
  3534.       else
  3535.         fprintf (Fp, "%c", SepChar);
  3536.       if (strcmp (WadInfo[M]->Path, DEFAULTWADDIR))
  3537.         if (strncmp (WadInfo[M]->Path, DoomDirectory, strlen (DoomDirectory)))                  /* Subdir of the DOOM directory ? */
  3538.           fprintf (Fp, "%s\\", WadInfo[M]->Path);                                                          /* Add the path if not */
  3539.         else
  3540.           if (WadInfo[M]->Drive == DoomDrive)                                                              /* Drive matches too ? */
  3541.             fprintf (Fp, "%s\\", WadInfo[M]->Path + strlen (DoomDirectory));                           /* Cut DOOM directory part */
  3542.           else
  3543.             fprintf (Fp, "%s\\", WadInfo[M]->Path);                                                        /* Add the path if not */
  3544.       fprintf (Fp, "%s", WadInfo[M]->OrigName);                                                               /* Add the filename */
  3545.     }
  3546.   fprintf (Fp, "\n");                                                                                          /* Add the newline */
  3547.   fclose (Fp);                                                                 /* Done; free all memory and shut down the program */
  3548.   DeAllocateAll ();
  3549.   exit (RETURNSTART);
  3550. }
  3551.