/**********************************************************************************************************************************/ /* File : EASYWAD2.C */ /* Executable : EASYWAD2.EXE */ /* Helpfile : EASYWAD2.CFG */ /* Doc file : EASYWAD2.DOC */ /* Version num : 1.01 */ /* Last changed : 18-02-1995 12:37 */ /* Update count : 2 */ /* OS type : PC (DOS) */ /* Description : Menu handler for multiple WAD files for DOOM (Trademark of Id Software) */ /* Compiler : Microsoft (R) Quick C Compiler Version 2.50 */ /* Linker : Microsoft (R) QuickC Linker Version 4.10 */ /* QCL attribs : /AC /G2 /Ox /F 2000 /link GRAPHICS.LIB (Compact model, fully optimized 80286 code, Stacksize 8K) */ /* Other : WM.BAT : start file */ /* START.BAT : the result */ /* START.OPT : response file */ /* Remarks : Credits go to Brendon Wyber & Raphael Quinet (Doom Editor Utilities) for WadHeader and WadDirectory structures. */ /* */ /* By M. van der Heide of ThunderWare Research Center */ /**********************************************************************************************************************************/ #include #include #include #include #include #include #include #include #include #include #pragma check_stack (off) #pragma check_pointer (off) #ifndef TRUE typedef char boolean; #define TRUE 1 #define FALSE 0 #endif #define DBLACK 0 /* Define names for the standard (VGA) palette colors */ #define DBLUE 1 #define DGREEN 2 #define DCYAN 3 #define DRED 4 #define DMAGENTA 5 #define DYELLOW 6 #define DWHITE 7 #define LBLACK 8 #define LBLUE 9 #define LGREEN 10 #define LCYAN 11 #define LRED 12 #define LMAGENTA 13 #define LYELLOW 14 #define LWHITE 15 #define ANYATTR _A_NORMAL|_A_RDONLY|_A_HIDDEN|_A_SYSTEM|_A_ARCH /* File attributes */ #define SUBATTR _A_NORMAL|_A_RDONLY|_A_HIDDEN|_A_SYSTEM|_A_ARCH|_A_SUBDIR /* Subdirectory attributes */ #define MAXFNAME 9 #define MAXWADS 1000 /* Max number of WAD files the program can handle */ #define MAXAUTOINCLUDE 5 /* Maximum number of autoinclude-WAD files in a CONFIGFILE */ #define MAXWADDIRS 400 /* Max number of WAD file directories a CONFIGFILE may contain */ #define PAGE 54 /* Max number of WAD files on screen */ #define WADHEIGHT 18 /* Max number of filenames vertically */ #define WADWIDTH 27 /* Max positions taken for a filename+info horizontally */ #define MAXINFOLEN 16 /* Max length of a WAD file info field */ #define ENTRYLEN 8 /* Length of the name of a 'directory' entry in a WAD file */ #define NOMEM (void far *)0 /* Memory allocation error */ #define DEFAULTCONFIGFILE "EASYWAD2.CFG" #define OTHERCONFIGFILE '+' /* Used on the command line: use other config file than EASYWAD.CFG */ #define COMMENT '#' /* All characters in a line following this one are ignored in a CONFIGFILE */ #define WADFILE "*.WAD" #define BATFILE "START.BAT" /* This file is built at the end */ #define RESPONSEFILE "START.OPT" /* Response file */ #define DOSUB1 "/S" /* Used in CONFIGFILE in a WADDIR entry: do subdirs too */ #define DOSUB2 "-S" #define RESCAN1 "/R" /* Used on the command line: rebuild WAD InfoFile */ #define RESCAN2 "-R" #define MAINWAD "DOOM2.WAD" /* Main WAD file */ #define STARTALONE "DOOM2" /* Define the start commands for DOOM */ #define STARTIPX "IPXSETUP" #define STARTLINK "SERSETUP" #define DMATCH "-DEATHMATCH" /* Define the command parameters for DOOM */ #define INCFILE "-FILE" #define GOTOANYTHING "-WARP" #define SKILL "-SKILL" #define NUMPLAYERS "-NODES" #define COMPORT "-COM" #define NOMONSTERS "-NOMONSTERS" #define RESPAWNMONSTERS "-RESPAWN" #define FASTMONSTERS "-FAST" #define DMATCHV2 "-ALTDEATH" #define NWSOCKET "-SOCKET" #define NOFIELD 0 /* Number the FIELDs on the screen */ #define FILEFIELD 1 #define DIFFICULTYFIELD 3 #define PLAYTYPEFIELD 4 #define LEVELFIELD 5 #define DEATHMATCHFIELD 6 #define PAGERFIELD 7 #define RDPREVFIELD 8 #define STARTFIELD 9 #define AUTOMATICFIELD 10 #define DEATHMATCHV2FIELD 11 #define RESPAWNFIELD 12 #define NOMONSTERSFIELD 13 #define FASTMONSTERSFIELD 14 #define DEFAULTVERSION 666 /* This effectively means 1.0 */ #define DEFAULTDIFFICULTY 3 #define DEFAULTPLAYTYPE 1 #define DEFAULTLEVEL 1 #define DEFAULTDMATCH FALSE #define DEFAULTDMATCHV2 FALSE #define DEFAULTRESPAWN FALSE #define DEFAULTNOMONSTERS FALSE #define DEFAULTFASTMONST FALSE #define DEFAULTSOCKET 0 #define DEFAULTNODES 2 #define DEFAULTCOMPORT 1 #define DEFAULTWADDIR "." /* Current directory */ #define DEFAULTINFOFILE "WADS.DSC" #define KEY_LEVELUP '.' /* Keyboard equivalents of mouse selections */ #define KEY_LEVELDOWN ',' #define KEY_DIFFICULTY 'S' /* (Skill) */ #define KEY_PLAYTYPE 'T' #define KEY_NODES 'N' #define KEY_COMPORT 'C' #define KEY_DEATHMATCH 'D' #define KEY_DEATHMATCHV2 'V' #define KEY_AUTO 'A' #define KEY_READPREVIOUS 'R' #define KEY_NOMONSTERS 'M' #define KEY_RESPAWNMONST 'P' #define KEY_FASTMONSTERS 'F' #define KEY_ABORT 0x1B /* [ESC] */ #define KEY_STARTGAME 0x0D /* [RETURN] */ #define KEY_PAGEUP 0x4900 #define KEY_PAGEDOWN 0x5100 #define KEY_DELETEWAD 0x5300 /* [DEL] */ #define KEY_HELP 0x3B00 /* [F1] */ #define KEY_RESCAN 0x3F00 /* [F5] */ #define KEY_TOGGLEFULL 0x4100 /* [F7] */ #define KEY_TOGGLESORT 0x4200 /* [F8] */ #define KEY_CURSLEFT 0x4B00 #define KEY_CURSRIGHT 0x4D00 #define KEY_CURSUP 0x4800 #define KEY_CURSDOWN 0x5000 #define KEY_SELECTFILE 0x3900 /* [SPACE] */ #define RETURNERROR 1 /* Define exit codes */ #define RETURNABORT 1 #define RETURNSTART 0 #define NUMLEVEL 99 /* Define the number of possible levels */ #define NUMDIFFICULTY 5 #define NUMPLAYTYPE 3 #define NUMOPTIONS 17 /* Define the number of CONFIGFILE options */ #define MAXIGNORE 12 /* Define number of WAD 'directory' identifiers per type */ #define MAXLEVELS 1 #define MAXCOLORS 2 #define MAXDEMOS 1 #define MAXSPRITES 92 #define MAXSOUNDS 2 #define MAXMUSIC 3 #define MAXGRAPHS 0 #define MAXADDSWITCHES 5 #define NEWCOLORS 0x01 /* Define bits for the 'NewStuff' field in the 'wadinfo' structure */ #define NEWDEMOS 0x02 #define NEWSOUNDS 0x04 #define NEWMUSIC 0x08 #define NEWSPRITES 0x10 #define NEWGRAPHS 0x20 #define ToNumber(_Drv) (toupper ((_Drv)) - 'A' + 1) /* Convert drive name to drive number */ #define ToName(_Drv) ((_Drv) + 'A' - 1) /* Convert drive number to drive name */ struct Mouse_s /* Define mouse info */ { int OldXx; /* Coordinates stored from previous status check */ int OldYy; int Xx; /* 0 - 79 */ int Yy; /* 0 - 29 */ boolean CoChange; /* TRUE if coordinates have changed (mouse moved to a next character) */ boolean Left; /* Status of the 3 mouse buttons; TRUE if pressed */ boolean Middle; boolean Right; boolean LeftStillPressed; /* TRUE if the left button was also pressed in previous status check */ } Mouse; struct WadDir_s /* Define info for a WADDIR entry */ { char Drive; /* 1 = A, etc., 0 means: no drive given */ char Name[_MAX_DIR]; boolean DoSubDirs; /* TRUE if the subdirectories should be searched as well */ }; struct WadInfo_s /* Define info for a WADFILE entry (also used for AUTOINCLUDE files) */ { char Drive; /* 1 = A, etc., 0 means: no drive given */ char Path[_MAX_DIR]; char OrigName[MAXFNAME + _MAX_EXT - 1]; /* The original name, with extension */ char Name[MAXFNAME]; /* Filled out, no extension (as this is always *.WAD) */ char Info[MAXINFOLEN + 1]; /* Info as found in a WADINFOFILE file */ char NewStuff; /* Each bit represents an identifier type (demo, sound, etc) */ unsigned long NewLevels1; /* Each bit represents a level: 01 - 32 */ unsigned long NewLevels2; /* 33 - 64 */ unsigned long NewLevels3; /* 65 - 96 */ unsigned short NewLevels4; /* 97 - 99 */ boolean Selected; /* TRUE if the WAD is selected on screen */ }; struct WadHeader_s /* The first 12 bytes of a WAD file */ { char Type[4]; /* "PWAD" for a patch WAD, "IWAD" for an initial WAD */ long DirSize; /* Number of entries in the WAD 'directory' */ long DirStart; /* Pointer to the location (offset) of the 'directory' in the WAD file */ }; struct WadDirectory_s /* A 'directory' entry in the WAD file */ { long Start; /* Pointer to the data of this entry in the WAD file */ long Size; /* Length in bytes of the data */ char Name[ENTRYLEN]; /* Identifier (name) of this entry */ }; struct ExtCom_s /* The commands that are available for the ADDSWITCHES keyword */ { char *Command; /* The name of the switch */ boolean InUse; /* TRUE if this switch was given in the CONFIGFILE */ }; struct ConfOp_s /* The commands available in a CONFIGFILE */ { char *OptionName; enum { OPT_DOOMDIR, OPT_WADDIR, OPT_AUTOINC, OPT_ADDSWIT, OPT_PLAYTYP, OPT_SET, OPT_NUM, OPT_STRING, OPT_FILE, OPT_SORTFIL } OptionType; void far *DataPtr; }; struct WadDir_s far *WadDir[MAXWADDIRS]; struct WadInfo_s far *WadInfo[MAXWADS]; struct WadInfo_s far *AutoInc[MAXAUTOINCLUDE]; char CurPath[_MAX_DIR]; /* Current directory (preceded by drive:) */ char ConfigFile[_MAX_PATH]; /* Filename of the CONFIGFILE */ char InfoFile[_MAX_PATH]; /* Filename of WADINFOFILE as found in the CONFIGFILE */ char DoomDirectory[_MAX_PATH]; /* The main DOOM directory as found in the CONFIGFILE */ char IpxDriver[256]; /* Different IPX driver as found in the CONFIGFILE */ char SerDriver[256]; /* Different SER driver as found in the CONFIGFILE */ char S[256]; /* All-purpose string */ char CurrentField; /* Current FIELD type (see defines) */ char SelectionChange; /* Used in file FIELD; TRUE if selection toggled */ char CurrentPage; /* Current file FIELD page */ char LastPage; /* Last file FIELD page */ int CurDrive; /* Current drive */ int DoomDrive; /* Drive of main DOOM directory */ int TotalWads; /* Total number of found WAD files */ int TotalWadDirs; /* Total number of read WADDIR entries in the CONFIGFILE */ int TotalAutoInc; /* Total number of read AUTOINCLUDE entries in a CONFIGFILE */ int M; int N; int Dummy; /* Used in _dos_setdrive function */ boolean OtherSerDriver; /* TRUE if a different SERDRIVER was found in a CONFIGFILE */ boolean OtherIpxDriver; /* TRUE if a different IPXDRIVER was found in a CONFIGFILE */ boolean UseMouse; /* TRUE if a mouse has been found */ boolean MouseHidden; /* TRUE if the mouse pointer is temporarely hidden */ boolean Rescan = FALSE; /* TRUE if '-R' was given on the command line */ boolean ConfigChange = FALSE; /* TRUE if a different CONFIGFILE was given on the command line */ boolean ScreenOpen = FALSE; boolean DoNotSearch = FALSE; /* TRUE if NOSEARCH was found in the CONFIGFILE */ boolean SortWadFiles = FALSE; /* TRUE if SORTFILES was found in the CONFIGFILE */ boolean SortByName = TRUE; /* TRUE for "NAME", FALSE for "INFO" */ boolean NoFullName = FALSE; /* TRUE if NOFULLNAME was found in the CONFIGFILE */ boolean NoAutoReturn = FALSE; /* TRUE if NOAUTORETURN was found in the CONFIGFILE */ int CurrentSelected = 0; /* Current pointed WAD file, 0 if none */ int PreviousWad = 0; /* Previous pointed WAD file, 0 if none */ int DifficultyActive = DEFAULTDIFFICULTY; int PlayTypeActive = DEFAULTPLAYTYPE; int NumNodesActive = DEFAULTNODES; int NetworkSocket = DEFAULTSOCKET; int CommPortActive = DEFAULTCOMPORT; int CurrentLevel = DEFAULTLEVEL; boolean DeathmatchOn = DEFAULTDMATCH; boolean DeathmatchV2On = DEFAULTDMATCHV2; boolean RespMonstersOn = DEFAULTRESPAWN; boolean NoMonstersOn = DEFAULTNOMONSTERS; boolean FastMonstersOn = DEFAULTFASTMONST; char *Difficulties[] = {"I'm too young to die ", "Hey, not too rough ", "Hurt me plenty ", "Ultra-Violence! ", "NIGHTMARE "}; char *PlayTypes[] = {"Alone ", "IPX-compatible ", "Serial link "}; char *NumberNodes = "Number of players "; char *CommPort = "COM port "; char *Deathmatch = "DEATHMATCH! "; char *DeathmatchV2 = "DEATHMATCH! V2.0 "; char *NoMonsters = "No Monsters "; char *RespawnMonsters = "Respawn monsters "; char *FastMonsters = "Fast monsters "; char *Boxes[] = {"( ) ", "(\x07) "}; char *PreviousPage = "<<<"; char *NextPage = ">>>"; char *PreviousLevel = "<<<"; char *NextLevel = ">>>"; char *StartGame = "( START DOOM2 )"; char *Automatic = "( AUTO SELECT )"; char *ReadPrevious = "(READ PREVIOUS)"; char *NoLevel = "- "; char *IdLevelNames[] = {" Entryway ", " Underhalls ", " The Gantlet ", " The Focus ", " The Waste Tunnels ", " The Crusher ", " Dead Simple ", " Tricks and Traps ", " The Pit ", " Refueling Base ", " Circle of Death ", " The Factory ", " Downtown ", " The Inmost Dens ", " Industrial Zone ", " Suburbs ", " Tenements ", " The Courtyard ", " The Citadel ", " Gotcha! ", " Nirvana ", " The Catacombs ", " Barrels o'Fun ", " The Chasm ", " Bloodfalls ", "The Abandoned Mines", " Monster Condo ", " The Spirit World ", " The Living End ", " Icon of Sin ", " Wolfenstein ", " Grosse "}; char *IdLevels[] = {"MAP", "M"}; char *IdIgnore[] = {"THINGS", "LINEDEFS", "SIDEDEFS", "VERTEXES", "SEGS", "SSECTORS", "NODES", "SECTORS", "REJECT", "BLOCKMAP", "INFOPACK", "PLATFORM"}; /* Last 2 are NOT from Id Software! */ char *IdColors[] = {"PLAYPAL", "COLORMAP", "c", "palette"}; char *IdDemos[] = {"DEMO", "d", "demos"}; char *IdSprites[] = {"SARG", "TROO", "BOSS", "PLAY", "POSS", "SPOS", "SKUL", "HEAD", "CYBR", "SPID", "CHG", "SAW", "PIS", "PBU", "PSH", "BAL", "PUF", "BLU", "MIS", "TFO", "PUN", "SHT", "PLS", "BFG", "BFS", "BFE", "POL", "CAND", "CBRA", "SHOT", "MGUN", "LAUN", "CSAW", "CLIP", "SHEL", "ROCK", "STIM", "MEDI", "ARM", "BAR", "BPAK", "BROK", "AMMO", "SBOX", "ELEC", "BKEY", "YKEY", "RKEY", "SUIT", "PVIS", "BEXP", "PMAP", "PIN", "BON", "SOUL", "COL", "FSKU", "CEYE", "TRE", "SMI", "BSKU", "RSKU", "YSKU", "PLAS", "BFUG", "CELL", "PSTR", "CELP", "GOR", "TGRN", "TBLU", "SMRT", "SMBT", "SMGT", "BOS2", "BSPI", "CPOS", "FATB", "FATT", "FBXP", "FIRE", "HDB", "KEEN", "MEGA", "PAIN", "SGN2", "SHT2", "SKEL", "SSWV", "TLMP", "TLP2", "VILE", "p", "sprites"}; char *IdSounds[] = {"DS", "DP", "s", "sounds"}; char *IdMusic[] = {"D_", "GENMIDI", "DMXGUSC", "m", "music"}; char *IdGraphics[] = {"g", "graphics"}; struct ConfOp_s ConfOp[] = {{"DOOMDIR", OPT_DOOMDIR, NOMEM}, {"WADDIR", OPT_WADDIR, NOMEM}, {"WADINFOFILE", OPT_FILE, InfoFile}, {"SETSKILL", OPT_NUM, &DifficultyActive}, {"DEATHMATCH", OPT_SET, &DeathmatchOn}, {"AUTOINCLUDE", OPT_AUTOINC, NOMEM}, {"NOSEARCH", OPT_SET, &DoNotSearch}, {"SETCOMPORT", OPT_NUM, &CommPortActive}, {"SETNODES", OPT_NUM, &NumNodesActive}, {"SETPLAYTYPE", OPT_PLAYTYP, NOMEM}, {"ADDSWITCHES", OPT_ADDSWIT, NOMEM}, {"SORTFILES", OPT_SORTFIL, NOMEM}, {"SETSOCKET", OPT_NUM, &NetworkSocket}, {"IPXDRIVER", OPT_STRING, IpxDriver}, {"SERDRIVER", OPT_STRING, SerDriver}, {"NOFULLNAME", OPT_SET, &NoFullName}, {"NOAUTORETURN",OPT_SET, &NoAutoReturn}}; struct ExtCom_s ExtCom[] = {{"-NOJOY", FALSE}, {"-NOMOUSE", FALSE}, {"-NOMUSIC", FALSE}, {"-NOSFX", FALSE}, {"-NOSOUND", FALSE}}; int ResetMouse (void) /**********************************************************************************************************************************/ /* Pre : None. */ /* Post : The mouse driver has been reset. If no mouse was found, then 0 is returned. Anything else means success. */ /* Import: None. */ /**********************************************************************************************************************************/ { union REGS Regs; Regs.x.ax = 0x0000; int86 (0x33, &Regs, &Regs); return (Regs.x.ax); } void ShowMouse (void) /**********************************************************************************************************************************/ /* Pre : (global) 'UseMouse' is TRUE if a mouse has been detected. */ /* Post : The mouse pointer is made visable. */ /* Import: None. */ /**********************************************************************************************************************************/ { union REGS Regs; if (UseMouse) { Regs.x.ax = 0x0001; int86 (0x33, &Regs, &Regs); } } void HideMouse (void) /**********************************************************************************************************************************/ /* Pre : (global) 'UseMouse' is TRUE if a mouse has been detected. */ /* Post : The mouse pointer is made invisable. */ /* Import: None. */ /**********************************************************************************************************************************/ { union REGS Regs; if (UseMouse) { Regs.x.ax = 0x0002; int86 (0x33, &Regs, &Regs); } } void MouseStatus (void) /**********************************************************************************************************************************/ /* Pre : None. */ /* Post : The mouse driver has been read, which returns the status of the buttons and the x and y coordinates of the mouse. */ /* All this information is stored in the 'Mouse' structure. */ /* Import: None. */ /**********************************************************************************************************************************/ { union REGS Regs; Mouse.LeftStillPressed = Mouse.Left; Regs.x.ax = 0x0003; int86 (0x33, &Regs, &Regs); Mouse.Left = (Regs.x.bx & 0x0001); /* Store the status of the mouse buttons */ Mouse.Right = (Regs.x.bx & 0x0002); Mouse.Middle = (Regs.x.bx & 0x0004); Mouse.Xx = Regs.x.cx / 0x0008; /* Convert pixel coordinates to character coordinates */ Mouse.Yy = Regs.x.dx / 0x0010; Mouse.CoChange = (Mouse.OldXx != Mouse.Xx || Mouse.OldYy != Mouse.Yy); if (!Mouse.Left) Mouse.LeftStillPressed = FALSE; } void DeAllocateAll (void) /**********************************************************************************************************************************/ /* Pre : None. */ /* Post : If any WadDirs were initialized yet, then the memory is deallocated completely. */ /* If any WadInfos were initialized yet, then the memory is deallocated completely. */ /* If any AutoIncs were initialized yet, then the memory is deallocated completely. */ /* Import: None. */ /**********************************************************************************************************************************/ { fcloseall (); flushall (); while (-- TotalWadDirs >= 0) _ffree (WadDir[TotalWadDirs]); while (-- TotalWads >= 0) _ffree (WadInfo[TotalWads]); while (-- TotalAutoInc >= 0) _ffree (AutoInc[TotalAutoInc]); } void Bye (int ReturnType, char *Message, ...) /**********************************************************************************************************************************/ /* Pre : 'ReturnType' holds the exit code, 'Message' holds the error message. */ /* Post : The error message has been printed, all memory is freed and the program has been aborted. */ /* Import: DeAllocateAll. */ /**********************************************************************************************************************************/ { va_list Args; if (ScreenOpen) /* Still in graphics mode ? */ _setvideomode (_DEFAULTMODE); /* Then close the screen */ va_start (Args, Message); vfprintf (stderr, Message, Args); /* Print the (formatted) error message */ va_end (Args); _dos_setdrive (CurDrive, &Dummy); /* Return to home drive and directory */ chdir (CurPath); DeAllocateAll (); exit (ReturnType); } void PrText (short UseBox, short Y0, short X0, unsigned char Color, char *Msg, ...) /**********************************************************************************************************************************/ /* Pre : 'Y0' and 'X0' hold the coordinates, 'Color' holds the (VGA) color and 'Msg' holds the message to print. */ /* UseBox < 0: no selection box is printed first; */ /* UseBox = 0: an empty selection box is printed first; */ /* UseBox > 0: a filled selection box is printed first; */ /* If Y0 is 0, then the coordinates are not used; the text is written directly after the previous. */ /* Post : If the coordinates were non-zero, then the message is printed at these coordinates in the given color. The coordinates */ /* are first converted to the pixel coordinates according to the used font. */ /* Import: None. */ /**********************************************************************************************************************************/ { char Message[80]; va_list Args; va_start (Args, Msg); vsprintf (Message, Msg, Args); /* Convert the message into one string */ va_end (Args); if (Y0 != 0) _settextposition (Y0, X0); _settextcolor (Color); if (UseBox == 0) _outtext (Boxes[0]); if (UseBox > 0) _outtext (Boxes[1]); _outtext (Message); } void InitVideo (void) /**********************************************************************************************************************************/ /* Pre : None. */ /* Post : A VGA display of 640 x 480 x 16 colors has been opened and the screen has been cleared. */ /* Import: Bye. */ /**********************************************************************************************************************************/ { if (!_setvideomode (_VRES16COLOR)) Bye (RETURNERROR, "ERROR - You need a VGA videocard for this utility\n"); _clearscreen (_GCLEARSCREEN); } char *TestName (char *OldName) /**********************************************************************************************************************************/ /* Pre : 'OldName' holds the name to be tested. */ /* Post : The name is tested. The return value is a string of the (adapted) input with the following specifications: */ /* - Each part (subdirname) has at most 8 characters, possibly followed by a '.' and at most 3 characters. */ /* - Each of the characters is valid to the DOS system. */ /* - Trailing backslashes have been cut, except when it was the only path character. */ /* Import: Bye. */ /**********************************************************************************************************************************/ { char Level[_MAX_PATH]; char NewName[_MAX_PATH]; char Part[MAXFNAME]; int LvlCnt; int Ex; int PathLenOldName; int CharCnt; int PartChar; boolean PointDone; boolean NError = FALSE; boolean DriveFound = FALSE; boolean Ready = FALSE; PathLenOldName = strlen (OldName); CharCnt = -1; NewName[0] = '\0'; while (!Ready && !NError) { LvlCnt = 0; while ((++ CharCnt < PathLenOldName) && OldName[CharCnt] != '\\' && OldName[CharCnt] != ':') Level[LvlCnt ++] = OldName[CharCnt]; Level[LvlCnt] = '\0'; if (OldName[CharCnt] == ':') /* Preceded by drive: ? */ if (DriveFound) /* Already a drive found! */ NError = TRUE; else { DriveFound = TRUE; strncpy (NewName, OldName, CharCnt + 1); NewName[CharCnt + 1] = '\0'; if (CharCnt != 1 || toupper (OldName[0]) < 'A' || toupper (OldName[0]) > 'Z') /* Test drive validity */ Bye (RETURNERROR, "\nERROR - Invalid drivename %s\n", NewName); if (CharCnt == PathLenOldName - 1) Ready = TRUE; /* Only a driveletter given */ else if (CharCnt == PathLenOldName - 2 && OldName[CharCnt + 1] == '\\') { Ready = TRUE; /* Exceptional case: only drive:\ given */ strcpy (NewName, OldName); } } else { if (CharCnt == PathLenOldName) /* Handling last part ? */ Ready = TRUE; if (CharCnt == PathLenOldName - 1 && OldName[CharCnt] == '\\') Ready = TRUE; /* Ended with backslash */ PointDone = FALSE; for (PartChar = 0 ; PartChar < strlen (Level) ; PartChar ++) switch (Level[PartChar]) { case '.' : if (!PointDone) { strncpy (Part, Level, PartChar < 8 ? PartChar : 8); Part[PartChar < 8 ? PartChar : 8] = '\0'; /* Cut >8 characters */ strcat (NewName, Part); strcat (NewName, "."); /* Add the '.' */ Ex = PartChar + 1; PointDone = TRUE; } else if (strcmp (Level, "..")) /* Exceptional case */ NError = TRUE; break; case ';' : case ',' : case '\'' : case '/' : case '(' : case ')' : case '[' : case ']' : /* Characters '>', '<', '|' '"' and '\' have already been taken out */ case '=' : NError = TRUE; /* All bad characters */ break; } if (!PointDone) /* Finish filenames without extension */ { strncpy (Part, Level, PartChar < 8 ? PartChar : 8); Part[PartChar < 8 ? PartChar : 8] = '\0'; strcat (NewName, Part); PointDone = TRUE; } else /* This also deals with the second point in '..' */ { strncpy (Part, Level + Ex, PartChar - Ex < 3 ? PartChar - Ex : 3); /* Cut >3 characters */ Part[PartChar - Ex < 3 ? PartChar - Ex : 3] = '\0'; strcat (NewName, Part); } if (!Ready) strcat (NewName, "\\"); /* Add the subdir character */ } } if (NError) /* Report bad characters */ Bye (RETURNERROR, "\nERROR - Invalid name %s\n", OldName); if (!strlen (NewName) && OldName[0] == '\\') /* Input was root dir */ return ("\\"); /* Which should be treated differently */ else return (NewName); /* Return (modified) string */ } void _fastcall GetWadInfo (int WadNumber, boolean GoThere) /**********************************************************************************************************************************/ /* Pre : 'WadNumber' holds the WAD file number in memory that should be checked. */ /* 'GoThere' is TRUE if the routine should first go to the required drive and directory. */ /* Post : The WAD directory of the file has been found and read out. The structure 'wadinfo' has been updated. */ /* Import: Bye. */ /**********************************************************************************************************************************/ { FILE *Fp; struct WadDirectory_s WadDirectory; struct WadHeader_s WadHeader; char Identifier[9]; char DrivePath[_MAX_DIR]; int register O; int register P; long register Entries; boolean More; if (GoThere) /* Jump to the directory if needed */ { _dos_setdrive (WadInfo[WadNumber]->Drive, &Dummy); free (getcwd (DrivePath, _MAX_DIR - 1)); /* Collect current directory */ chdir (WadInfo[WadNumber]->Path); } if (!(Fp = fopen (WadInfo[WadNumber]->OrigName, "rb"))) Bye (RETURNERROR, "\nERROR - Error opening file %s\n", WadInfo[WadNumber]->OrigName); if (fread (&WadHeader, 1, sizeof (struct WadHeader_s), Fp) != sizeof (struct WadHeader_s)) /* Read the WAD header */ Bye (RETURNERROR, "\nERROR - Error reading file %s\n", WadInfo[WadNumber]->OrigName); if (strnicmp (WadHeader.Type, "PWAD", 4) && strnicmp (WadHeader.Type, "IWAD", 4)) /* Is it a WAD file ? */ Bye (RETURNERROR, "\nERROR - File %s is not a WAD file\n", WadInfo[WadNumber]->OrigName); if (fseek (Fp, WadHeader.DirStart, SEEK_SET)) /* Go to the WAD 'directory' part of the file */ Bye (RETURNERROR, "\nERROR - Error reading file %s\n", WadInfo[WadNumber]->OrigName); WadInfo[WadNumber]->NewStuff = 0x00; /* Clear all entries */ WadInfo[WadNumber]->NewLevels1 = 0x00000000; WadInfo[WadNumber]->NewLevels2 = 0x00000000; WadInfo[WadNumber]->NewLevels3 = 0x00000000; WadInfo[WadNumber]->NewLevels4 = 0x00; Entries = -1; /* Count all WAD 'directory' entries */ while (++ Entries < WadHeader.DirSize) /* The number of entries was found in the WAD header */ { if (fread (&WadDirectory, 1, sizeof (struct WadDirectory_s), Fp) != sizeof (struct WadDirectory_s)) /* Read an entry */ Bye (RETURNERROR, "\nERROR - Error reading file %s\n", WadInfo[WadNumber]->OrigName); for (O = 0 ; O < ENTRYLEN ; O ++) /* Fill the identifier to 8 positions */ Identifier[O] = WadDirectory.Name[O]; Identifier[ENTRYLEN] = '\0'; /* And make it a string */ More = TRUE; /* Now test it against all types and signal successes */ for (O = 0 ; O < MAXIGNORE && More ; O ++) if (!strnicmp (Identifier, IdIgnore[O], strlen (IdIgnore[O]))) More = FALSE; if (More) for (O = 0 ; O < MAXCOLORS && More ; O ++) if (!strnicmp (Identifier, IdColors[O], strlen (IdColors[O]))) { More = FALSE; WadInfo[WadNumber]->NewStuff |= NEWCOLORS; } if (More) for (O = 0 ; O < MAXDEMOS && More ; O ++) if (!strnicmp (Identifier, IdDemos[O], strlen (IdDemos[O]))) { More = FALSE; WadInfo[WadNumber]->NewStuff |= NEWDEMOS; } if (More) for (O = 0 ; O < MAXLEVELS && More ; O ++) if (!strnicmp (Identifier, IdLevels[O], strlen (IdLevels[O]))) { More = FALSE; P = atoi (Identifier + strlen (IdLevels[O])); if (P <= 32) WadInfo[WadNumber]->NewLevels1 |= ((unsigned long)1 << (P - 1)); else if (P <= 64) WadInfo[WadNumber]->NewLevels2 |= ((unsigned long)1 << (P - 33)); else if (P <= 96) WadInfo[WadNumber]->NewLevels3 |= ((unsigned long)1 << (P - 65)); else WadInfo[WadNumber]->NewLevels4 |= ((unsigned short)1 << (P - 97)); } if (More) for (O = 0 ; O < MAXSPRITES && More ; O ++) if (!strnicmp (Identifier, IdSprites[O], strlen (IdSprites[O]))) { More = FALSE; WadInfo[WadNumber]->NewStuff |= NEWSPRITES; } if (More) for (O = 0 ; O < MAXSOUNDS && More ; O ++) if (!strnicmp (Identifier, IdSounds[O], strlen (IdSounds[O]))) { More = FALSE; WadInfo[WadNumber]->NewStuff |= NEWSOUNDS; } if (More) for (O = 0 ; O < MAXMUSIC && More ; O ++) if (!strnicmp (Identifier, IdMusic[O], strlen (IdMusic[O]))) { More = FALSE; WadInfo[WadNumber]->NewStuff |= NEWMUSIC; } if (More && WadDirectory.Start != 0x00000000 && Identifier[0] != '\0') /* All other identifiers are counted as graphics */ WadInfo[WadNumber]->NewStuff |= NEWGRAPHS; } fclose (Fp); if (GoThere) { chdir (DrivePath); _dos_setdrive (CurDrive, &Dummy); /* Return to home location */ } } void _fastcall WriteWadInfo (char *FileName) /**********************************************************************************************************************************/ /* Pre : 'FileName' holds the name of the file (which is the same as set in 'InfoFile'). */ /* Post : All found WAD files are considered. All files have their fields 'NewStuff' and 'NewLevels' initialized. This info is */ /* converted into readable text and written to the file, together with the path information of the WAD file. */ /* Import: Bye. */ /**********************************************************************************************************************************/ { FILE *Fp; char register Memory; char register P; char register Q; char T[81]; int register WadNumber; boolean First; boolean New; boolean More; if (!(Fp = fopen (FileName, "w"))) Bye (RETURNERROR, "\nERROR - Cannot write WAD info file\n"); for (WadNumber = 0 ; WadNumber < TotalWads ; WadNumber ++) { More = FALSE; S[0] = '\0'; fprintf (Fp, "%d %s %s ", WadInfo[WadNumber]->Drive, WadInfo[WadNumber]->Path, WadInfo[WadNumber]->OrigName); More = (WadInfo[WadNumber]->NewLevels1 != 0x00000000 || WadInfo[WadNumber]->NewLevels2 != 0x00000000 || WadInfo[WadNumber]->NewLevels3 != 0x00000000 || WadInfo[WadNumber]->NewLevels4 != 0x00); /* Are their any patch levels in this file ? */ if (More) /* Skip if not */ { Memory = -1; First = TRUE; for (Q = 0 ; Q < 32 ; Q ++) /* Handle all level-bits */ if (WadInfo[WadNumber]->NewLevels1 & ((unsigned long)1 << Q)) { if (Memory == -1) if (First) { sprintf (T, "%s%02d", IdLevels[MAXLEVELS], (int)Q + 1); strcat (S, T); First = FALSE; Memory = Q + 1; New = FALSE; } else { sprintf (T, ",%02d", (int)Q + 1); strcat (S, T); Memory = Q + 1; New = FALSE; } else { Memory ++; New = TRUE; } } else { if (Memory > 0 && New) { sprintf (T, "-%02d", (int)Memory); strcat (S, T); } Memory = -1; } for (Q = 0 ; Q < 32 ; Q ++) if (WadInfo[WadNumber]->NewLevels2 & ((unsigned long)1 << Q)) { if (Memory == -1) if (First) { sprintf (T, "%s%02d", IdLevels[MAXLEVELS], (int)Q + 33); strcat (S, T); First = FALSE; Memory = Q + 33; New = FALSE; } else { sprintf (T, ",%02d", (int)Q + 33); strcat (S, T); Memory = Q + 33; New = FALSE; } else { Memory ++; New = TRUE; } } else { if (Memory > 0 && New) { sprintf (T, "-%02d", (int)Memory); strcat (S, T); } Memory = -1; } for (Q = 0 ; Q < 32 ; Q ++) if (WadInfo[WadNumber]->NewLevels3 & ((unsigned long)1 << Q)) { if (Memory == -1) if (First) { sprintf (T, "%s%02d", IdLevels[MAXLEVELS], (int)Q + 65); strcat (S, T); First = FALSE; Memory = Q + 65; New = FALSE; } else { sprintf (T, ",%02d", (int)Q + 65); strcat (S, T); Memory = Q + 65; New = FALSE; } else { Memory ++; New = TRUE; } } else { if (Memory > 0 && New) { sprintf (T, "-%02d", (int)Memory); strcat (S, T); } Memory = -1; } for (Q = 0 ; Q < 3 ; Q ++) if (WadInfo[WadNumber]->NewLevels4 & ((unsigned short)1 << Q)) { if (Memory == -1) if (First) { sprintf (T, "%s%02d", IdLevels[NUMLEVEL], (int)Q + 97); strcat (S, T); First = FALSE; Memory = Q + 97; New = FALSE; } else { sprintf (T, ",%02d", (int)Q + 97); strcat (S, T); Memory = Q + 97; New = FALSE; } else { Memory ++; New = TRUE; } } else { if (Memory > 0 && New) { sprintf (T, "-%02d", (int)Memory); strcat (S, T); } Memory = -1; } if (Memory > 0 && New) { sprintf (T, "-%02d", (int)Memory); strcat (S, T); } sprintf (T, "%-10s", S); strcpy (S, T); } /* No new levels in this WAD file */ else /* If only one type was found (for example, only sounds) */ if (!(WadInfo[WadNumber]->NewStuff ^ NEWCOLORS)) /* Then use the complete word ('sounds' in this example) */ sprintf (S, "%s", IdColors[MAXCOLORS + 1]); else if (!(WadInfo[WadNumber]->NewStuff ^ NEWDEMOS)) sprintf (S, "%s", IdDemos[MAXDEMOS + 1]); else if (!(WadInfo[WadNumber]->NewStuff ^ NEWSOUNDS)) sprintf (S, "%s", IdSounds[MAXSOUNDS + 1]); else if (!(WadInfo[WadNumber]->NewStuff ^ NEWMUSIC)) sprintf (S, "%s", IdMusic[MAXMUSIC + 1]); else if (!(WadInfo[WadNumber]->NewStuff ^ NEWSPRITES)) sprintf (S, "%s", IdSprites[MAXSPRITES + 1]); else if (!(WadInfo[WadNumber]->NewStuff ^ NEWGRAPHS)) sprintf (S, "%s", IdGraphics[MAXGRAPHS + 1]); else { More = TRUE; /* More than one type found */ strcpy (S, NoLevel); /* Clear level string-part */ } if (More) /* Levels found or more types than 1 */ { if (WadInfo[WadNumber]->NewStuff & NEWCOLORS) /* Print one character to indicate the type */ strcat (S, IdColors[MAXCOLORS]); if (WadInfo[WadNumber]->NewStuff & NEWDEMOS) strcat (S, IdDemos[MAXDEMOS]); if (WadInfo[WadNumber]->NewStuff & NEWSOUNDS) strcat (S, IdSounds[MAXSOUNDS]); if (WadInfo[WadNumber]->NewStuff & NEWMUSIC) strcat (S, IdMusic[MAXMUSIC]); if (WadInfo[WadNumber]->NewStuff & NEWSPRITES) strcat (S, IdSprites[MAXSPRITES]); if (WadInfo[WadNumber]->NewStuff & NEWGRAPHS) strcat (S, IdGraphics[MAXGRAPHS]); } sprintf (WadInfo[WadNumber]->Info, "%-16s", S); /* MAXINFOLEN */ fprintf (Fp, "%-16s\n", S); } fclose (Fp); } int _fastcall NextString (FILE *Fp, char *String) /**********************************************************************************************************************************/ /* Pre : 'Fp' points to the open CONFIGFILE, 'String' holds the address of the string to be read. */ /* Post : Any string is read. If it started with a '#' (COMMENT character), then the rest of the line has been ignored. The */ /* function does not return before a string was read WITHOUT this character or EOF has been reached. The result of the */ /* function is the length of the read string, or 0 if EOF was encountered. */ /* Import: Bye. */ /**********************************************************************************************************************************/ { char Ch; char Cnt; boolean Ready = FALSE; boolean SkipSpaces; String[0] = '\0'; while (!Ready) /* Read until a valid string is found */ { SkipSpaces = TRUE; while (SkipSpaces) { fscanf (Fp, "%c", &Ch); /* Read until no white-spaces found */ if (feof (Fp)) { String[0] = '\0'; /* Or until EOF */ return (0); } SkipSpaces = isspace (Ch); } if (Ch == COMMENT) /* First character is the COMMENT character ? */ do { fscanf (Fp, "%c", &Ch); /* Ignore until end of the line */ if (feof (Fp)) { String[0] = '\0'; /* Or until EOF */ return (0); } } while (Ch != '\n'); else Ready = TRUE; } Cnt = 0; Ready = FALSE; while (!Ready) { while (!isspace (Ch) && Ch != '"') /* Trap quoted argument(part)s */ { String[Cnt ++] = Ch; fscanf (Fp, "%c", &Ch); /* Read until first white-space */ if (feof (Fp)) { String[Cnt] = '\0'; /* Or until EOF */ return (Cnt); } } if (Ch == '"') /* Handle quoted part */ { do { fscanf (Fp, "%c", &Ch); if (feof (Fp)) Bye (RETURNERROR, "ERROR - Unexpected end of configuration file\n"); if (Ch == '\n') Bye (RETURNERROR, "ERROR - Unexpected end of line in configuration file\n"); String[Cnt ++] = Ch; } while (Ch != '"'); Cnt --; fscanf (Fp, "%c", &Ch); /* Read first after */ if (feof (Fp)) { String[Cnt] = '\0'; /* Or until EOF */ return (Cnt); } } else Ready = TRUE; } String[Cnt] = '\0'; return (Cnt); } void _fastcall HandleOptWaddir (char *Item) /**********************************************************************************************************************************/ /* Pre : 'Item' holds an operand that has been read following an 'WADDIR' option. */ /* Post : If 'Item' holds '/S' (or '-S'), then the previously declared WadDir is flagged 'DoSubDirs'. Otherwise, 'Item' holds a */ /* filename, that is included in the WadDir list. If any error occurs, then no return is made. */ /* Import: TestName, Bye. */ /**********************************************************************************************************************************/ { int register DriveNo; if (!stricmp (Item, DOSUB1) || !stricmp (Item, DOSUB2)) if (TotalWadDirs == 0) Bye (RETURNERROR, "ERROR - Badly placed switch %s in WADDIR field\n", DOSUB1); else WadDir[TotalWadDirs - 1]->DoSubDirs = TRUE; else { if (TotalWadDirs == MAXWADDIRS) Bye (RETURNERROR, "ERROR - Too many WADDIR entries\n"); if ((WadDir[TotalWadDirs] = ((struct WadDir_s far *)_fmalloc ((size_t)sizeof (struct WadDir_s)))) == NOMEM) Bye (RETURNERROR, "FATAL ERROR - Out of memory\n"); strcpy (S, TestName (Item)); if (S[1] == ':') /* Preceded by drive: ? */ { if (strlen (S) == 2) /* Just a drive, no path ? */ Bye (RETURNERROR, "ERROR - Missing path for WADDIR in configuration file\n"); DriveNo = ToNumber (S[0]); if (DriveNo != CurDrive) /* A new drive ? */ WadDir[TotalWadDirs]->Drive = DriveNo; /* Store drive number */ else WadDir[TotalWadDirs]->Drive = 0; /* Signal: no new drive given */ strcpy (WadDir[TotalWadDirs]->Name, strupr (S + 2)); /* Store path */ } else { WadDir[TotalWadDirs]->Drive = 0; /* Signal: no drive given */ strcpy (WadDir[TotalWadDirs]->Name, strupr (S)); } WadDir[TotalWadDirs]->DoSubDirs = FALSE; TotalWadDirs ++; } } void _fastcall HandleOptDoomdir (char *Item) /**********************************************************************************************************************************/ /* Pre : 'Item' holds the operand that has been read following a 'DOOMDIR' option. */ /* Post : The (global) variables 'DoomDrive' and 'DoomDirectory' have been initialized. */ /* Import: TestName, Bye. */ /**********************************************************************************************************************************/ { int register DriveNo; strcpy (S, TestName (Item)); if (S[1] == ':') /* Preceded by drive: ? */ { if (strlen (S) == 2) /* Just a drive, no path ? */ Bye (RETURNERROR, "ERROR - Missing path for DOOMDIR in configuration file\n"); DriveNo = ToNumber (S[0]); if (DriveNo != CurDrive) /* A new drive ? */ DoomDrive = DriveNo; /* Store drive number */ else DoomDrive = 0; /* Signal: no new drive given */ strcpy (DoomDirectory, strupr (S + 2)); /* Store path */ } else { DoomDrive = 0; /* Signal: no drive given */ strcpy (DoomDirectory, strupr (S)); } } void _fastcall HandleOptAutoinc (char *Item) /**********************************************************************************************************************************/ /* Pre : 'Item' holds a filename that has been read following an 'AUTOINCLUDE' option. */ /* Post : The filename is include in the AutoInc list. If any error occurs, then no return is made. */ /* Import: TestName, Bye. */ /**********************************************************************************************************************************/ { int register DriveNo; int register Index; if (TotalAutoInc == MAXAUTOINCLUDE) Bye (RETURNERROR, "ERROR - Too many AUTOINCLUDE entries\n"); if ((AutoInc[TotalAutoInc] = ((struct WadInfo_s far *)_fmalloc ((size_t)sizeof (struct WadInfo_s)))) == NOMEM) Bye (RETURNERROR, "FATAL ERROR - Out of memory\n"); strcpy (S, TestName (Item)); if (S[1] == ':') /* Preceded by drive: ? */ { if (strlen (S) == 2) /* Just a drive, no path ? */ Bye (RETURNERROR, "ERROR - Missing path for AUTOINCLUDE in configuration file\n"); DriveNo = ToNumber (S[0]); if (DriveNo != CurDrive) /* A new drive ? */ AutoInc[TotalAutoInc]->Drive = DriveNo; /* Store drive number */ else AutoInc[TotalAutoInc]->Drive = 0; /* Signal: no new drive given */ strcpy (AutoInc[TotalAutoInc]->Path, strupr (S + 2)); /* Store path */ } else { AutoInc[TotalAutoInc]->Drive = 0; /* Signal: no drive given */ strcpy (AutoInc[TotalAutoInc]->Path, strupr (S)); } Index = strlen (AutoInc[TotalAutoInc]->Path) - 1; while (AutoInc[TotalAutoInc]->Path[Index] != '\\' && Index > 0) Index --; if (Index == 0) { strcpy (AutoInc[TotalAutoInc]->OrigName, AutoInc[TotalAutoInc]->Path); /* No preceding path */ strcpy (AutoInc[TotalAutoInc]->Path, DEFAULTWADDIR); } else { strcpy (AutoInc[TotalAutoInc]->OrigName, AutoInc[TotalAutoInc]->Path + Index + 1); /* Copy over last part (filename) */ AutoInc[TotalAutoInc]->Path[Index] = '\0'; /* Cut filename from path */ } TotalAutoInc ++; } void _fastcall HandleOptAddSwitches (char *Item) /**********************************************************************************************************************************/ /* Pre : 'Item' holds the operand that has been read following an 'ADDSWITCHES' option. */ /* Post : If 'Item' holds a valid switch, then the 'InUse' flag of that switch is set. Otherwise no return is made. */ /* Import: Bye. */ /**********************************************************************************************************************************/ { int register Index; if (Item[0] == '-') /* Remember that all switches start with a '-' character */ { Index = 0; while (Index < MAXADDSWITCHES && stricmp (Item, ExtCom[Index].Command)) /* Test the name against all allowed switches */ Index ++; if (Index == MAXADDSWITCHES) Bye (RETURNERROR, "ERROR - Switch %s not supported in ADDSWITCHES\n", Item); ExtCom[Index].InUse = TRUE; } else Bye (RETURNERROR, "ERROR - Unrecognised ADDSWITCHES %s in configuration file\n", Item); } void ReadConfig (void) /**********************************************************************************************************************************/ /* Pre : The (global) 'ConfigFile' should be initialized. */ /* Post : If a configuration file is found, then it has been read out. Only 8 keywords are recognised: */ /* - DOOMDIR, after which the name of the main DOOM directory should be given. */ /* - WADDIR, after which a maximum of 400 WAD directories may be given; If an entry '/S' or '-S' is encountered, then all */ /* subdirectories of the previously declared directory will also be used as WADDIRs. */ /* - WADINFOFILE, after which a WAD info file may be given. All simple errors are reported. */ /* - SERDRIVER, after which a new serial driver should be given (in stead of SERSETUP); */ /* - IPXDRIVER, after which a new network driver should be given (in stead of IPXSETUP); */ /* - SETSKILL, after which the default skill (1-5) must be given. */ /* - DEATHMATCH (no parameters). This means that deathmatch will be set as default. */ /* - AUTOINCLUDE, after which a maximum of 5 WAD files (complete with (partial) path) may be given. These files will then */ /* automatically be selected when starting. If the file is not crossed when reading the directories (WADDIRs), then the */ /* file is just not selected (no error will be generated). */ /* - NOSEARCH (no parameters). If this keyword is given, then the program will not search all given WADDIR directories, */ /* it will use the WADINFOFILE instead and use all named entries instead. (This should be used with caution!) */ /* - SETCOMPORT, after which the default COM port (1-4) must be given for null-modem link. */ /* - SETNODES, after which the default number of players (2-4) must be given for IPX link. */ /* - SETSOCKET, after which a network socket (0-255) must be given. */ /* - SETPLAYTYPE, after which one of the keywords "ALONE", "IPX" or "SERIAL" must be given. */ /* - ADDSWITCHES, after which all the direct DOOM switches should be typed. */ /* - SORTFILES, after which one of the keywords "NAME" or "INFO" must be given. */ /* - NOFULLNAME (no parameters). Will stop using "music" i.s.o. "- m". */ /* - NOAUTORETURN (no parameters). If this option is given, then no is passed to DOOM at startup. */ /* */ /* If one (or all) are not found, then they are initialized with the defaults. */ /* */ /* If a character '#' is encountered, then the rest of this line is ignored (comment). */ /* Import: NextString, HandleOptWaddir, HandleOptAutoinc, HandleOptAddSwitches, HandleOptDoomdir, Bye. */ /**********************************************************************************************************************************/ { FILE *Fp; char Item[256]; char ContinueOption; boolean Handled; TotalWadDirs = 0; /* Initialize all counters */ TotalAutoInc = 0; strcpy (DoomDirectory, DEFAULTWADDIR); /* Use the current directory as default */ strcpy (InfoFile, DEFAULTINFOFILE); /* Initialize a default file name */ strcpy (IpxDriver, STARTIPX); strcpy (SerDriver, STARTLINK); if (Fp = fopen (ConfigFile, "r")) /* Skip if no CONFIGFILE was found */ { NextString (Fp, Item); /* Read-ahead first string: must be a keyword */ while (!feof (Fp)) { Handled = FALSE; for (M = 0 ; !Handled && M < NUMOPTIONS ; M ++) if (!stricmp (Item, ConfOp[M].OptionName)) { if (ConfOp[M].OptionType != OPT_SET) /* Option takes one (or more) operand(s) ? */ { NextString (Fp, Item); /* Read the operand */ if (Item[0] == '\0') Bye (RETURNERROR, "ERROR - Missing operand after switch %s\n", ConfOp[M].OptionName); } ContinueOption = 0; /* Signal: no option has more than one operand */ switch (ConfOp[M].OptionType) { case OPT_DOOMDIR : HandleOptDoomdir (Item); break; case OPT_FILE : strcpy (S, TestName (Item)); if (S[1] == ':' && S[2] == '\0') /* Only a drive given */ Bye (RETURNERROR, "ERROR - Missing pathname after %s in switch %s\n", S, ConfOp[M].OptionName); strcpy ((char *)ConfOp[M].DataPtr, S); break; case OPT_NUM : for (N = 0 ; N < strlen (Item) ; N ++) if (strlen (Item) > 3 || !isdigit (Item[N])) Bye (RETURNERROR, "ERROR - Invalid number %s after switch %s\n", Item, ConfOp[M].OptionName); *((int *)ConfOp[M].DataPtr) = atoi (Item); break; case OPT_SET : *((char *)ConfOp[M].DataPtr) = TRUE; break; case OPT_STRING : strcpy ((char *)ConfOp[M].DataPtr, Item); break; case OPT_WADDIR : ContinueOption = 1; HandleOptWaddir (Item); break; case OPT_AUTOINC : ContinueOption = 2; HandleOptAutoinc (Item); break; case OPT_ADDSWIT : ContinueOption = 3; HandleOptAddSwitches (Item); break; case OPT_PLAYTYP : if (!stricmp (Item, "ALONE")) PlayTypeActive = 1; else if (!stricmp (Item, "IPX")) PlayTypeActive = 2; else if (!stricmp (Item, "SERIAL")) PlayTypeActive = 3; else Bye (RETURNERROR, "ERROR - Unknown playtype %s\n", Item); break; case OPT_SORTFIL : SortWadFiles = TRUE; if (!stricmp (Item, "NAME") || !stricmp (Item, "INFO")) SortByName = !stricmp (Item, "NAME"); else Bye (RETURNERROR, "ERROR - Unknown sort criteria %s after keyword SORTFILES\n", Item); break; } Handled = TRUE; } if (!Handled) /* Read item is not one of the keywords */ switch (ContinueOption) { case 0 : Bye (RETURNERROR, "ERROR - Unknown keyword %s in configuration file\n", Item); case 1 : HandleOptWaddir (Item); break; case 2 : HandleOptAutoinc (Item); break; case 3 : HandleOptAddSwitches (Item); } NextString (Fp, Item); /* Read next option */ } fclose (Fp); } else if (ConfigChange) /* It is an error if a different file was given */ Bye (RETURNERROR, "ERROR - configuration file not found\n"); OtherSerDriver = strcmp (SerDriver, STARTLINK); OtherIpxDriver = strcmp (IpxDriver, STARTIPX); if (TotalWadDirs == 0) /* No CONFIGFILE or WADDIR entries found ? */ { if ((WadDir[0] = ((struct WadDir_s far *)_fmalloc ((size_t)sizeof (struct WadDir_s)))) == NOMEM) Bye (RETURNERROR, "FATAL ERROR - Out of memory\n"); WadDir[0]->Drive = 0; /* Then use the DOOM directory as default */ strcpy (WadDir[0]->Name, DoomDirectory); TotalWadDirs = 1; } if (DifficultyActive == 0 || DifficultyActive > 5) /* Filter out nonsense numerical options */ Bye (RETURNERROR, "ERROR - Invalid skill number %d\n", DifficultyActive); if (CommPortActive == 0 || CommPortActive > 4) Bye (RETURNERROR, "ERROR - Invalid COM port number %d\n", CommPortActive); if (NumNodesActive < 2 || NumNodesActive > 4) Bye (RETURNERROR, "ERROR - You cannot play network DOOM with %d player(s)\n", NumNodesActive); if (DeathmatchOn) { DeathmatchOn = FALSE; /* Select the better deathmatch version if available */ DeathmatchV2On = TRUE; } } void _fastcall PrintDifficulties (int HighLite) /**********************************************************************************************************************************/ /* Pre : 'HighLite' contains the pointed difficulty, or 0 if none was pointed to. */ /* Post : The difficulties have been printed. The active difficulty has a checked box, all others have empty boxes. The pointed */ /* difficulty is printed in a different color. */ /* Import: PrText. */ /**********************************************************************************************************************************/ { for (M = 0 ; M < NUMDIFFICULTY ; M ++) if (M == HighLite - 1) PrText ((M == DifficultyActive - 1), 3 + M, 28, LRED, Difficulties[M]); else PrText ((M == DifficultyActive - 1), 3 + M, 28, LMAGENTA, Difficulties[M]); } void _fastcall PrintPlayTypes (int HighLite) /**********************************************************************************************************************************/ /* Pre : 'HighLite' contains the pointed playtype, or 0 if none was pointed to. */ /* Post : The playtypes have been printed. The active playtype has a checked box, all others have empty boxes. The pointed */ /* playtype is printed in a different color. */ /* Import: PrText. */ /**********************************************************************************************************************************/ { for (M = 0 ; M < NUMPLAYTYPE ; M ++) if (M == HighLite - 1) PrText ((M == PlayTypeActive - 1), 3 + M, 55, LRED, PlayTypes[M]); else PrText ((M == PlayTypeActive - 1), 3 + M, 55, LMAGENTA, PlayTypes[M]); switch (PlayTypeActive) { case 1: PrText (-1, 6, 55, DBLACK, " "); /* Alone */ break; case 2: if (!OtherIpxDriver) /* IPX compatible */ if (HighLite == 4) PrText (-1, 6, 55, LRED, "(%c) %s", NumNodesActive + '0', NumberNodes); else PrText (-1, 6, 55, LMAGENTA, "(%c) %s", NumNodesActive + '0', NumberNodes); else PrText (-1, 6, 55, LBLACK, "(%c) %s", NumNodesActive + '0', NumberNodes); break; case 3: if (!OtherSerDriver) /* Null-modem link */ if (HighLite == 4) PrText (-1, 6, 55, LRED, "(%c) %s", CommPortActive + '0', CommPort); else PrText (-1, 6, 55, LMAGENTA, "(%c) %s", CommPortActive + '0', CommPort); else PrText (-1, 6, 55, LBLACK, "(%c) %s", CommPortActive + '0', CommPort); } } void _fastcall PrintRespawnMonsters (boolean HighLite) /**********************************************************************************************************************************/ /* Pre : 'HighLite' is TRUE if this item was pointed to. */ /* Post : The RESPAWN text has been printed, with a checked box before it if it was selected, or empty otherwise. */ /* If 'HighLite' was TRUE, then the text is printed in a different color. */ /* Import: PrText. */ /**********************************************************************************************************************************/ { if (HighLite) PrText (RespMonstersOn, 10, 28, LRED, RespawnMonsters); else PrText (RespMonstersOn, 10, 28, LMAGENTA, RespawnMonsters); } void _fastcall PrintNoMonsters (boolean HighLite) /**********************************************************************************************************************************/ /* Pre : 'HighLite' is TRUE if this item was pointed to. */ /* Post : The NOMONSTERS text has been printed, with a checked box before it if it was selected, or empty otherwise. */ /* If 'HighLite' was TRUE, then the text is printed in a different color. */ /* Import: PrText. */ /**********************************************************************************************************************************/ { if (HighLite) PrText (NoMonstersOn, 9, 28, LRED, NoMonsters); else PrText (NoMonstersOn, 9, 28, LMAGENTA, NoMonsters); } void _fastcall PrintFastMonsters (boolean HighLite) /**********************************************************************************************************************************/ /* Pre : 'HighLite' is TRUE if this item was pointed to. */ /* Post : The FASTMONSTERS text has been printed, with a checked box before it if it was selected, or empty otherwise. */ /* If 'HighLite' was TRUE, then the text is printed in a different color. */ /* Import: PrText. */ /**********************************************************************************************************************************/ { if (HighLite) PrText (FastMonstersOn, 11, 28, LRED, FastMonsters); else PrText (FastMonstersOn, 11, 28, LMAGENTA, FastMonsters); } void _fastcall PrintDeathmatch (boolean HighLite) /**********************************************************************************************************************************/ /* Pre : 'HighLite' is TRUE if this item was pointed to. */ /* Post : The DEATHMATCH text has been printed, with a checked box before it if it was selected, or empty otherwise. */ /* If 'HighLite' was TRUE, then the text is printed in a different color. */ /* Import: PrText. */ /**********************************************************************************************************************************/ { if (HighLite) PrText (DeathmatchOn, 8, 55, LRED, Deathmatch); else PrText (DeathmatchOn, 8, 55, LMAGENTA, Deathmatch); } void _fastcall PrintV2Deathmatch (boolean HighLite) /**********************************************************************************************************************************/ /* Pre : 'HighLite' is TRUE if this item was pointed to. */ /* Post : The DEATHMATCH V2 text has been printed, with a checked box before it if it was selected, or empty otherwise. */ /* If 'HighLite' was TRUE, then the text is printed in a different color. */ /* Import: PrText. */ /**********************************************************************************************************************************/ { if (HighLite) PrText (DeathmatchV2On, 9, 55, LRED, DeathmatchV2); else PrText (DeathmatchV2On, 9, 55, LMAGENTA, DeathmatchV2); } void _fastcall PrintLevelPagers (int HighLite) /**********************************************************************************************************************************/ /* Pre : 'HighLite' is 1 for left, 2 for right or 0 for no pager. */ /* Post : The pagers have been printed. The 'HighLite' pager in a different color. */ /* Import: PrText. */ /**********************************************************************************************************************************/ { if (CurrentLevel > 1) /* Are there previous levels ? */ if (HighLite == 1) /* Print pager for 'previous' level (left) */ PrText (-1, 3, 3, LRED, PreviousLevel); else PrText (-1, 3, 3, LWHITE, PreviousLevel); else PrText (-1, 3, 3, LBLACK, PreviousLevel); if (CurrentLevel < NUMLEVEL) /* Are there next levels ? */ if (HighLite == 2) /* Print pager for 'next' level (right) */ PrText (-1, 3, 14, LRED, NextLevel); else PrText (-1, 3, 14, LWHITE, NextLevel); else PrText (-1, 3, 14, LBLACK, NextLevel); } void _fastcall PrintLevel (void) /**********************************************************************************************************************************/ /* Pre : None. */ /* Post : The current level is printed, with the level name below it. */ /* Import: PrText. */ /**********************************************************************************************************************************/ { PrText (-1, 3, 7, DCYAN, "MAP %02d", CurrentLevel); if (CurrentLevel <= 32) PrText (-1, 5, 1, DWHITE, IdLevelNames[CurrentLevel - 1]); else PrText (-1, 5, 1, DWHITE, " "); } void _fastcall PrintWadFiles (void) /**********************************************************************************************************************************/ /* Pre : None. */ /* Post : The active page with WAD files has been printed (as determined by 'CurrentPage') has been printed. All selected WAD */ /* files are printed in a different color, any unused part of the page has been cleared from the screen. After each name */ /* is the read info printed. Highliting of a pointed wadfile is not handled here. */ /* Import: PrText. */ /**********************************************************************************************************************************/ { short register PositionX; short register PositionY; for (M = CurrentPage * PAGE ; M < (CurrentPage + 1) * PAGE ; M += WADHEIGHT) /* Handle each column */ for (N = M ; N < M + WADHEIGHT ; N ++) /* Handle each row in the column */ { PositionY = 13 + (N % WADHEIGHT); PositionX = ((M - CurrentPage * PAGE) / WADHEIGHT) * WADWIDTH + 1; if (N < TotalWads) /* WAD file number exists ? */ { if (WadInfo[N]->Selected) PrText (-1, PositionY, PositionX, DGREEN, WadInfo[N]->Name); /* Print filename */ else PrText (-1, PositionY, PositionX, DWHITE, WadInfo[N]->Name); PrText (-1, PositionY, PositionX + MAXFNAME, DCYAN, WadInfo[N]->Info); /* Print info */ } else /* WAD file number after the last WAD file */ PrText (-1, PositionY, PositionX, DBLACK, " "); /* Clear this screen part */ } } void _fastcall PrintPagers (int HighLite) /**********************************************************************************************************************************/ /* Pre : 'HighLite' is 1 for left, 2 for right or 0 for no pager. */ /* Post : The pagers have been printed. The 'HighLite' pager in a different color. */ /* Import: PrText. */ /**********************************************************************************************************************************/ { if (CurrentPage > 0) /* Are there previous pages ? */ if (HighLite == 1) /* Print pager for 'previous' page (left) */ PrText (-1, 11, 60, LRED, PreviousPage); else PrText (-1, 11, 60, LWHITE, PreviousPage); else PrText (-1, 11, 60, LBLACK, PreviousPage); if (CurrentPage < LastPage) /* Are there next pages ? */ if (HighLite == 2) /* Print pager for 'next' page (right) */ PrText (-1, 11, 78, LRED, NextPage); else PrText (-1, 11, 78, LWHITE, NextPage); else PrText (-1, 11, 78, LBLACK, NextPage); } void _fastcall PrintRdPrev (boolean HighLite) /**********************************************************************************************************************************/ /* Pre : 'HighLite' is TRUE if this item is selected. */ /* Post : The read previous text has been printed. If 'HighLite' was TRUE, than in a different color. */ /* Import: PrText. */ /**********************************************************************************************************************************/ { if (HighLite) PrText (-1, 11, 1, LRED, ReadPrevious); else PrText (-1, 11, 1, LMAGENTA, ReadPrevious); } void _fastcall PrintAutomatic (boolean HighLite) /**********************************************************************************************************************************/ /* Pre : 'HighLite' is TRUE if this item is selected. */ /* Post : The automatic text has been printed. If 'HighLite' was TRUE, than in a different color. */ /* Import: PrText. */ /**********************************************************************************************************************************/ { if (HighLite) PrText (-1, 10, 1, LRED, Automatic); else PrText (-1, 10, 1, LMAGENTA, Automatic); } void _fastcall PrintStart (boolean HighLite) /**********************************************************************************************************************************/ /* Pre : 'HighLite' is TRUE if this item is selected. */ /* Post : The start text has been printed. If 'HighLite' was TRUE, than in a different color. */ /* Import: PrText. */ /**********************************************************************************************************************************/ { if (HighLite) PrText (-1, 9, 1, LRED, StartGame); else PrText (-1, 9, 1, LMAGENTA, StartGame); } void _fastcall UnselectPreviousField (char SkipFieldNum) /**********************************************************************************************************************************/ /* Pre : 'SkipFieldNum' holds the field number that should NOT be unselected. */ /* Post : The previous selected field has been unselected, if one was pointed to and it was not 'SkipFieldNum'. */ /* Import: PrintDifficulties, PrintPlayTypes, PrintLevelPagers, PrintDeathmatch, PrintPagers, PrintRdPrev, PrintStart, */ /* PrintAutomatic, PrintDeathmatchV2, PrintRespawnMonsters, PrintNoMonsters, PrintFastMonsters, PrText, HideMouse, */ /* ShowMouse. */ /**********************************************************************************************************************************/ { short register PositionX; short register PositionY; int register OldWadNumber; if (CurrentField != NOFIELD && CurrentField != SkipFieldNum) { switch (CurrentField) { case DIFFICULTYFIELD : PrintDifficulties (0); break; case PLAYTYPEFIELD : PrintPlayTypes (0); break; case LEVELFIELD : PrintLevelPagers (0); break; case DEATHMATCHFIELD : PrintDeathmatch (FALSE); break; case DEATHMATCHV2FIELD : PrintV2Deathmatch (FALSE); break; case RESPAWNFIELD : PrintRespawnMonsters (FALSE); break; case NOMONSTERSFIELD : PrintNoMonsters (FALSE); break; case FASTMONSTERSFIELD : PrintFastMonsters (FALSE); break; case PAGERFIELD : PrintPagers (0); break; case RDPREVFIELD : PrintRdPrev (FALSE); break; case STARTFIELD : PrintStart (FALSE); break; case AUTOMATICFIELD : PrintAutomatic (FALSE); break; case FILEFIELD : PositionY = 13 + ((PreviousWad - 1) % WADHEIGHT); /* Location of previously selected WAD */ PositionX = ((PreviousWad - 1) / WADHEIGHT) * WADWIDTH + 1; OldWadNumber = CurrentPage * PAGE + PreviousWad - 1; /* Number of that WAD */ HideMouse (); if (WadInfo[OldWadNumber]->Selected) PrText (-1, PositionY, PositionX, DGREEN, WadInfo[OldWadNumber]->Name); else PrText (-1, PositionY, PositionX, DWHITE, WadInfo[OldWadNumber]->Name); ShowMouse (); } } } boolean WaitForConfirmation (void) /**********************************************************************************************************************************/ /* Pre : None. */ /* Post : The user must press a key. If this key is 'Y', then TRUE is returned, otherwise FALSE. */ /* Import: None. */ /**********************************************************************************************************************************/ { unsigned int Key; while (!_bios_keybrd (_KEYBRD_READY)) /* Wait for a key */ ; Key = _bios_keybrd (_KEYBRD_READ) & 0x00FF; /* Read pressed key */ return (toupper ((char)Key) == 'Y'); /* Return TRUE if it is 'Y' */ } boolean _fastcall Requester (short Y0, short X0, short DY, short DX) /**********************************************************************************************************************************/ /* Pre : 'Y0' and 'X0' hold the top-left coordinates of the requester that is to be printed. The hight will be 'DY' lines, the */ /* width will be 'DX' columns. */ /* Post : The requester has been drawn. */ /* Note that the requester is surrounded with an empty zone. */ /* Import: HideMouse, UnselectPreviousField, PrText. */ /**********************************************************************************************************************************/ { HideMouse (); /* Clear mousepointer and hi-light bars */ UnselectPreviousField (NOFIELD); CurrentField = NOFIELD; Mouse.CoChange = TRUE; /* Signal: re-hi-light on return */ for (M = 0 ; M < DX ; M ++) PrText (-1, Y0, X0 + M, DBLACK, " "); PrText (-1, Y0 + 1, X0, LRED, " \xC9"); for (M = 0 ; M < (DX - 4) ; M ++) PrText (-1, 0, 0, LRED, "\xCD"); PrText (-1, 0, 0, LRED, "\xBB "); for (M = 2 ; M < (DY - 2) ; M ++) { PrText (-1, Y0 + M, X0, LRED, " \xBA"); for (N = 0 ; N < (DX - 4) ; N ++) PrText (-1, 0, 0, DBLACK, " "); PrText (-1, 0, 0, LRED, "\xBA "); } PrText (-1, Y0 + DY - 2, X0, LRED, " \xC8"); for (M = 0 ; M < (DX - 4) ; M ++) PrText (-1, 0, 0, LRED, "\xCD"); PrText (-1, 0, 0, LRED, "\xBC "); for (M = 0 ; M < DX ; M ++) PrText (-1, Y0 + DY - 1, X0 + M, DBLACK, " "); } void GiveHelp (void) /**********************************************************************************************************************************/ /* Pre : The user pressed [F1]. */ /* Post : A requester has been drawn, containing a listing of all available keys (depending whether a mouse has been found). */ /* The user must press a key to return. */ /* Import: Requester, PrText, WaitForConfirmation, PrintWadFiles, ShowMouse. */ /**********************************************************************************************************************************/ { Requester (14, 2, 16, 78); PrText (-1, 17, 24, DCYAN, "The following keys are available:"); PrText (-1, 19, 5, DWHITE, "%c ", KEY_LEVELUP); PrText (-1, 0, 0, DCYAN, "Level up"); PrText (-1, 20, 5, DWHITE, "%c ", KEY_LEVELDOWN); PrText (-1, 0, 0, DCYAN, "Level down"); PrText (-1, 21, 5, DWHITE, "%c ", KEY_DIFFICULTY); PrText (-1, 0, 0, DCYAN, "Skill"); PrText (-1, 22, 5, DWHITE, "%c ", KEY_PLAYTYPE); PrText (-1, 0, 0, DCYAN, "Playtype"); if (!OtherIpxDriver) { PrText (-1, 25, 5, DWHITE, "%c ", KEY_NODES); PrText (-1, 0, 0, DCYAN, "Number of players"); } PrText (-1, 23, 5, DWHITE, "%c ", KEY_DEATHMATCH); PrText (-1, 0, 0, DCYAN, "Deathmatch"); if (!OtherSerDriver) { PrText (-1, 26, 5, DWHITE, "%c ", KEY_COMPORT); PrText (-1, 0, 0, DCYAN, "COM port"); } PrText (-1, 19, 28, DWHITE, "%c ", KEY_NOMONSTERS); PrText (-1, 0, 0, DCYAN, "No monsters"); PrText (-1, 20, 28, DWHITE, "%c ", KEY_RESPAWNMONST); PrText (-1, 0, 0, DCYAN, "Respawn monsters"); PrText (-1, 24, 5, DWHITE, "%c ", KEY_DEATHMATCHV2); PrText (-1, 0, 0, DCYAN, "Deathmatch v2.0"); PrText (-1, 21, 28, DWHITE, "%c ", KEY_FASTMONSTERS); PrText (-1, 0, 0, DCYAN, "Fast monsters"); PrText (-1, 22, 28, DWHITE, "%c ", KEY_AUTO); PrText (-1, 0, 0, DCYAN, "AUTO SELECT"); PrText (-1, 23, 28, DWHITE, "%c ", KEY_READPREVIOUS); PrText (-1, 0, 0, DCYAN, "READ PREVIOUS"); PrText (-1, 24, 28, DWHITE, "[ENTER] "); PrText (-1, 0, 0, DCYAN, "START DOOM!"); PrText (-1, 25, 28, DWHITE, "[ESC] "); PrText (-1, 0, 0, DCYAN, "Abort EasyWAD"); PrText (-1, 26, 28, DWHITE, "[DEL] "); PrText (-1, 0, 0, DCYAN, "Delete WAD"); PrText (-1, 19, 55, DWHITE, "[PG UP] "); PrText (-1, 0, 0, DCYAN, "Next WAD page"); PrText (-1, 20, 55, DWHITE, "[PG DN] "); PrText (-1, 0, 0, DCYAN, "Previous page"); PrText (-1, 21, 55, DWHITE, "[F1] "); PrText (-1, 0, 0, DCYAN, "This help page"); PrText (-1, 22, 55, DWHITE, "[F5] "); PrText (-1, 0, 0, DCYAN, "Rescan WADDIRs"); PrText (-1, 23, 55, DWHITE, "[F7] "); PrText (-1, 0, 0, DCYAN, "Reset fullname"); PrText (-1, 24, 55, DWHITE, "[F8] "); PrText (-1, 0, 0, DCYAN, "Resort WADs"); if (!UseMouse) { PrText (-1, 25, 55, DWHITE, "[CURS] "); PrText (-1, 0, 0, DCYAN, "Move around"); PrText (-1, 26, 55, DWHITE, "[SPACE] "); PrText (-1, 0, 0, DCYAN, "Select WAD"); } WaitForConfirmation (); for (M = 15 ; M < 29 ; M ++) /* Erase requester */ PrText (-1, M, 3, DBLACK, " "); PrintWadFiles (); ShowMouse (); } void SortFiles (void) /**********************************************************************************************************************************/ /* Pre : None. */ /* Post : If (global) 'SortWadFiles' is TRUE, then the WAD files in memory have been sorted according to the sorttype. */ /* If (global) 'SortByName' is TRUE, then they have been sorted by name, otherwise they have been sorted by info field. */ /* Import: None. */ /**********************************************************************************************************************************/ { boolean More; struct WadInfo_s far *TmpPtr; /* Entry as read from the WADINFOFILE */ if (SortWadFiles) /* SORTFILES given in CONFIGFILE ? */ { More = TRUE; for (N = 0 ; N < TotalWads - 1 && More ; N ++) /* Perform a bubblesort */ { More = FALSE; for (M = 0 ; M < TotalWads - 1 ; M ++) if (SortByName) /* Sort by 'Name' field */ { if (strcmp (WadInfo[M]->Name, WadInfo[M + 1]->Name) > 0) /* Next 'larger' than current ? */ { More = TRUE; TmpPtr = WadInfo[M]; /* Then flip the pointers */ WadInfo[M] = WadInfo[M + 1]; WadInfo[M + 1] = TmpPtr; } } else /* Sort by 'Info' field */ { if (strcmp (WadInfo[M]->Info, WadInfo[M + 1]->Info) > 0) { More = TRUE; TmpPtr = WadInfo[M]; WadInfo[M] = WadInfo[M + 1]; WadInfo[M + 1] = TmpPtr; } } } } } void ConvertFullName (struct WadInfo_s far *ConInfo) /**********************************************************************************************************************************/ /* Pre : 'ConInfo' points to the WadInfo structure that should have its Info field re-examined. */ /* Post : If the Info field contained a full name (e.g. 'music'), then it is converted to the short name (e.g. '- m'). */ /* Before returning, the Info field is expanded to contain exactly MAXINFOLEN characters. */ /* Import: None. */ /**********************************************************************************************************************************/ { int register Index; int register SLNoLevel; /* StrLen NoLevel */ for (Index = strlen (ConInfo->Info) - 1 ; ConInfo->Info[Index] == ' ' && Index > 0 ; Index --) /* Cut trailing spaces */ ConInfo->Info[Index] = '\0'; if (NoFullName) /* Convert full names to short name if wanted */ { if (!strcmp (ConInfo->Info, IdColors[MAXCOLORS + 1])) sprintf (ConInfo->Info, "%s%s", NoLevel, IdColors[MAXCOLORS]); else if (!strcmp (ConInfo->Info, IdDemos[MAXDEMOS + 1])) sprintf (ConInfo->Info, "%s%s", NoLevel, IdDemos[MAXDEMOS]); else if (!strcmp (ConInfo->Info, IdSounds[MAXSOUNDS + 1])) sprintf (ConInfo->Info, "%s%s", NoLevel, IdSounds[MAXSOUNDS]); else if (!strcmp (ConInfo->Info, IdMusic[MAXMUSIC + 1])) sprintf (ConInfo->Info, "%s%s", NoLevel, IdMusic[MAXMUSIC]); else if (!strcmp (ConInfo->Info, IdSprites[MAXSPRITES + 1])) sprintf (ConInfo->Info, "%s%s", NoLevel, IdSprites[MAXSPRITES]); else if (!strcmp (ConInfo->Info, IdGraphics[MAXGRAPHS + 1])) sprintf (ConInfo->Info, "%s%s", NoLevel, IdGraphics[MAXGRAPHS]); } else { SLNoLevel = strlen (NoLevel); /* Just to speed things up a little ... */ if (!strncmp (ConInfo->Info, NoLevel, SLNoLevel)) { if (!strcmp (ConInfo->Info + SLNoLevel, IdColors[MAXCOLORS])) strcpy (ConInfo->Info, IdColors[MAXCOLORS + 1]); else if (!strcmp (ConInfo->Info + SLNoLevel, IdDemos[MAXDEMOS])) strcpy (ConInfo->Info, IdDemos[MAXDEMOS + 1]); else if (!strcmp (ConInfo->Info + SLNoLevel, IdSounds[MAXSOUNDS])) strcpy (ConInfo->Info, IdSounds[MAXSOUNDS + 1]); else if (!strcmp (ConInfo->Info + SLNoLevel, IdMusic[MAXMUSIC])) strcpy (ConInfo->Info, IdMusic[MAXMUSIC + 1]); else if (!strcmp (ConInfo->Info + SLNoLevel, IdSprites[MAXSPRITES])) strcpy (ConInfo->Info, IdSprites[MAXSPRITES + 1]); else if (!strcmp (ConInfo->Info + SLNoLevel, IdGraphics[MAXGRAPHS])) strcpy (ConInfo->Info, IdGraphics[MAXGRAPHS + 1]); } } for (Index = strlen (ConInfo->Info) ; Index < MAXINFOLEN ; Index ++) /* Fill info field to maximum */ ConInfo->Info[Index] = ' '; ConInfo->Info[Index] = '\0'; } void _fastcall HandleFileSort (boolean Toggle) /**********************************************************************************************************************************/ /* Pre : 'Toggle' is TRUE if the sort criterium should be toggled (when called from ToggleFileSort), FALSE if not (when called */ /* from RescanFiles). A requester border should have been previously drawn, together with the top text line. */ /* Post : The question is finished in the requester. The user must confirm the question. If confirmed, then the files are sorted */ /* according to 'Toggle'. If this was TRUE, then the criterium is toggled from "NAME" to "INFO" first. Afterward the */ /* requester has been removed and the files reprinted (if the sort was done, then page 1 is automatically selected). */ /* Import: WaitForConfirmation, PrintWadFiles, ShowMouse, SortFiles, PrText, PrintPagers. */ /**********************************************************************************************************************************/ { PrText (-1, 20, 32, LRED, "RESORT THE FILES ?"); PrText (-1, 22, 31, DRED, "PRESS TO CONFIRM"); if (!WaitForConfirmation ()) /* Acknowledged ? */ { /* Step out if not */ for (M = 16 ; M < 25 ; M ++) /* Erase requester */ PrText (-1, M, 26, DBLACK, " "); PrintWadFiles (); ShowMouse (); return; } if (Toggle) { SortWadFiles = TRUE; SortByName = !SortByName; /* Toggle between "NAME" and "INFO" */ } PrText (-1, 18, 29, DRED, " "); PrText (-1, 20, 32, LRED, " Sorting ... "); PrText (-1, 22, 31, DRED, " "); SortFiles (); for (M = 16 ; M < 25 ; M ++) /* Erase requester */ PrText (-1, M, 26, DBLACK, " "); CurrentPage = 0; CurrentSelected = 0; PrintPagers (0); PrintWadFiles (); ShowMouse (); } void ToggleFileSort (void) /**********************************************************************************************************************************/ /* Pre : The user pressed [F8]. */ /* Post : A requester has been drawn to confirm the action. If this was acknowledged, then the file sort criterium has been */ /* toggled, the files have been resorted and reprinted. */ /* Import: Requester, PrText, HandleFileSort. */ /**********************************************************************************************************************************/ { Requester (15, 25, 11, 32); PrText (-1, 18, 29, DRED, "ARE YOU SURE YOU WANT TO"); HandleFileSort (TRUE); } void RescanFiles (void) /**********************************************************************************************************************************/ /* Pre : The user pressed [F5]. */ /* Post : A requester has been drawn to confirm the action. If this was acknowledged, then all files that are currently in */ /* memory have been rescanned, and the result has been written to the WADINFOFILE. After this, the user has been asked if */ /* the files should be resorted. At the end, all files are reprinted on screen. */ /* Import: Requester, PrText, HandleFileSort, GetWadInfo, WriteWadInfo, WaitForConfirmation, ShowMouse, ConvertFullName. */ /**********************************************************************************************************************************/ { int register CountWadFiles; Requester (15, 25, 11, 32); PrText (-1, 18, 29, DRED, "ARE YOU SURE YOU WANT TO"); PrText (-1, 20, 32, LRED, "RESCAN THE FILES ?"); PrText (-1, 22, 31, DRED, "PRESS TO CONFIRM"); if (!WaitForConfirmation ()) /* Acknowledged ? */ { /* Step out if not */ for (M = 16 ; M < 25 ; M ++) /* Erase requester */ PrText (-1, M, 26, DBLACK, " "); PrintWadFiles (); ShowMouse (); return; } PrText (-1, 18, 29, DRED, " "); PrText (-1, 20, 32, LRED, " Searching ... "); PrText (-1, 22, 31, DRED, " "); for (CountWadFiles = 0 ; CountWadFiles < TotalWads ; CountWadFiles ++) GetWadInfo (CountWadFiles, TRUE); /* Collect information on each WAD file */ PrText (-1, 20, 29, LRED, "Writing WADINFOFILE ... "); WriteWadInfo (InfoFile); /* Write the result */ for (CountWadFiles = 0 ; CountWadFiles < TotalWads ; CountWadFiles ++) ConvertFullName (WadInfo[CountWadFiles]); /* Convert full names to short names if wanted */ PrText (-1, 20, 29, LRED, " "); PrText (-1, 18, 34, DRED, "DO YOU WANT TO"); HandleFileSort (FALSE); /* Handle sorting of the result */ } void ToggleFullName (void) /**********************************************************************************************************************************/ /* Pre : The user pressed [F7]. */ /* Post : A requester has been drawn to confirm the action. If this was acknowledged, then the full name criterium has been */ /* toggled, the files have been resorted and reprinted. */ /* Import: Requester, PrText, HandleFileSort. */ /**********************************************************************************************************************************/ { int register CountWadFiles; Requester (15, 25, 11, 32); PrText (-1, 18, 29, DRED, "ARE YOU SURE YOU WANT TO"); PrText (-1, 20, 31, LRED, "CONVERT FULL NAMES ?"); PrText (-1, 22, 31, DRED, "PRESS TO CONFIRM"); if (!WaitForConfirmation ()) /* Acknowledged ? */ { /* Step out if not */ for (M = 16 ; M < 25 ; M ++) /* Erase requester */ PrText (-1, M, 26, DBLACK, " "); PrintWadFiles (); ShowMouse (); return; } PrText (-1, 18, 29, DRED, " "); PrText (-1, 20, 31, LRED, " Converting ... "); PrText (-1, 22, 31, DRED, " "); NoFullName = !NoFullName; /* Toggle NoFullName flag */ for (CountWadFiles = 0 ; CountWadFiles < TotalWads ; CountWadFiles ++) ConvertFullName (WadInfo[CountWadFiles]); PrText (-1, 20, 31, DRED, " "); PrText (-1, 18, 34, DRED, "DO YOU WANT TO"); HandleFileSort (FALSE); /* Handle sorting of the result */ } void DeleteWadFile (void) /**********************************************************************************************************************************/ /* Pre : None. */ /* Post : The user has been asked permission to delete the hi-lighted file. If he/she comfirmed, then the file has been deleted */ /* from disk and memory. If the file is protected on disk, then nothing is done. */ /* Import: Requester, PrintWadFiles, ShowMouse, UnselectPreviousWad, WaitForConfirmation. */ /**********************************************************************************************************************************/ { int register DeleteWadNumber; if (CurrentField != FILEFIELD) /* Return immediately if no file appointed */ return; DeleteWadNumber = CurrentPage * PAGE + CurrentSelected - 1; /* Determine the WAD number */ Requester (15, 25, 11, 32); PrText (-1, 18, 29, DRED, "ARE YOU SURE YOU WANT TO"); sprintf (S, "DELETE %s", WadInfo[DeleteWadNumber]->Name); for (M = strlen (S) - 1 ; S[M] == ' ' ; M --) S[M] = '\0'; strcat (S, " ?"); PrText (-1, 20, 40 - (strlen (S) / 2), LRED, S); PrText (-1, 22, 31, DRED, "PRESS TO CONFIRM"); if (!WaitForConfirmation ()) /* Acknowledged ? */ { /* Step out if not */ for (M = 16 ; M < 25 ; M ++) /* Erase requester */ PrText (-1, M, 26, DBLACK, " "); PrintWadFiles (); ShowMouse (); return; } for (M = 16 ; M < 25 ; M ++) /* Erase requester */ PrText (-1, M, 26, DBLACK, " "); if (WadInfo[DeleteWadNumber]->Drive) /* Build complete path to the file */ sprintf (S, "%c:", ToName (WadInfo[DeleteWadNumber]->Drive)); else sprintf (S, "%c:", ToName (CurDrive)); if (!strcmp (WadInfo[DeleteWadNumber]->Path, DEFAULTWADDIR)) strcat (S, CurPath + 2); else strcat (S, WadInfo[DeleteWadNumber]->Path); strcat (S, "\\"); strcat (S, WadInfo[DeleteWadNumber]->OrigName); if (_dos_setfileattr (S, _A_NORMAL)) /* Clear any preventing file attributes */ { /* Step out if protected from a higher level */ PrintWadFiles (); ShowMouse (); return; } if (remove (S) == -1) /* Now remove the file from disk */ { /* Step out if protected from a higher level */ PrintWadFiles (); ShowMouse (); return; } _ffree (WadInfo[DeleteWadNumber]); /* Remove the file from memory */ for (M = DeleteWadNumber ; M < TotalWads ; M ++) /* Move all files thereafter one back */ WadInfo[M] = WadInfo[M + 1]; TotalWads --; PrintWadFiles (); /* Print result ... */ ShowMouse (); } void _fastcall HandleFile (unsigned int Key) /**********************************************************************************************************************************/ /* Pre : 'Key' holds the preesed (raw) key code that caused calling this routine; if (global) 'UseMouse' was FALSE. */ /* Considered keys are the cursor keys and space. If 'UseMouse' was TRUE, than 'Key' is always 0x0000 (dummy). */ /* Post : The mouse pointer was at the bottom of the screen. The pointed filename has been highlighted. If the right mousebutton */ /* was pressed, then the file is selected, which is shown by a different color. If the file was already selected, then it */ /* is now deselected. Selection can only be done once on a button press. To invert the selection, the mouse button must */ /* first be released, and then pressed again. */ /* Import: HideMouse, ShowMouse, UnselectPreviousField. */ /**********************************************************************************************************************************/ { int register PositionX; int register PositionY; int register OldWadNumber; int register NewWadNumber; UnselectPreviousField (FILEFIELD); PositionY = 13 + ((PreviousWad - 1) % WADHEIGHT); /* Location of previously selected WAD */ PositionX = ((PreviousWad - 1) / WADHEIGHT) * WADWIDTH + 1; OldWadNumber = CurrentPage * PAGE + PreviousWad - 1; /* Number of that WAD */ CurrentSelected = 0; /* Signal: no file pointed to */ if (UseMouse) { if (Mouse.Xx <= 7) /* Determine the file column */ CurrentSelected = Mouse.Yy - 11; else if (Mouse.Xx >= 27 && Mouse.Xx <= 34) CurrentSelected = Mouse.Yy - 11 + WADHEIGHT; else if (Mouse.Xx >= 54 && Mouse.Xx <= 61) CurrentSelected = Mouse.Yy - 11 + (2 * WADHEIGHT); if (CurrentPage * PAGE + CurrentSelected > TotalWads) /* Empty screen part */ CurrentSelected = -1; MouseHidden = FALSE; if (CurrentField == FILEFIELD && Mouse.CoChange) /* Only unhighlite the previous one if the mouse moved */ { HideMouse (); MouseHidden = TRUE; /* Signal: mouse pointer hidden */ if (WadInfo[OldWadNumber]->Selected) PrText (-1, PositionY, PositionX, DGREEN, WadInfo[OldWadNumber]->Name); else PrText (-1, PositionY, PositionX, DWHITE, WadInfo[OldWadNumber]->Name); } } else { switch (Key) { case KEY_CURSLEFT : if (PreviousWad > WADHEIGHT) /* Possible to go left ? */ CurrentSelected = PreviousWad - WADHEIGHT; else /* Determine the far right and go there */ if ((CurrentPage * PAGE + PreviousWad + 2 * WADHEIGHT) <= TotalWads) CurrentSelected = PreviousWad + 2 * WADHEIGHT; /* 3 columns */ else if ((CurrentPage * PAGE + PreviousWad + WADHEIGHT) <= TotalWads) /* 2 columns */ CurrentSelected = PreviousWad + WADHEIGHT; else /* Only 1 column; no move possible */ CurrentSelected = PreviousWad; break; case KEY_CURSRIGHT : if ((PreviousWad < 2 * WADHEIGHT) && (CurrentPage * PAGE + PreviousWad + WADHEIGHT) <= TotalWads) CurrentSelected = PreviousWad + WADHEIGHT; else CurrentSelected = PreviousWad % WADHEIGHT; break; case KEY_CURSUP : if (((PreviousWad - 1) % WADHEIGHT) > 0) CurrentSelected = PreviousWad - 1; else if ((CurrentPage * PAGE + PreviousWad + WADHEIGHT) <= TotalWads) CurrentSelected = PreviousWad - 1 + WADHEIGHT; else CurrentSelected = TotalWads % PAGE; break; case KEY_CURSDOWN : if (((PreviousWad - 1) % WADHEIGHT) < (WADHEIGHT - 1)) if ((CurrentPage * PAGE + PreviousWad + 1) <= TotalWads) CurrentSelected = PreviousWad + 1; else CurrentSelected = (TotalWads % PAGE) - (TotalWads % WADHEIGHT) + 1; else CurrentSelected = PreviousWad + 1 - WADHEIGHT; break; case KEY_SELECTFILE : CurrentSelected = PreviousWad; } if (Key != KEY_SELECTFILE) /* Unhighlite the previous one */ if (WadInfo[OldWadNumber]->Selected) PrText (-1, PositionY, PositionX, DGREEN, WadInfo[OldWadNumber]->Name); else PrText (-1, PositionY, PositionX, DWHITE, WadInfo[OldWadNumber]->Name); } NewWadNumber = CurrentPage * PAGE + CurrentSelected - 1; if ((UseMouse && (Mouse.Left && !Mouse.LeftStillPressed && CurrentSelected > 0)) /* Mouse button pressed ? */ || (Key == KEY_SELECTFILE)) { WadInfo[NewWadNumber]->Selected = !WadInfo[NewWadNumber]->Selected; /* Invert selection */ SelectionChange = TRUE; /* Signal: screenchange */ } else SelectionChange = FALSE; if (CurrentSelected > 0) /* A (valid) new file is pointed to */ { if (Mouse.CoChange || SelectionChange || !UseMouse) /* Color change needed ? */ { PositionY = 13 + ((CurrentSelected - 1) % WADHEIGHT); PositionX = ((CurrentSelected - 1) / WADHEIGHT) * WADWIDTH + 1; if (!MouseHidden) /* Hide the mouse if not hidden already */ HideMouse (); MouseHidden = TRUE; if (WadInfo[NewWadNumber]->Selected) /* 'Draw' highlite bar */ PrText (-1, PositionY, PositionX, LGREEN, WadInfo[NewWadNumber]->Name); else PrText (-1, PositionY, PositionX, LRED, WadInfo[NewWadNumber]->Name); } CurrentField = FILEFIELD; PreviousWad = CurrentSelected; } else CurrentField = NOFIELD; if (MouseHidden) /* Reprint the mouse pointer if needed */ ShowMouse (); } void _fastcall HandleDifficulty (boolean KeyInput) /**********************************************************************************************************************************/ /* Pre : 'KeyInput' is TRUE if this routine was called because of a keypress. */ /* Post : The mouse pointer was at the difficulty block. The pointed difficulty has been highlited. If the mouse button was */ /* pressed then this difficulty is selected (and the previous automatically de-selected). The new result is reprinted. */ /* Import: HideMouse, ShowMouse, PrintDifficulties, UnselectPreviousField. */ /**********************************************************************************************************************************/ { int register MaxDiff; HideMouse (); if (!KeyInput) UnselectPreviousField (DIFFICULTYFIELD); if (Mouse.Left && !Mouse.LeftStillPressed) DifficultyActive = Mouse.Yy - 1; else if (KeyInput) if (++ DifficultyActive > NUMDIFFICULTY) DifficultyActive = 1; if (KeyInput) if (CurrentField == DIFFICULTYFIELD) PrintDifficulties (Mouse.Yy - 1); else PrintDifficulties (0); else { PrintDifficulties (Mouse.Yy - 1); CurrentField = DIFFICULTYFIELD; } ShowMouse (); } void _fastcall HandlePlayType (boolean KeyInput, char Key) /**********************************************************************************************************************************/ /* Pre : 'KeyInput' is TRUE if this routine was called because of a keypress. */ /* If it was TRUE, then 'Key' holds the (ASCII) keyvalue, otherwise 'Key' holds 0x00 (dummy). */ /* Post : The mouse pointer was at the playtype block. The pointed playtype has been highlited. If the mouse button was pressed */ /* then this playtype is selected (and the previous automatically de-selected). The new result is reprinted. */ /* If the mouse pointer was at the fourth line, than the PlayType parameter of the current PlayType is handled. */ /* Import: HideMouse, ShowMouse, PrintPlayTypes, UnselectPreviousField. */ /**********************************************************************************************************************************/ { HideMouse (); if (!KeyInput) UnselectPreviousField (PLAYTYPEFIELD); if (KeyInput) { switch (Key) { case KEY_PLAYTYPE : if (++ PlayTypeActive > NUMPLAYTYPE) PlayTypeActive = 1; break; case KEY_NODES : if (PlayTypeActive == 2 && !OtherIpxDriver) /* IPX compatible */ if (++ NumNodesActive == 5) /* Increase number of players */ NumNodesActive = 2; /* Must be between 2 and 4 */ break; case KEY_COMPORT : if (PlayTypeActive == 3 && !OtherSerDriver) /* Null-modem link */ if (++ CommPortActive == 5) /* Increase COM port number */ CommPortActive = 1; /* Must be between 1 and 4 */ } if (CurrentField == PLAYTYPEFIELD) PrintPlayTypes (Mouse.Yy - 1); else PrintPlayTypes (0); } else /* Mouse input */ { if (Mouse.Left && !Mouse.LeftStillPressed) if (Mouse.Yy <= NUMPLAYTYPE + 1) /* Change PlayType ? */ PlayTypeActive = Mouse.Yy - 1; else if (PlayTypeActive == 2 && !OtherIpxDriver) /* IPX compatible */ { if (++ NumNodesActive == 5) /* Increase number of players */ NumNodesActive = 2; /* Must be between 2 and 4 */ } else if (PlayTypeActive == 3 && !OtherSerDriver) /* Null-modem link */ { if (++ CommPortActive == 5) /* Increase COM port number */ CommPortActive = 1; /* Must be between 1 and 4 */ } PrintPlayTypes (Mouse.Yy - 1); CurrentField = PLAYTYPEFIELD; } ShowMouse (); } void _fastcall HandleRespawnMonsters (boolean KeyInput) /**********************************************************************************************************************************/ /* Pre : 'KeyInput' is TRUE if this routine was called because of a keypress. */ /* Post : The mouse pointer was at the respawn item. This item has been highlited. If the mouse button was pressed, then the */ /* active value is inverted. The new result is reprinted. */ /* Import: HideMouse, ShowMouse, PrintRespawnMonsters, PrintNoMonsters, UnselectPreviousField. */ /**********************************************************************************************************************************/ { HideMouse (); if (!KeyInput) UnselectPreviousField (RESPAWNFIELD); if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput) if (RespMonstersOn = !RespMonstersOn) /* Toggle the RESPAWN item */ NoMonstersOn = FALSE; if (KeyInput) { PrintRespawnMonsters ((boolean)(CurrentField == RESPAWNFIELD)); PrintNoMonsters ((boolean)(CurrentField == NOMONSTERSFIELD)); } else { PrintRespawnMonsters (TRUE); PrintNoMonsters (FALSE); CurrentField = RESPAWNFIELD; } ShowMouse (); } void _fastcall HandleNoMonsters (boolean KeyInput) /**********************************************************************************************************************************/ /* Pre : 'KeyInput' is TRUE if this routine was called because of a keypress. */ /* Post : The mouse pointer was at the nomonsters item. This item has been highlited. If the mouse button was pressed, then the */ /* active value is inverted. The new result is reprinted. */ /* Import: HideMouse, ShowMouse, PrintNoMonsters, PrintRespawnMonsters, PrintFastMonsters, UnselectPreviousField. */ /**********************************************************************************************************************************/ { HideMouse (); if (!KeyInput) UnselectPreviousField (NOMONSTERSFIELD); if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput) if (NoMonstersOn = !NoMonstersOn) /* Toggle the NOMONSTERS item */ { RespMonstersOn = FALSE; /* These are contradictionary */ FastMonstersOn = FALSE; } if (KeyInput) { PrintNoMonsters ((boolean)(CurrentField == NOMONSTERSFIELD)); PrintRespawnMonsters ((boolean)(CurrentField == RESPAWNFIELD)); PrintFastMonsters ((boolean)(CurrentField == FASTMONSTERSFIELD)); } else { PrintNoMonsters (TRUE); PrintRespawnMonsters (FALSE); PrintFastMonsters (FALSE); CurrentField = NOMONSTERSFIELD; } ShowMouse (); } void _fastcall HandleFastMonsters (boolean KeyInput) /**********************************************************************************************************************************/ /* Pre : 'KeyInput' is TRUE if this routine was called because of a keypress. */ /* Post : The mouse pointer was at the fastmonsters item. This item has been highlited. If the mouse button was pressed, then */ /* the active value is inverted. The new result is reprinted. */ /* Import: HideMouse, ShowMouse, PrintFastMonsters, PrintNoMonsters, UnselectPreviousField. */ /**********************************************************************************************************************************/ { HideMouse (); if (!KeyInput) UnselectPreviousField (FASTMONSTERSFIELD); if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput) if (FastMonstersOn = !FastMonstersOn) /* Toggle the FASTMONSTERS item */ NoMonstersOn = FALSE; /* These are contradictionary */ if (KeyInput) { PrintFastMonsters ((boolean)(CurrentField == FASTMONSTERSFIELD)); PrintNoMonsters ((boolean)(CurrentField == NOMONSTERSFIELD)); } else { PrintFastMonsters (TRUE); PrintNoMonsters (FALSE); CurrentField = FASTMONSTERSFIELD; } ShowMouse (); } void _fastcall HandleDeathmatch (boolean KeyInput) /**********************************************************************************************************************************/ /* Pre : 'KeyInput' is TRUE if this routine was called because of a keypress. */ /* Post : The mouse pointer was at the deathmatch item. This item has been highlited. If the mouse button was pressed, then the */ /* active value is inverted. The new result is reprinted. */ /* Import: HideMouse, ShowMouse, PrintDeathmatch, UnselectPreviousField, PrintV2Deathmatch. */ /**********************************************************************************************************************************/ { HideMouse (); if (!KeyInput) UnselectPreviousField (DEATHMATCHFIELD); if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput) if (DeathmatchOn = !DeathmatchOn) /* Toggle the DEATHMATCH item */ DeathmatchV2On = FALSE; if (KeyInput) { PrintDeathmatch ((boolean)(CurrentField == DEATHMATCHFIELD)); PrintV2Deathmatch ((boolean)(CurrentField == DEATHMATCHV2FIELD)); } else { PrintDeathmatch (TRUE); PrintV2Deathmatch (FALSE); CurrentField = DEATHMATCHFIELD; } ShowMouse (); } void _fastcall HandleV2Deathmatch (boolean KeyInput) /**********************************************************************************************************************************/ /* Pre : 'KeyInput' is TRUE if this routine was called because of a keypress. */ /* Post : The mouse pointer was at the deathmatch item. This item has been highlited. If the mouse button was pressed, then the */ /* active value is inverted. The new result is reprinted. */ /* Import: HideMouse, ShowMouse, PrintDeathmatch, UnselectPreviousField, PrintV2Deathmatch. */ /**********************************************************************************************************************************/ { HideMouse (); if (!KeyInput) UnselectPreviousField (DEATHMATCHV2FIELD); if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput) if (DeathmatchV2On = !DeathmatchV2On) DeathmatchOn = FALSE; if (KeyInput) { PrintV2Deathmatch ((boolean)(CurrentField == DEATHMATCHV2FIELD)); PrintDeathmatch ((boolean)(CurrentField == DEATHMATCHFIELD)); } else { PrintV2Deathmatch (TRUE); PrintDeathmatch (FALSE); CurrentField = DEATHMATCHV2FIELD; } ShowMouse (); } void _fastcall HandleNextLevel (boolean KeyInput) /**********************************************************************************************************************************/ /* Pre : 'KeyInput' is TRUE if this routine was called because of a keypress. */ /* Post : The mouse pointer was at the nextlevel item. This item has been highlited. If the mouse button was pressed, then a */ /* test is made if there is a next level. If there is one, then the active level is increased. */ /* Import: HideMouse, ShowMouse, PrintLevel, PrintLevelPagers, UnselectPreviousField. */ /**********************************************************************************************************************************/ { if (CurrentLevel == NUMLEVEL) return; HideMouse (); if (!KeyInput) UnselectPreviousField (LEVELFIELD); if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput) { CurrentLevel ++; /* Increase current level */ PrintLevel (); } if (!KeyInput) { PrintLevelPagers (2); CurrentField = LEVELFIELD; } else PrintLevelPagers (CurrentField == LEVELFIELD ? 2 : 0); ShowMouse (); } void _fastcall HandlePreviousLevel (boolean KeyInput) /**********************************************************************************************************************************/ /* Pre : 'KeyInput' is TRUE if this routine was called because of a keypress. */ /* Post : The mouse pointer was at the previouslevel item. This item has been highlited. If the mouse button was pressed, then a */ /* test is made if there is a previous level. If there is one, then the active level is decreased. */ /* Import: HideMouse, ShowMouse, PrintLevel, PrintLevelPagers, UnselectPreviousField. */ /**********************************************************************************************************************************/ { if (CurrentLevel == 1) return; HideMouse (); if (!KeyInput) UnselectPreviousField (LEVELFIELD); if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput) { CurrentLevel --; /* Decrease current level */ PrintLevel (); } if (!KeyInput) { PrintLevelPagers (1); CurrentField = LEVELFIELD; } else PrintLevelPagers (CurrentField == LEVELFIELD ? 1 : 0); ShowMouse (); } void _fastcall HandlePreviousPage (boolean KeyInput) /**********************************************************************************************************************************/ /* Pre : 'KeyInput' is TRUE if this routine is entered because the [PAGE UP] key was pressed. */ /* Post : The mouse pointer was at the previouspage item. This item has been highlited. If the mouse button was pressed, then */ /* a test is made if there are previous pages. If not, then the keyboard bell is sound, otherwise the previous page has */ /* been made the current. This new page has been printed. */ /* Import: HideMouse, ShowMouse, PrintWadFiles, PrintPagers, UnselectPreviousField. */ /**********************************************************************************************************************************/ { if (CurrentPage == 0) /* Step out if there is no previous page */ return; HideMouse (); if (!KeyInput) UnselectPreviousField (PAGERFIELD); if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput) { CurrentPage --; /* Go back a page */ PrText (-1, 11, 69, LWHITE, "%2d", CurrentPage + 1); PrintWadFiles (); /* Print this new page */ if (!UseMouse) PrText (-1, 13 + ((PreviousWad - 1) % WADHEIGHT), ((PreviousWad - 1) / WADHEIGHT) * WADWIDTH + 1, LRED, WadInfo[CurrentPage * PAGE + PreviousWad - 1]->Name); /* Hi-light 'new current' WAD file */ } if (!KeyInput) { PrintPagers (1); CurrentField = PAGERFIELD; } else PrintPagers (CurrentField == PAGERFIELD ? 1 : 0); ShowMouse (); } void _fastcall HandleNextPage (boolean KeyInput) /**********************************************************************************************************************************/ /* Pre : 'KeyInput' is TRUE if this routine was called because the user pressed the [PAGE DOWN] key. */ /* Post : The mouse pointer was at the nextpage item. This item has been highlited. If the mouse button was pressed, then a test */ /* is made if there are next pages. If not, then the keyboard bell is sound, otherwise the next page has been made the */ /* current. This new page has been printed. */ /* Import: HideMouse, ShowMouse, PrintWadFiles, PrintPagers, UnselectPreviousField. */ /**********************************************************************************************************************************/ { if (CurrentPage == LastPage) /* Step out if there is no next page */ return; HideMouse (); if (!KeyInput) UnselectPreviousField (PAGERFIELD); if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput) { CurrentPage ++; PrText (-1, 11, 69, LWHITE, "%2d", CurrentPage + 1); PrintWadFiles (); if (!UseMouse) { if ((CurrentPage * PAGE + PreviousWad - 1) >= TotalWads) /* If not a full page, then test that the new pointed */ PreviousWad = TotalWads % PAGE; /* WAD file is valid, otherwise set it to the last one */ PrText (-1, 13 + ((PreviousWad - 1) % WADHEIGHT), ((PreviousWad - 1) / WADHEIGHT) * WADWIDTH + 1, LRED, WadInfo[CurrentPage * PAGE + PreviousWad - 1]->Name); /* Hi-light 'new current' WAD file */ } } if (!KeyInput) { PrintPagers (2); CurrentField = PAGERFIELD; } else PrintPagers (CurrentField == PAGERFIELD ? 2 : 0); ShowMouse (); } void _fastcall HandleReadPrev (boolean KeyInput) /**********************************************************************************************************************************/ /* Pre : 'KeyInput' is TRUE if this routine was called because of a keypress. */ /* Post : The mouse pointer was at the read previous item. This item has been highlited. If the mouse button was pressed, then */ /* the file 'START.BAT' is read. Each item is set to the read value, all read WAD files are selected. */ /* Import: HideMouse, ShowMouse, PrintRdPrev, PrintDifficulties, PrintPlayTypes, PrintDeathmatch, PrintLevel, PrintLevelPagers, */ /* PrintWadFiles, UnselectPreviousField. */ /**********************************************************************************************************************************/ { FILE *Fp; char FileName[MAXFNAME]; char Directory[_MAX_DIR]; char Tmp[_MAX_DIR]; char DriveNum; boolean Handled; boolean Stop; HideMouse (); if (!KeyInput) UnselectPreviousField (RDPREVFIELD); if (!KeyInput) { PrintRdPrev (TRUE); CurrentField = RDPREVFIELD; } if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput) { Stop = FALSE; if (!(Fp = fopen (BATFILE, "r"))) /* Open "START.BAT" */ Stop = TRUE; else { DifficultyActive = DEFAULTDIFFICULTY; PlayTypeActive = DEFAULTPLAYTYPE; CurrentLevel = DEFAULTLEVEL; DeathmatchOn = DEFAULTDMATCH; DeathmatchV2On = DEFAULTDMATCHV2; RespMonstersOn = DEFAULTRESPAWN; NoMonstersOn = DEFAULTNOMONSTERS; FastMonstersOn = DEFAULTFASTMONST; CommPortActive = DEFAULTCOMPORT; NumNodesActive = DEFAULTNODES; for (M = 0 ; M < TotalWads ; M ++) /* Unselect all WAD files */ WadInfo[M]->Selected = FALSE; fscanf (Fp, "%s", S); /* First read the command */ while (!feof (Fp) && stricmp (S, STARTALONE) && stricmp (S, STARTIPX) && stricmp (S, IpxDriver) && stricmp (S, STARTLINK) && stricmp (S, SerDriver)) fscanf (Fp, "%s", S); if (!stricmp (S, STARTALONE)) PlayTypeActive = 1; else if (!stricmp (S, STARTIPX) || !stricmp (S, IpxDriver)) PlayTypeActive = 2; else if (!stricmp (S, STARTLINK) || !stricmp (S, SerDriver)) PlayTypeActive = 3; else Stop = TRUE; /* Not a DOOM start command */ if (!Stop) { fscanf (Fp, "%s", S); while (!feof (Fp) && !Stop) /* Handle each parameter */ { Handled = FALSE; if (S[0] == '@') /* 'Response' file following */ { fclose (Fp); /* Close "START.BAT" */ if (!(Fp = fopen (S + 1, "r"))) /* Open the 'Response' file */ Stop = TRUE; /* Stop if the file does not exist */ else fscanf (Fp, "%s", S); /* Else: read the first string */ } if (!feof (Fp) && !stricmp (S, NWSOCKET) && !Stop) /* Found '-SOCKET' */ { fscanf (Fp, "%s", S); /* '-SOCKET' takes a parameter: 0-255 */ if (feof (Fp)) Stop = TRUE; for (N = 0 ; N < strlen (S) && !Stop ; N ++) Stop = !isdigit (S[N]); if (!Stop) { NetworkSocket = atoi (S); Handled = TRUE; fscanf (Fp, "%s", S); } } if (!feof (Fp) && !stricmp (S, SKILL) && !Stop) /* Found '-SKILL' */ { fscanf (Fp, "%s", S); /* '-SKILL' takes a parameter: 1-5 */ if (feof (Fp) || strlen (S) != 1 || S[0] < '1' || S[0] > '5') Stop = TRUE; else { DifficultyActive = S[0] - '0'; Handled = TRUE; fscanf (Fp, "%s", S); } } if (!feof (Fp) && !stricmp (S, DMATCH) && !Stop) /* Found '-DEATHMATCH' */ { DeathmatchOn = TRUE; Handled = TRUE; fscanf (Fp, "%s", S); } if (!feof (Fp) && !stricmp (S, DMATCHV2) && !Stop) /* Found '-ALTDEATH' */ { DeathmatchV2On = TRUE; Handled = TRUE; fscanf (Fp, "%s", S); } if (!feof (Fp) && !stricmp (S, NOMONSTERS) && !Stop) /* Found '-NOMONSTERS' */ { NoMonstersOn = TRUE; Handled = TRUE; fscanf (Fp, "%s", S); } if (!feof (Fp) && !stricmp (S, RESPAWNMONSTERS) && !Stop) /* Found '-RESPAWN' */ { RespMonstersOn = TRUE; Handled = TRUE; fscanf (Fp, "%s", S); } if (!feof (Fp) && !stricmp (S, FASTMONSTERS) && !Stop) /* Found '-FAST' */ { FastMonstersOn = TRUE; Handled = TRUE; fscanf (Fp, "%s", S); } if (!feof (Fp) && !stricmp (S, GOTOANYTHING) && !Stop) /* Found '-WARP' */ { fscanf (Fp, "%s", S); /* '-WARP' takes a parameter: 01-32 */ if (feof (Fp)) Stop = TRUE; for (N = 0 ; N < strlen (S) && !Stop ; N ++) Stop = !isdigit (S[N]); if (!Stop) { CurrentLevel = atoi (S); Handled = TRUE; fscanf (Fp, "%s", S); } } if (!feof (Fp) && !strnicmp (S, COMPORT, 4) && !Stop) /* Found '-COM#' */ { if (strlen (S) != 5 || S[4] < '1' || S[0] > '4') /* COM port number is last char in this string */ Stop = TRUE; /* Port number must be between 1-4 */ else { CommPortActive = S[4] - '0'; Handled = TRUE; fscanf (Fp, "%s", S); } } if (!feof (Fp) && !stricmp (S, NUMPLAYERS) && !Stop) /* Found '-NODES' */ { fscanf (Fp, "%s", S); /* '-NODES' takes a parameter: 2-4 */ if (feof (Fp) || strlen (S) != 1 || S[0] < '2' || S[0] > '4') Stop = TRUE; else { NumNodesActive = S[0] - '0'; Handled = TRUE; fscanf (Fp, "%s", S); } } if (!feof (Fp) && !stricmp (S, INCFILE) && !Stop) /* Found '-FILE' */ { fscanf (Fp, "%s", S); /* Read-ahead first filename (at least 1 needed) */ do /* Each following word is a filename, until the */ { /* next keyword is found or EOF is encountered */ if (!feof (Fp) && !Stop) { DriveNum = DoomDrive; /* Assume: not preceded by a drive: */ if (S[1] == ':') /* Preceded by drive: */ if (toupper (S[0]) < 'A' || toupper (S[0]) > 'Z') Stop = TRUE; else { DriveNum = ToNumber (S[0]); for (M = 2 ; M <= strlen (S) ; M ++) /* Cut drive */ S[M - 2] = S[M]; } if (!Stop) if (stricmp (S + strlen (S) - 4, ".WAD")) /* Filename MUST end with '.WAD' */ Stop = TRUE; if (!Stop) { S[strlen (S) - 4] = '\0'; /* Cut the '.WAD' from the filename */ M = strlen (S); if (!M || S[M - 1] == '\\') /* Ended with a '\' or no filename at all */ Stop = TRUE; else { while (S[-- M] != '\\' && M >= 0) ; if (M >= 0) /* Preceded by path */ { if (M == 0) strcpy (Directory, "\\"); /* Handle root differently */ else { strncpy (Directory, strupr (S), M); Directory[M] = '\0'; if (DriveNum == DoomDrive && Directory[0] != '\\') { sprintf (Tmp, "%s\\%s", DoomDirectory, Directory); strcpy (Directory, Tmp); } } strcpy (FileName, strupr (S) + M + 1); } else /* No path */ { strcpy (FileName, strupr (S)); if (DriveNum == DoomDrive) strcpy (Directory, DoomDirectory); else strcpy (Directory, DEFAULTWADDIR); } if (strlen (FileName) > 8) /* Filename contains more than 8 characters */ Stop = TRUE; else { for (M = strlen (FileName) ; M < 8 ; M ++) /* Fill out filename to 8 characters */ FileName[M] = ' '; FileName[M] = '\0'; Handled = FALSE; for (M = 0 ; M < TotalWads && !Handled; M ++) /* Match against all WAD filenames in memory */ if (WadInfo[M]->Drive == DriveNum) if (!strcmp (WadInfo[M]->Path, Directory)) if (!strcmp (WadInfo[M]->Name, FileName)) { Handled = TRUE; WadInfo[M]->Selected = TRUE; /* If it matches, than auto-select the file */ } fscanf (Fp, "%s", S); /* Read next */ } } } } } while (!feof (Fp) && S[0] != '-' && !Stop); Handled = TRUE; } if (!Handled && !feof (Fp) && !Stop) fscanf (Fp, "%s", S); /* Other switch: skip */ } } fclose (Fp); if (DeathmatchOn && DeathmatchV2On) /* -DEATHMATCH -ALTDEATH means deathmatch v2.0 */ DeathmatchOn = FALSE; if (NoMonstersOn) /* Handle contradictionaries */ { RespMonstersOn = FALSE; FastMonstersOn = FALSE; } PrintLevel (); PrintLevelPagers (0); PrintDifficulties (0); PrintPlayTypes (0); PrintDeathmatch (FALSE); PrintV2Deathmatch (FALSE); PrintNoMonsters (FALSE); PrintRespawnMonsters (FALSE); PrintFastMonsters (FALSE); PrintWadFiles (); } } ShowMouse (); } void HandleAutomatic (boolean KeyInput) /**********************************************************************************************************************************/ /* Pre : 'KeyInput' is TRUE if this routine was called because of a keypress. */ /* Post : The mouse pointer was at the automatic item. This item has been highlited. If the mouse button was pressed, then this */ /* routine reads all selected WAD files and initializes the current level to the lowest ones found. */ /* Import: HideMouse, ShowMouse, PrintAutomatic, PrintLevel, PrintLevelPagers, UnselectPreviousField. */ /**********************************************************************************************************************************/ { unsigned long register Startpoint1 = 0x00000000; unsigned long register Startpoint2 = 0x00000000; unsigned long register Startpoint3 = 0x00000000; unsigned short register Startpoint4 = 0x00; unsigned long register LevelMask; boolean Found = FALSE; HideMouse (); if (!KeyInput) UnselectPreviousField (AUTOMATICFIELD); if (!KeyInput) { PrintAutomatic (TRUE); CurrentField = AUTOMATICFIELD; } ShowMouse (); if ((Mouse.Left && !Mouse.LeftStillPressed) || KeyInput) { for (M = 0 ; M < TotalWads ; M ++) if (WadInfo[M]->Selected) /* Now merge all level information of all selcted WAD files */ { GetWadInfo (M, TRUE); Startpoint1 |= WadInfo[M]->NewLevels1; Startpoint2 |= WadInfo[M]->NewLevels2; Startpoint3 |= WadInfo[M]->NewLevels3; Startpoint4 |= WadInfo[M]->NewLevels4; } if (Startpoint1 != 0x00000000 || Startpoint2 != 0x00000000 || Startpoint3 != 0x00000000 || Startpoint4 != 0x00) /* Selected files contain new levels ? */ { CurrentLevel = 1; LevelMask = 0x00000001; for (M = 0 ; M < 32 && !Found ; M ++) if ((Startpoint1 & LevelMask) == LevelMask) /* Search for the lowest level */ Found = TRUE; else { LevelMask *= 2; /* Shift left */ CurrentLevel ++; } LevelMask = 0x00000001; for (M = 0 ; M < 32 && !Found ; M ++) if ((Startpoint2 & LevelMask) == LevelMask) Found = TRUE; else { LevelMask *= 2; CurrentLevel ++; } LevelMask = 0x00000001; for (M = 0 ; M < 32 && !Found ; M ++) if ((Startpoint3 & LevelMask) == LevelMask) Found = TRUE; else { LevelMask *= 2; CurrentLevel ++; } LevelMask = 0x00000001; for (M = 0 ; M < 3 && !Found ; M ++) if ((Startpoint4 & LevelMask) == LevelMask) Found = TRUE; else { LevelMask *= 2; CurrentLevel ++; } } else /* No levels in the files */ CurrentLevel = 1; PrintLevel (); PrintLevelPagers (0); } } boolean _fastcall HandleStartGame (boolean KeyInput) /**********************************************************************************************************************************/ /* Pre : 'KeyInput' is TRUE if this routine was called because of a keypress. */ /* Post : The mouse pointer was at the startgame item. This item has been highlited. If the mouse button was pressed, then this */ /* routine returns FALSE (which effectively means: start the game), otherwise TRUE. */ /* Import: HideMouse, ShowMouse, PrintStart, UnselectPreviousField. */ /**********************************************************************************************************************************/ { HideMouse (); UnselectPreviousField (STARTFIELD); PrintStart (TRUE); CurrentField = STARTFIELD; ShowMouse (); return ((!Mouse.Left || Mouse.LeftStillPressed) && !KeyInput); } int main (int argc, char **argv) /**********************************************************************************************************************************/ /* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> MAIN ROUTINE <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */ /* Import: ResetMouse, ReadConfig, PrintDifficulties, PrintPlayTypes, PrintDeathmatch, PrintLevel, PrintLevelPagers, */ /* PrintWadFiles, PrintPagers, ShowMouse, MouseStatus, HandleDifficulty, HandleDeathmatch, HandleLevel, HandleNextPage, */ /* HandlePreviousPage, HideMouse, DeAllocateAll, PrintStart, PrintRdPrev, HandleStartGame, HandleReadPrev, GetWadInfo, */ /* WriteWadInfo, PrintAutomatic, HandleAutomatic, HandlePlayType, UnselectPreviousField, InitVideo, PrintV2Deathmatch, */ /* PrintRespawnMonsters, PrintNoMonsters, PrintFastMonsters, HandleV2Deathmatch, HandleRespawnMonsters, HandleNoMonsters, */ /* HandleFastMonsters, SortFiles, RescanFiles, ToggleFileSort, Bye, PrText. */ /**********************************************************************************************************************************/ { FILE *Fp; struct find_t FileBlock; struct WadInfo_s ReadInfo; /* Entry as read from the WADINFOFILE */ char DrivePath[_MAX_DIR]; /* Current directory of a drive */ char NextArgument; /* Argument number on the command line */ char Ch; char SepChar; /* Seperation char in START.*; '\n' or ' ' */ boolean SepCharNeeded; boolean More; boolean FirstWad; /* First WAD file to include in START.BAT ? */ boolean AutoFound; /* Files given as AUTOINCLUDE paremeters ? */ boolean DriveChanged; /* Handling other drive than the 'home' drive ? */ boolean CLineError = FALSE; /* Nonsense on the command line ? */ boolean WarpDone = FALSE; /* TRUE if WARP has been included in START.BAT */ unsigned int Key; free (getcwd (CurPath, _MAX_DIR - 1)); /* Collect current directory */ CurDrive = ToNumber (CurPath[0]); /* Derive current drive */ UseMouse = ResetMouse (); /* Force mouse to re-initialize */ strcpy (ConfigFile, DEFAULTCONFIGFILE); NextArgument = 0; /* Handle command line arguments */ if (argc > 3) /* (Max number of arguments is 2) */ CLineError = TRUE; while (++ NextArgument < argc) if (!stricmp (argv[NextArgument], RESCAN1) || !stricmp (argv[NextArgument], RESCAN2)) /* Handle any -r parameter */ Rescan = TRUE; else if (argv[NextArgument][0] == OTHERCONFIGFILE) /* Handle any +file parameter */ { strcpy (ConfigFile, argv[NextArgument] + 1); ConfigChange = TRUE; } else CLineError = TRUE; if (CLineError) Bye (RETURNERROR, "Usage: EASYWAD [%s] [%cconfig]\n", RESCAN1, OTHERCONFIGFILE); ReadConfig (); /* Find CONFIGFILE and handle it */ if (!(Fp = fopen (InfoFile, "r"))) /* Test if the InfoFile exists */ Rescan = TRUE; /* No, it should be created */ else fclose (Fp); TotalWads = 0; if (Rescan) DoNotSearch = FALSE; /* Override if there is no infofile or if '-R' was given */ _dos_setdrive (CurDrive, &Dummy); /* Start from 'home' location (for partial paths) */ chdir (CurPath); if (!DoNotSearch) /* Search all WADDIR directories ? */ { printf ("Searching for WAD files ... "); for (N = 0 ; N < TotalWadDirs ; N ++) /* Handle all given directories */ { DriveChanged = FALSE; if (WadDir[N]->Drive) /* Change drive if one was given */ { _dos_setdrive (WadDir[N]->Drive, &Dummy); _dos_getdrive (&Dummy); if (Dummy != WadDir[N]->Drive) /* Check that the change has been made and report any error */ Bye (RETURNERROR, "ERROR - Drive of WADDIR does not exist\n"); DriveChanged = TRUE; } free (getcwd (DrivePath, _MAX_DIR - 1)); /* Collect current directory of this drive */ if (strcmp (WadDir[N]->Name, DEFAULTWADDIR)) /* Change directory if the entry was not "." */ if (chdir (WadDir[N]->Name)) /* Report the error if the directory does not exist */ Bye (RETURNERROR, "ERROR - Directory of WADDIR does not exist\n"); More = !_dos_findfirst (WADFILE, ANYATTR, &FileBlock); /* Look-ahead for a WAD file */ while (TotalWads < MAXWADS && More) /* Handle all WAD files in this directory */ { if ((strcmp (WadDir[N]->Name, DEFAULTWADDIR) || strcmp (FileBlock.name, MAINWAD)) && FileBlock.size >= sizeof (struct WadHeader_s)) /* Exclude the main DOOM WAD and too small files */ { if ((WadInfo[TotalWads] = ((struct WadInfo_s far *)_fmalloc ((size_t)sizeof (struct WadInfo_s)))) == NOMEM) Bye (RETURNERROR, "FATAL ERROR - Out of memory\n"); strcpy (WadInfo[TotalWads]->OrigName, FileBlock.name); /* Copy filename */ strncpy (WadInfo[TotalWads]->Name, FileBlock.name, strlen (FileBlock.name) - 4); /* Copy name, without extension */ for (M = strlen (FileBlock.name) - 4 ; M < 8 ; M ++) /* Fill out to 8 characters */ WadInfo[TotalWads]->Name[M] = ' '; WadInfo[TotalWads]->Name[M] = '\0'; /* Make it a valid string */ WadInfo[TotalWads]->Drive = WadDir[N]->Drive; strcpy (WadInfo[TotalWads]->Path, WadDir[N]->Name); for (M = 0 ; M < MAXINFOLEN ; M ++) WadInfo[TotalWads]->Info[M] = ' '; /* Initialize the other fields */ WadInfo[TotalWads]->Info[M] = '\0'; WadInfo[TotalWads]->Selected = FALSE; if (Rescan) GetWadInfo (TotalWads, FALSE); /* Read out the WAD directory */ TotalWads ++; } More = !_dos_findnext (&FileBlock); } if (WadDir[N]->DoSubDirs) { More = !_dos_findfirst ("*.*", SUBATTR, &FileBlock); /* Look for subdirectories */ while (More) { while ((!(FileBlock.attrib & _A_SUBDIR) || (FileBlock.name[0] == '.')) && More) /* Not '.' or '..' */ More = !_dos_findnext (&FileBlock); if (More) { if (TotalWadDirs == MAXWADDIRS) Bye (RETURNERROR, "ERROR - Too many WADDIR entries, max is %d\n", MAXWADDIRS); if ((WadDir[TotalWadDirs] = ((struct WadDir_s far *)_fmalloc ((size_t)sizeof (struct WadDir_s)))) == NOMEM) Bye (RETURNERROR, "FATAL ERROR - Out of memory\n"); WadDir[TotalWadDirs]->Drive = WadDir[N]->Drive; /* Add this directory to the WADDIR list */ sprintf (WadDir[TotalWadDirs]->Name, "%s\\%s", WadDir[N]->Name, FileBlock.name); WadDir[TotalWadDirs ++]->DoSubDirs = TRUE; /* Signal: recurse */ More = !_dos_findnext (&FileBlock); } } } chdir (DrivePath); /* Return to the current directory of this drive */ if (DriveChanged) _dos_setdrive (CurDrive, &Dummy); /* Return to home drive */ } } _dos_setdrive (CurDrive, &Dummy); /* Return to home location */ chdir (CurPath); if (Rescan) { printf ("Writing WADINFOFILE ... "); WriteWadInfo (InfoFile); } if (!(Fp = fopen (InfoFile, "r"))) /* Now handle the InfoFile, which should be found */ Bye (RETURNERROR, "ERROR - Drive read error\n"); fscanf (Fp, "%s", S); /* Read-ahead: first part is the drive number */ while (!feof (Fp)) { ReadInfo.Drive = atoi (S); /* Convert to number */ fscanf (Fp, "%s", ReadInfo.Path); /* Second part is the directory */ fscanf (Fp, "%s ", ReadInfo.OrigName); /* Third part is the filename */ More = TRUE; for (M = 0 ; M < MAXINFOLEN && More ; M ++) /* Last part is the info. It may contain spaces */ { fscanf (Fp, "%c", &ReadInfo.Info[M]); if (ReadInfo.Info[M] == '\n') /* String shorter than maximum ? */ More = FALSE; } while (More) /* End of line not yet found */ { fscanf (Fp, "%c", &Ch); if (Ch == '\n') /* Read rest of the line out */ More = FALSE; } ReadInfo.Info[M] = '\0'; ConvertFullName (&ReadInfo); /* Convert full to short name if wanted */ if (DoNotSearch) { if ((WadInfo[TotalWads] = ((struct WadInfo_s far *)_fmalloc ((size_t)sizeof (struct WadInfo_s)))) == NOMEM) Bye (RETURNERROR, "FATAL ERROR - Out of memory\n"); strcpy (WadInfo[TotalWads]->OrigName, ReadInfo.OrigName); /* Copy filename */ strncpy (WadInfo[TotalWads]->Name, ReadInfo.OrigName, strlen (ReadInfo.OrigName) - 4); /* Copy name, without extension */ for (M = strlen (ReadInfo.OrigName) - 4 ; M < 8 ; M ++) /* Fill out to 8 characters */ WadInfo[TotalWads]->Name[M] = ' '; WadInfo[TotalWads]->Name[M] = '\0'; WadInfo[TotalWads]->Drive = ReadInfo.Drive; strcpy (WadInfo[TotalWads]->Path, ReadInfo.Path); strcpy (WadInfo[TotalWads]->Info, ReadInfo.Info); WadInfo[TotalWads]->Selected = FALSE; TotalWads ++; } else { More = TRUE; for (M = 0 ; M < TotalWads && More; M ++) /* Match against all read WAD file names */ if (WadInfo[M]->Drive == ReadInfo.Drive) if (!stricmp (WadInfo[M]->Path, ReadInfo.Path)) if (!stricmp (WadInfo[M]->OrigName, ReadInfo.OrigName)) { More = FALSE; strcpy (WadInfo[M]->Info, ReadInfo.Info); /* Copy info field to correct WAD file */ } } fscanf (Fp, "%s", S); } fclose (Fp); if (TotalWads == 0) /* Abort the program if no WAD files were found (other than the main DOOM WAD) */ { remove (InfoFile); Bye (RETURNERROR, "No WAD files found!\n"); } SortFiles (); /* Sort the WAD files if that was requested */ N = -1; while (++ N < TotalAutoInc) /* Handle all AUTOINCLUDE entries */ { AutoFound = FALSE; for (M = 0 ; M < TotalWads && !AutoFound ; M ++) /* AUTOINCLUDE file ? Then select it */ if ((WadInfo[M]->Drive == AutoInc[N]->Drive) && (!strcmp (WadInfo[M]->Path, AutoInc[N]->Path)) && (!strcmp (WadInfo[M]->OrigName, AutoInc[N]->OrigName))) { WadInfo[M]->Selected = TRUE; AutoFound = TRUE; } } CurrentPage = 0; LastPage = TotalWads / PAGE; InitVideo (); /* Open a VGA screen of 640x480x16 (80x30 characters in text mode) */ ScreenOpen = TRUE; /* Signal: in graphics mode */ PrText (-1, 1, 1, LWHITE, "DOOM ][ EasyWAD2 v1.01 - (C) 94-95 ThunderWare Research Center"); /* Draw the screen */ PrText (-1, 1, 72, DWHITE, "F1 = HELP"); PrText (-1, 11, 64, LWHITE, "PAGE 1 OF %2d", LastPage + 1); PrintDifficulties (0); PrintPlayTypes (0); PrintLevel (); PrintDeathmatch (FALSE); PrintV2Deathmatch (FALSE); PrintRespawnMonsters (FALSE); PrintNoMonsters (FALSE); PrintFastMonsters (FALSE); PrintWadFiles (); PrintPagers (0); PrintLevelPagers (0); PrintRdPrev (FALSE); PrintAutomatic (FALSE); PrintStart (FALSE); if (UseMouse) { ShowMouse (); /* Show the mouse pointer */ MouseStatus (); /* Get current mouse status */ Mouse.OldXx = Mouse.Xx; /* Initialize all non-hardware fields */ Mouse.OldYy = Mouse.Yy; Mouse.CoChange = TRUE; Mouse.LeftStillPressed = FALSE; CurrentField = NOFIELD; /* Signal: check FIELD type directly */ } else { CurrentField = FILEFIELD; /* Hi-lighting is only done in the FILEFIELD */ PreviousWad = 1; PrText (-1, 13, 1, LRED, WadInfo[0]->Name); /* Hi-light the first WAD file */ } More = TRUE; while (More) /* Or: while not [START] pressed ... */ { if (_bios_keybrd (_KEYBRD_READY)) /* Key pressed ? */ { Key = _bios_keybrd (_KEYBRD_READ); /* Read the key */ if ((Key & 0xFF00) == KEY_PAGEDOWN) HandlePreviousPage (TRUE); else if ((Key & 0xFF00) == KEY_PAGEUP) HandleNextPage (TRUE); else if (!UseMouse /* Only available if no mouse found */ && ((Key & 0xFF00) == KEY_CURSLEFT || (Key & 0xFF00) == KEY_CURSRIGHT || (Key & 0xFF00) == KEY_CURSUP || (Key & 0xFF00) == KEY_CURSDOWN || (Key & 0xFF00) == KEY_SELECTFILE)) HandleFile (Key & 0xFF00); else if ((Key & 0xFF00) == KEY_DELETEWAD) DeleteWadFile (); else if ((Key & 0xFF00) == KEY_TOGGLESORT) ToggleFileSort (); else if ((Key & 0xFF00) == KEY_RESCAN) RescanFiles (); else if ((Key & 0xFF00) == KEY_HELP) GiveHelp (); else if ((Key & 0xFF00) == KEY_TOGGLEFULL) ToggleFullName (); else switch (toupper ((char)(Key & 0x00FF))) { case KEY_ABORT : Bye (RETURNABORT, "Program aborted - Thank you for using DOOM ][ EasyWAD2\n"); case KEY_STARTGAME : More = HandleStartGame (TRUE); break; case KEY_DIFFICULTY : HandleDifficulty (TRUE); break; case KEY_LEVELUP : HandleNextLevel (TRUE); break; case KEY_LEVELDOWN : HandlePreviousLevel (TRUE); break; case KEY_DEATHMATCH : HandleDeathmatch (TRUE); break; case KEY_DEATHMATCHV2 : HandleV2Deathmatch (TRUE); break; case KEY_NOMONSTERS : HandleNoMonsters (TRUE); break; case KEY_RESPAWNMONST : HandleRespawnMonsters (TRUE); break; case KEY_FASTMONSTERS : HandleFastMonsters (TRUE); break; case KEY_NODES : case KEY_COMPORT : case KEY_PLAYTYPE : HandlePlayType (TRUE, toupper ((char)(Key & 0x00FF))); break; case KEY_AUTO : HandleAutomatic (TRUE); break; case KEY_READPREVIOUS : HandleReadPrev (TRUE); } } if (UseMouse && (Mouse.CoChange || Mouse.Left)) /* Mouse moved or left buttonpressed ? */ { if (Mouse.Yy > 11) /* Find out which FIELD is pointed to and handle that field */ HandleFile (0x0000); /* Signal: no key */ else if (Mouse.Yy > 1 && Mouse.Yy < 7 && Mouse.Xx > 26 && Mouse.Xx < 51) HandleDifficulty (FALSE); else if (Mouse.Yy > 1 && Mouse.Yy < 6 && Mouse.Xx > 53) HandlePlayType (FALSE, 0x00); else if (Mouse.Yy == 7 && Mouse.Xx > 53) HandleDeathmatch (FALSE); else if (Mouse.Yy == 8 && Mouse.Xx > 53) HandleV2Deathmatch (FALSE); else if (Mouse.Yy == 8 && Mouse.Xx > 26 && Mouse.Xx < 51) HandleNoMonsters (FALSE); else if (Mouse.Yy == 9 && Mouse.Xx > 26 && Mouse.Xx < 51) HandleRespawnMonsters (FALSE); else if (Mouse.Yy == 10 && Mouse.Xx > 26 && Mouse.Xx < 51) HandleFastMonsters (FALSE); else if (Mouse.Yy == 2 && Mouse.Xx > 1 && Mouse.Xx < 5) HandlePreviousLevel (FALSE); else if (Mouse.Yy == 2 && Mouse.Xx > 12 && Mouse.Xx < 16) HandleNextLevel (FALSE); else if (Mouse.Yy == 10 && Mouse.Xx > 76) HandleNextPage (FALSE); else if (Mouse.Yy == 10 && Mouse.Xx > 58 && Mouse.Xx < 62) HandlePreviousPage (FALSE); else if (Mouse.Yy == 8 && Mouse.Xx < 15) More = HandleStartGame (FALSE); else if (Mouse.Yy == 9 && Mouse.Xx < 15) HandleAutomatic (FALSE); else if (Mouse.Yy == 10 && Mouse.Xx < 15) HandleReadPrev (FALSE); else if (CurrentField != NOFIELD) /* No FIELD was pointed to */ { /* If previously a FIELD WAS pointed to, then un-highlite that field */ HideMouse (); UnselectPreviousField (NOFIELD); ShowMouse (); CurrentField = NOFIELD; } Mouse.OldXx = Mouse.Xx; Mouse.OldYy = Mouse.Yy; } if (UseMouse) { MouseStatus (); if (Mouse.Right) { HideMouse (); Bye (RETURNABORT, "Program aborted - Thank you for using DOOM ][ EasyWAD2\n"); } } } HideMouse (); _setvideomode (_DEFAULTMODE); /* Close the screen */ ScreenOpen = FALSE; /* Signal: in normal mode */ if (!(Fp = fopen (BATFILE, "w"))) /* Open "START.BAT" */ Bye (RETURNERROR, "ERROR - Cannot create file, disk full ?\n"); fprintf (Fp, "@ECHO OFF\n"); if (DoomDrive) fprintf (Fp, "%c:\n", ToName (DoomDrive)); if (strcmp (DoomDirectory, DEFAULTWADDIR)) fprintf (Fp, "CD %s\n", DoomDirectory); if (!NoAutoReturn) fprintf (Fp, "ECHO.| "); switch (PlayTypeActive) /* Print the correct command to the file */ { case 1 : fprintf (Fp, "%s", STARTALONE); break; case 2 : fprintf (Fp, "%s", IpxDriver); break; case 3 : fprintf (Fp, "%s", SerDriver); } fprintf (Fp, " @%s\\%s\n", CurPath, RESPONSEFILE); /* CurPath also contains the drive: part */ if (DoomDrive) fprintf (Fp, "%c:\n", ToName (CurDrive)); if (strcmp (DoomDirectory, DEFAULTWADDIR)) fprintf (Fp, "CD %s\n", CurPath + 2); fclose (Fp); if (!(Fp = fopen (RESPONSEFILE, "w"))) /* Open "START.OPT" */ Bye (RETURNERROR, "ERROR - Cannot create file, disk full ?\n"); SepChar = '\n'; SepCharNeeded = TRUE; switch (PlayTypeActive) { case 1 : SepCharNeeded = FALSE; break; case 2 : if (!OtherIpxDriver) fprintf (Fp, "%s%c%d%c%d", NUMPLAYERS, SepChar, NumNodesActive, SepChar, NetworkSocket); break; case 3 : if (!OtherSerDriver) fprintf (Fp, "%s%d", COMPORT, CommPortActive); } if (DifficultyActive != DEFAULTDIFFICULTY) /* Different difficulty ? */ { if (SepCharNeeded) fprintf (Fp, "%c", SepChar); SepCharNeeded = TRUE; if (!WarpDone) { fprintf (Fp, "%s%c%02d%c", GOTOANYTHING, SepChar, CurrentLevel, SepChar); WarpDone = TRUE; } fprintf (Fp, "%s%c%d", SKILL, SepChar, DifficultyActive); /* Then add it */ } if (DeathmatchOn != DEFAULTDMATCH) /* DEATHMATCH wanted (and implemented) ? */ { if (SepCharNeeded) fprintf (Fp, "%c", SepChar); SepCharNeeded = TRUE; fprintf (Fp, "%s", DMATCH); } if (DeathmatchV2On != DEFAULTDMATCHV2) /* DEATHMATCH v2.0 wanted ? */ { if (SepCharNeeded) fprintf (Fp, "%c", SepChar); SepCharNeeded = TRUE; fprintf (Fp, "%s%c%s", DMATCH, SepChar, DMATCHV2); } if (NoMonstersOn != DEFAULTNOMONSTERS) /* NOMONSTERS wanted ? */ { if (SepCharNeeded) fprintf (Fp, "%c", SepChar); SepCharNeeded = TRUE; if (!WarpDone) { fprintf (Fp, "%s%c%02d%c", GOTOANYTHING, SepChar, CurrentLevel, SepChar); WarpDone = TRUE; } fprintf (Fp, "%s", NOMONSTERS); } if (RespMonstersOn != DEFAULTRESPAWN) /* RESPAWN wanted ? */ { if (SepCharNeeded) fprintf (Fp, "%c", SepChar); SepCharNeeded = TRUE; if (!WarpDone) { fprintf (Fp, "%s%c%02d%c", GOTOANYTHING, SepChar, CurrentLevel, SepChar); WarpDone = TRUE; } fprintf (Fp, "%s", RESPAWNMONSTERS); } if (FastMonstersOn != DEFAULTFASTMONST) /* FASTMONSTERS wanted ? */ { if (SepCharNeeded) fprintf (Fp, "%c", SepChar); SepCharNeeded = TRUE; if (!WarpDone) { fprintf (Fp, "%s%c%02d%c", GOTOANYTHING, SepChar, CurrentLevel, SepChar); WarpDone = TRUE; } fprintf (Fp, "%s", FASTMONSTERS); } if (CurrentLevel != DEFAULTLEVEL) /* Different starting level ? */ { if (SepCharNeeded) fprintf (Fp, "%c", SepChar); SepCharNeeded = TRUE; fprintf (Fp, "%s%c%02d", GOTOANYTHING, SepChar, CurrentLevel); } for (M = 0 ; M < MAXADDSWITCHES ; M ++) /* Now add the direct switches */ if (ExtCom[M].InUse) { if (SepCharNeeded) fprintf (Fp, "%c", SepChar); SepCharNeeded = TRUE; fprintf (Fp, ExtCom[M].Command); } FirstWad = TRUE; strcat (DoomDirectory, "\\"); for (M = 0 ; M < TotalWads ; M ++) /* Add each selected WAD file */ if (WadInfo[M]->Selected) { if (FirstWad) /* Before adding the first, add "-FILE" */ { FirstWad = FALSE; if (SepCharNeeded) fprintf (Fp, "%c", SepChar); fprintf (Fp, "%s", INCFILE); } if (WadInfo[M]->Drive && WadInfo[M]->Drive != DoomDrive) fprintf (Fp, "%c%c:", SepChar, ToName (WadInfo[M]->Drive)); /* Add the drive if found */ else fprintf (Fp, "%c", SepChar); if (strcmp (WadInfo[M]->Path, DEFAULTWADDIR)) if (strncmp (WadInfo[M]->Path, DoomDirectory, strlen (DoomDirectory))) /* Subdir of the DOOM directory ? */ fprintf (Fp, "%s\\", WadInfo[M]->Path); /* Add the path if not */ else if (WadInfo[M]->Drive == DoomDrive) /* Drive matches too ? */ fprintf (Fp, "%s\\", WadInfo[M]->Path + strlen (DoomDirectory)); /* Cut DOOM directory part */ else fprintf (Fp, "%s\\", WadInfo[M]->Path); /* Add the path if not */ fprintf (Fp, "%s", WadInfo[M]->OrigName); /* Add the filename */ } fprintf (Fp, "\n"); /* Add the newline */ fclose (Fp); /* Done; free all memory and shut down the program */ DeAllocateAll (); exit (RETURNSTART); }