home *** CD-ROM | disk | FTP | other *** search
/ 1,001 Nights of Doom / 1001NightsOfDoom1995wickedSensations.iso / misc / easyw110.zip / EASYWAD.C next >
C/C++ Source or Header  |  1994-09-15  |  180KB  |  3,182 lines

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