home *** CD-ROM | disk | FTP | other *** search
/ Doom 2 Explosion / Doom2Explosion.bin / doom2exp / programs / easyw105 / easywad.c next >
C/C++ Source or Header  |  1994-09-09  |  154KB  |  2,703 lines

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