home *** CD-ROM | disk | FTP | other *** search
/ Crazy Collection 12 / CC-12_1.iso / update / doompack / data.a00 / EASYW121.ZIP / EASYWAD.C next >
Encoding:
C/C++ Source or Header  |  1995-09-07  |  202.3 KB  |  3,614 lines

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