]> 4ch.mooo.com Git - 16.git/commitdiff
wwww added catacombs source~
authorsparky4 <sparky4@cock.li>
Tue, 23 Jun 2015 16:28:48 +0000 (11:28 -0500)
committersparky4 <sparky4@cock.li>
Tue, 23 Jun 2015 16:28:48 +0000 (11:28 -0500)
new file:   16/cawat/16_in.c
new file:   16/cawat/16_in.h
new file:   16/cawat/16_mm.c
new file:   16/cawat/16_mm.h
new file:   16/cawat/ARMGAME.DSK
new file:   16/cawat/ARMGAME.PRJ
new file:   16/cawat/AUDIOARM.H
new file:   16/cawat/C5_ACT1.C
new file:   16/cawat/C5_ACT2.C
new file:   16/cawat/C5_ACT3.C
new file:   16/cawat/C5_ACT4.C
new file:   16/cawat/C5_ASM.ASM
new file:   16/cawat/C5_DEBUG.C
new file:   16/cawat/C5_DRAW.C
new file:   16/cawat/C5_GAME.C
new file:   16/cawat/C5_MAIN.C
new file:   16/cawat/C5_PLAY.C
new file:   16/cawat/C5_SCALE.C
new file:   16/cawat/C5_SCA_A.ASM
new file:   16/cawat/C5_STATE.C
new file:   16/cawat/C5_TRACE.C
new file:   16/cawat/C5_WIZ.C
new file:   16/cawat/COPYING
new file:   16/cawat/DEF.H
new file:   16/cawat/GELIB.C
new file:   16/cawat/GELIB.H
new file:   16/cawat/GFXE_ARM.EQU
new file:   16/cawat/GFXE_ARM.H
new file:   16/cawat/ID_ASM.EQU
new file:   16/cawat/ID_CA.C
new file:   16/cawat/ID_CA.H
new file:   16/cawat/ID_HEADS.H
new file:   16/cawat/ID_IN.C
new file:   16/cawat/ID_IN.H
new file:   16/cawat/ID_MM.C
new file:   16/cawat/ID_MM.H
new file:   16/cawat/ID_RF.C
new file:   16/cawat/ID_RF.H
new file:   16/cawat/ID_RF_A.ASM
new file:   16/cawat/ID_SD.C
new file:   16/cawat/ID_SD.H
new file:   16/cawat/ID_STRS.H
new file:   16/cawat/ID_US.C
new file:   16/cawat/ID_US.H
new file:   16/cawat/ID_US_1.C
new file:   16/cawat/ID_US_2.C
new file:   16/cawat/ID_US_A.ASM
new file:   16/cawat/ID_VW.C
new file:   16/cawat/ID_VW.H
new file:   16/cawat/ID_VW_A.ASM
new file:   16/cawat/ID_VW_AC.ASM
new file:   16/cawat/ID_VW_AE.ASM
new file:   16/cawat/JABHACK.ASM
new file:   16/cawat/JAMPAK.C
new file:   16/cawat/JAMPAK.H
new file:   16/cawat/JAM_IO.C
new file:   16/cawat/JAM_IO.H
new file:   16/cawat/LZHUF.C
new file:   16/cawat/LZHUFF.H
new file:   16/cawat/LZW.C
new file:   16/cawat/LZW.H
new file:   16/cawat/MAPSARM.H
new file:   16/cawat/README.md
new file:   16/cawat/SL_FILE.H
new file:   16/cawat/SOFT.C
new file:   16/cawat/SOFT.H
new file:   16/cawat/TC0000.SWP
new file:   16/cawat/TD.TR
new file:   16/cawat/TDCONFIG.TD
new file:   16/cawat/cawat.bfproject
new file:   16/cawat/lib_head.h
new file:   16/cawat/port.h
new file:   16/cawat/types.h

73 files changed:
16/cawat/16_in.c [new file with mode: 0644]
16/cawat/16_in.h [new file with mode: 0644]
16/cawat/16_mm.c [new file with mode: 0644]
16/cawat/16_mm.h [new file with mode: 0644]
16/cawat/ARMGAME.DSK [new file with mode: 0644]
16/cawat/ARMGAME.PRJ [new file with mode: 0644]
16/cawat/AUDIOARM.H [new file with mode: 0644]
16/cawat/C5_ACT1.C [new file with mode: 0644]
16/cawat/C5_ACT2.C [new file with mode: 0644]
16/cawat/C5_ACT3.C [new file with mode: 0644]
16/cawat/C5_ACT4.C [new file with mode: 0644]
16/cawat/C5_ASM.ASM [new file with mode: 0644]
16/cawat/C5_DEBUG.C [new file with mode: 0644]
16/cawat/C5_DRAW.C [new file with mode: 0644]
16/cawat/C5_GAME.C [new file with mode: 0644]
16/cawat/C5_MAIN.C [new file with mode: 0644]
16/cawat/C5_PLAY.C [new file with mode: 0644]
16/cawat/C5_SCALE.C [new file with mode: 0644]
16/cawat/C5_SCA_A.ASM [new file with mode: 0644]
16/cawat/C5_STATE.C [new file with mode: 0644]
16/cawat/C5_TRACE.C [new file with mode: 0644]
16/cawat/C5_WIZ.C [new file with mode: 0644]
16/cawat/COPYING [new file with mode: 0644]
16/cawat/DEF.H [new file with mode: 0644]
16/cawat/GELIB.C [new file with mode: 0644]
16/cawat/GELIB.H [new file with mode: 0644]
16/cawat/GFXE_ARM.EQU [new file with mode: 0644]
16/cawat/GFXE_ARM.H [new file with mode: 0644]
16/cawat/ID_ASM.EQU [new file with mode: 0644]
16/cawat/ID_CA.C [new file with mode: 0644]
16/cawat/ID_CA.H [new file with mode: 0644]
16/cawat/ID_HEADS.H [new file with mode: 0644]
16/cawat/ID_IN.C [new file with mode: 0644]
16/cawat/ID_IN.H [new file with mode: 0644]
16/cawat/ID_MM.C [new file with mode: 0644]
16/cawat/ID_MM.H [new file with mode: 0644]
16/cawat/ID_RF.C [new file with mode: 0644]
16/cawat/ID_RF.H [new file with mode: 0644]
16/cawat/ID_RF_A.ASM [new file with mode: 0644]
16/cawat/ID_SD.C [new file with mode: 0644]
16/cawat/ID_SD.H [new file with mode: 0644]
16/cawat/ID_STRS.H [new file with mode: 0644]
16/cawat/ID_US.C [new file with mode: 0644]
16/cawat/ID_US.H [new file with mode: 0644]
16/cawat/ID_US_1.C [new file with mode: 0644]
16/cawat/ID_US_2.C [new file with mode: 0644]
16/cawat/ID_US_A.ASM [new file with mode: 0644]
16/cawat/ID_VW.C [new file with mode: 0644]
16/cawat/ID_VW.H [new file with mode: 0644]
16/cawat/ID_VW_A.ASM [new file with mode: 0644]
16/cawat/ID_VW_AC.ASM [new file with mode: 0644]
16/cawat/ID_VW_AE.ASM [new file with mode: 0644]
16/cawat/JABHACK.ASM [new file with mode: 0644]
16/cawat/JAMPAK.C [new file with mode: 0644]
16/cawat/JAMPAK.H [new file with mode: 0644]
16/cawat/JAM_IO.C [new file with mode: 0644]
16/cawat/JAM_IO.H [new file with mode: 0644]
16/cawat/LZHUF.C [new file with mode: 0644]
16/cawat/LZHUFF.H [new file with mode: 0644]
16/cawat/LZW.C [new file with mode: 0644]
16/cawat/LZW.H [new file with mode: 0644]
16/cawat/MAPSARM.H [new file with mode: 0644]
16/cawat/README.md [new file with mode: 0644]
16/cawat/SL_FILE.H [new file with mode: 0644]
16/cawat/SOFT.C [new file with mode: 0644]
16/cawat/SOFT.H [new file with mode: 0644]
16/cawat/TC0000.SWP [new file with mode: 0644]
16/cawat/TD.TR [new file with mode: 0644]
16/cawat/TDCONFIG.TD [new file with mode: 0644]
16/cawat/cawat.bfproject [new file with mode: 0644]
16/cawat/lib_head.h [new file with mode: 0644]
16/cawat/port.h [new file with mode: 0644]
16/cawat/types.h [new file with mode: 0644]

diff --git a/16/cawat/16_in.c b/16/cawat/16_in.c
new file mode 100644 (file)
index 0000000..67e02e2
--- /dev/null
@@ -0,0 +1,1287 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+//     ID Engine\r
+//     ID_IN.c - Input Manager\r
+//     v1.0d1\r
+//     By Jason Blochowiak\r
+//\r
+\r
+//\r
+//     This module handles dealing with the various input devices\r
+//\r
+//     Depends on: Memory Mgr (for demo recording), Sound Mgr (for timing stuff),\r
+//                             User Mgr (for command line parms)\r
+//\r
+//     Globals:\r
+//             LastScan - The keyboard scan code of the last key pressed\r
+//             LastASCII - The ASCII value of the last key pressed\r
+//     DEBUG - there are more globals\r
+//\r
+\r
+//#include "ID_HEADS.H"
+#include "16_in.h"\r
+//#pragma      hdrstop\r
+\r
+#define        KeyInt  9       // The keyboard ISR number\r
+\r
+// Stuff for the joystick\r
+#define        JoyScaleMax             32768\r
+#define        JoyScaleShift   8\r
+#define        MaxJoyValue             5000\r
+\r
+//     Global variables\r
+               boolean JoystickCalibrated=false;               // MDM (GAMERS EDGE) - added\r
+               ControlType ControlTypeUsed;                            // MDM (GAMERS EDGE) - added\r
+\r
+               boolean         Keyboard[NumCodes],\r
+                                       JoysPresent[MaxJoys],\r
+                                       MousePresent;\r
+               boolean         Paused;\r
+               char            LastASCII;\r
+               ScanCode        LastScan;\r
+               KeyboardDef     KbdDefs[MaxKbds] = {{0x1d,0x38,0x47,0x48,0x49,0x4b,0x4d,0x4f,0x50,0x51}};\r
+               JoystickDef     JoyDefs[MaxJoys];\r
+               ControlType     Controls[MaxPlayers];\r
+\r
+//             Demo            DemoMode = demo_Off;\r
+//             byte /*_1seg*/  *DemoBuffer;\r
+//             word            DemoOffset,DemoSize;\r
+\r
+//     Internal variables\r
+static boolean         IN_Started;\r
+static boolean         CapsLock;\r
+static ScanCode        CurCode,LastCode;\r
+static byte        far ASCIINames[] =          // Unshifted ASCII for scan codes\r
+                                       {\r
+//      0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F\r
+       0  ,27 ,'1','2','3','4','5','6','7','8','9','0','-','=',8  ,9  ,        // 0\r
+       'q','w','e','r','t','y','u','i','o','p','[',']',13 ,0  ,'a','s',        // 1\r
+       'd','f','g','h','j','k','l',';',39 ,'`',0  ,92 ,'z','x','c','v',        // 2\r
+       'b','n','m',',','.','/',0  ,'*',0  ,' ',0  ,0  ,0  ,0  ,0  ,0  ,        // 3\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,'7','8','9','-','4','5','6','+','1',        // 4\r
+       '2','3','0',127,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 5\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 6\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0           // 7\r
+                                       },\r
+                                       far ShiftNames[] =              // Shifted ASCII for scan codes\r
+                                       {\r
+//      0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F\r
+       0  ,27 ,'!','@','#','$','%','^','&','*','(',')','_','+',8  ,9  ,        // 0\r
+       'Q','W','E','R','T','Y','U','I','O','P','{','}',13 ,0  ,'A','S',        // 1\r
+       'D','F','G','H','J','K','L',':',34 ,'~',0  ,'|','Z','X','C','V',        // 2\r
+       'B','N','M','<','>','?',0  ,'*',0  ,' ',0  ,0  ,0  ,0  ,0  ,0  ,        // 3\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,'7','8','9','-','4','5','6','+','1',        // 4\r
+       '2','3','0',127,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 5\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 6\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0           // 7\r
+                                       },\r
+                                       far SpecialNames[] =    // ASCII for 0xe0 prefixed codes\r
+                                       {\r
+//      0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 0\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,13 ,0  ,0  ,0  ,        // 1\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 2\r
+       0  ,0  ,0  ,0  ,0  ,'/',0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 3\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 4\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 5\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 6\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0           // 7\r
+                                       },\r
+\r
+#if 0\r
+                                       *ScanNames[] =          // Scan code names with single chars\r
+                                       {\r
+       "?","?","1","2","3","4","5","6","7","8","9","0","-","+","?","?",\r
+       "Q","W","E","R","T","Y","U","I","O","P","[","]","|","?","A","S",\r
+       "D","F","G","H","J","K","L",";","\"","?","?","?","Z","X","C","V",\r
+       "B","N","M",",",".","/","?","?","?","?","?","?","?","?","?","?",\r
+       "?","?","?","?","?","?","?","?","\xf","?","-","\x15","5","\x11","+","?",\r
+       "\x13","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?",\r
+       "?","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?",\r
+       "?","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?"\r
+                                       },      // DEBUG - consolidate these\r
+#endif\r
+\r
+                                       far ExtScanCodes[] =    // Scan codes with >1 char names\r
+                                       {\r
+       1,0xe,0xf,0x1d,0x2a,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,\r
+       0x3f,0x40,0x41,0x42,0x43,0x44,0x57,0x59,0x46,0x1c,0x36,\r
+       0x37,0x38,0x47,0x49,0x4f,0x51,0x52,0x53,0x45,0x48,\r
+       0x50,0x4b,0x4d,0x00\r
+                                       };\r
+#if 0\r
+                                       *ExtScanNames[] =       // Names corresponding to ExtScanCodes\r
+                                       {\r
+       "Esc","BkSp","Tab","Ctrl","LShft","Space","CapsLk","F1","F2","F3","F4",\r
+       "F5","F6","F7","F8","F9","F10","F11","F12","ScrlLk","Enter","RShft",\r
+       "PrtSc","Alt","Home","PgUp","End","PgDn","Ins","Del","NumLk","Up",\r
+       "Down","Left","Right",""\r
+                                       };\r
+#endif\r
+static Direction       DirTable[] =            // Quick lookup for total direction\r
+                                       {\r
+                                               dir_NorthWest,  dir_North,      dir_NorthEast,\r
+                                               dir_West,               dir_None,       dir_East,\r
+                                               dir_SouthWest,  dir_South,      dir_SouthEast\r
+                                       };\r
+\r
+static void                    (*INL_KeyHook)(void);\r
+static void interrupt  (*OldKeyVect)(void);\r
+\r
+static char                    *ParmStrings[] = {"nojoys","nomouse",nil};\r
+\r
+//     Internal routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_KeyService() - Handles a keyboard interrupt (key up/down)\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void interrupt\r
+INL_KeyService(void)\r
+{\r
+static boolean special;\r
+               byte    k,c,\r
+                               temp;\r
+\r
+       k = inp(0x60);  // Get the scan code\r
+\r
+       // Tell the XT keyboard controller to clear the key\r
+       outp(0x61,(temp = inp(0x61)) | 0x80);\r
+       outp(0x61,temp);\r
+\r
+       if (k == 0xe0)          // Special key prefix\r
+               special = true;\r
+       else if (k == 0xe1)     // Handle Pause key\r
+               Paused = true;\r
+       else\r
+       {\r
+               if (k & 0x80)   // Break code\r
+               {\r
+                       k &= 0x7f;\r
+\r
+// DEBUG - handle special keys: ctl-alt-delete, print scrn\r
+\r
+                       Keyboard[k] = false;\r
+               }\r
+               else                    // Make code\r
+               {\r
+                       LastCode = CurCode;\r
+                       CurCode = LastScan = k;\r
+                       Keyboard[k] = true;\r
+\r
+                       if (special)\r
+                               c = SpecialNames[k];\r
+                       else\r
+                       {\r
+                               if (k == sc_CapsLock)\r
+                               {\r
+                                       CapsLock ^= true;\r
+                                       // DEBUG - make caps lock light work\r
+                               }\r
+\r
+                               if (Keyboard[sc_LShift] || Keyboard[sc_RShift]) // If shifted\r
+                               {\r
+                                       c = ShiftNames[k];\r
+                                       if ((c >= 'A') && (c <= 'Z') && CapsLock)\r
+                                               c += 'a' - 'A';\r
+                               }\r
+                               else\r
+                               {\r
+                                       c = ASCIINames[k];\r
+                                       if ((c >= 'a') && (c <= 'z') && CapsLock)\r
+                                               c -= 'a' - 'A';\r
+                               }\r
+                       }\r
+                       if (c)\r
+                               LastASCII = c;\r
+               }\r
+\r
+               special = false;\r
+       }\r
+\r
+       if (INL_KeyHook && !special)\r
+               INL_KeyHook();\r
+       outp(0x20,0x20);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_GetMouseDelta() - Gets the amount that the mouse has moved from the\r
+//             mouse driver\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_GetMouseDelta(int *x,int *y)\r
+{\r
+       Mouse(MDelta);\r
+       *x = _CX;\r
+       *y = _DX;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_GetMouseButtons() - Gets the status of the mouse buttons from the\r
+//             mouse driver\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static word\r
+INL_GetMouseButtons(void)\r
+{\r
+       word    buttons;\r
+\r
+       Mouse(MButtons);\r
+       buttons = _BX;\r
+       return(buttons);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_GetJoyAbs() - Reads the absolute position of the specified joystick\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_GetJoyAbs(word joy,word *xp,word *yp)\r
+{\r
+       byte    xb,yb,\r
+                       xs,ys;\r
+       word    x,y;\r
+\r
+       x = y = 0;\r
+       xs = joy? 2 : 0;                // Find shift value for x axis\r
+       xb = 1 << xs;                   // Use shift value to get x bit mask\r
+       ys = joy? 3 : 1;                // Do the same for y axis\r
+       yb = 1 << ys;\r
+\r
+// Read the absolute joystick values
+       __asm
+       {\r
+               pushf                           // Save some registers\r
+               push    si\r
+               push    di\r
+               cli                                     // Make sure an interrupt doesn't screw the timings\r
+\r
+\r
+               mov             dx,0x201\r
+               in              al,dx\r
+               out             dx,al           // Clear the resistors\r
+\r
+               mov             ah,[xb]         // Get masks into registers\r
+               mov             ch,[yb]\r
+\r
+               xor             si,si           // Clear count registers\r
+               xor             di,di\r
+               xor             bh,bh           // Clear high byte of bx for later\r
+
+               push    bp                      // Don't mess up stack frame\r
+               mov             bp,MaxJoyValue\r
+\r
+loop:\r
+               in              al,dx           // Get bits indicating whether all are finished\r
+\r
+               dec             bp                      // Check bounding register\r
+               jz              done            // We have a silly value - abort\r
+\r
+               mov             bl,al           // Duplicate the bits\r
+               and             bl,ah           // Mask off useless bits (in [xb])\r
+               add             si,bx           // Possibly increment count register\r
+               mov             cl,bl           // Save for testing later\r
+\r
+               mov             bl,al\r
+               and             bl,ch           // [yb]\r
+               add             di,bx\r
+\r
+               add             cl,bl\r
+               jnz             loop            // If both bits were 0, drop out\r
+\r
+done:\r
+     pop               bp\r
+
+               mov             cl,[xs]         // Get the number of bits to shift\r
+               shr             si,cl           //  and shift the count that many times\r
+\r
+               mov             cl,[ys]\r
+               shr             di,cl\r
+\r
+               mov             [x],si          // Store the values into the variables\r
+               mov             [y],di\r
+
+               pop             di\r
+               pop             si\r
+               popf                            // Restore the registers
+       }\r
+\r
+       *xp = x;\r
+       *yp = y;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_GetJoyDelta() - Returns the relative movement of the specified\r
+//             joystick (from +/-127, scaled adaptively)\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_GetJoyDelta(word joy,int *dx,int *dy,boolean adaptive)\r
+{\r
+       word            x,y;\r
+       dword   time;\r
+       JoystickDef     *def;\r
+static dword   lasttime;\r
+\r
+       IN_GetJoyAbs(joy,&x,&y);\r
+       def = JoyDefs + joy;\r
+\r
+       if (x < def->threshMinX)\r
+       {\r
+               if (x < def->joyMinX)\r
+                       x = def->joyMinX;\r
+\r
+               x = -(x - def->threshMinX);\r
+               x *= def->joyMultXL;\r
+               x >>= JoyScaleShift;\r
+               *dx = (x > 127)? -127 : -x;\r
+       }\r
+       else if (x > def->threshMaxX)\r
+       {\r
+               if (x > def->joyMaxX)\r
+                       x = def->joyMaxX;\r
+\r
+               x = x - def->threshMaxX;\r
+               x *= def->joyMultXH;\r
+               x >>= JoyScaleShift;\r
+               *dx = (x > 127)? 127 : x;\r
+       }\r
+       else\r
+               *dx = 0;\r
+\r
+       if (y < def->threshMinY)\r
+       {\r
+               if (y < def->joyMinY)\r
+                       y = def->joyMinY;\r
+\r
+               y = -(y - def->threshMinY);\r
+               y *= def->joyMultYL;\r
+               y >>= JoyScaleShift;\r
+               *dy = (y > 127)? -127 : -y;\r
+       }\r
+       else if (y > def->threshMaxY)\r
+       {\r
+               if (y > def->joyMaxY)\r
+                       y = def->joyMaxY;\r
+\r
+               y = y - def->threshMaxY;\r
+               y *= def->joyMultYH;\r
+               y >>= JoyScaleShift;\r
+               *dy = (y > 127)? 127 : y;\r
+       }\r
+       else\r
+               *dy = 0;\r
+\r
+       if (adaptive)\r
+       {\r
+               time = (TimeCount - lasttime) / 2;\r
+               if (time)\r
+               {\r
+                       if (time > 8)\r
+                               time = 8;\r
+                       *dx *= time;\r
+                       *dy *= time;\r
+               }\r
+       }\r
+       lasttime = TimeCount;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_GetJoyButtons() - Returns the button status of the specified\r
+//             joystick\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static word\r
+INL_GetJoyButtons(word joy)\r
+{\r
+register       word    result;\r
+\r
+       result = inp(0x201);    // Get all the joystick buttons\r
+       result >>= joy? 6 : 4;  // Shift into bits 0-1\r
+       result &= 3;                            // Mask off the useless bits\r
+       result ^= 3;\r
+       return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_GetJoyButtonsDB() - Returns the de-bounced button status of the\r
+//             specified joystick\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+word\r
+IN_GetJoyButtonsDB(word joy)\r
+{\r
+       dword   lasttime;\r
+       word            result1,result2;\r
+\r
+       do\r
+       {\r
+               result1 = INL_GetJoyButtons(joy);\r
+               lasttime = TimeCount;\r
+               while (TimeCount == lasttime)\r
+                       ;\r
+               result2 = INL_GetJoyButtons(joy);\r
+       } while (result1 != result2);\r
+       return(result1);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_StartKbd() - Sets up my keyboard stuff for use\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_StartKbd(void)\r
+{\r
+       INL_KeyHook = 0;        // Clear key hook\r
+\r
+       IN_ClearKeysDown();\r
+\r
+       OldKeyVect = getvect(KeyInt);\r
+       setvect(KeyInt,INL_KeyService);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_ShutKbd() - Restores keyboard control to the BIOS\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_ShutKbd(void)\r
+{\r
+       poke(0x40,0x17,peek(0x40,0x17) & 0xfaf0);       // Clear ctrl/alt/shift flags\r
+\r
+       setvect(KeyInt,OldKeyVect);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_StartMouse() - Detects and sets up the mouse\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+INL_StartMouse(void)\r
+{\r
+       if (getvect(MouseInt))\r
+       {\r
+               Mouse(MReset);\r
+               if (_AX == 0xffff)\r
+                       return(true);\r
+       }\r
+       return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_ShutMouse() - Cleans up after the mouse\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_ShutMouse(void)\r
+{\r
+}\r
+\r
+//\r
+//     INL_SetJoyScale() - Sets up scaling values for the specified joystick\r
+//\r
+static void\r
+INL_SetJoyScale(word joy)\r
+{\r
+       JoystickDef     *def;\r
+\r
+       def = &JoyDefs[joy];\r
+       def->joyMultXL = JoyScaleMax / (def->threshMinX - def->joyMinX);\r
+       def->joyMultXH = JoyScaleMax / (def->joyMaxX - def->threshMaxX);\r
+       def->joyMultYL = JoyScaleMax / (def->threshMinY - def->joyMinY);\r
+       def->joyMultYH = JoyScaleMax / (def->joyMaxY - def->threshMaxY);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_SetupJoy() - Sets up thresholding values and calls INL_SetJoyScale()\r
+//             to set up scaling values\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_SetupJoy(word joy,word minx,word maxx,word miny,word maxy)\r
+{\r
+       word            d,r;\r
+       JoystickDef     *def;\r
+\r
+       def = &JoyDefs[joy];\r
+\r
+       def->joyMinX = minx;\r
+       def->joyMaxX = maxx;\r
+       r = maxx - minx;\r
+       d = r / 5;\r
+       def->threshMinX = ((r / 2) - d) + minx;\r
+       def->threshMaxX = ((r / 2) + d) + minx;\r
+\r
+       def->joyMinY = miny;\r
+       def->joyMaxY = maxy;\r
+       r = maxy - miny;\r
+       d = r / 5;\r
+       def->threshMinY = ((r / 2) - d) + miny;\r
+       def->threshMaxY = ((r / 2) + d) + miny;\r
+\r
+       INL_SetJoyScale(joy);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_StartJoy() - Detects & auto-configures the specified joystick\r
+//                                     The auto-config assumes the joystick is centered\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+INL_StartJoy(word joy)\r
+{\r
+       word            x,y;\r
+\r
+       IN_GetJoyAbs(joy,&x,&y);\r
+\r
+       if\r
+       (\r
+               ((x == 0) || (x > MaxJoyValue - 10))\r
+       ||      ((y == 0) || (y > MaxJoyValue - 10))\r
+       )\r
+               return(false);\r
+       else\r
+       {\r
+               IN_SetupJoy(joy,0,x * 2,0,y * 2);\r
+               return(true);\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_ShutJoy() - Cleans up the joystick stuff\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_ShutJoy(word joy)\r
+{\r
+       JoysPresent[joy] = false;\r
+}\r
+\r
+//     Public routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_Startup() - Starts up the Input Mgr\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_Startup(void)\r
+{\r
+       boolean checkjoys,checkmouse;\r
+       word    i;\r
+\r
+       if (IN_Started)\r
+               return;\r
+\r
+       checkjoys = true;\r
+       checkmouse = true;\r
+       for (i = 1;i < _argc;i++)\r
+       {\r
+               switch (US_CheckParm(_argv[i],ParmStrings))\r
+               {\r
+               case 0:\r
+                       checkjoys = false;\r
+                       break;\r
+               case 1:\r
+                       checkmouse = false;\r
+                       break;\r
+               }\r
+       }\r
+\r
+       INL_StartKbd();\r
+       MousePresent = checkmouse? INL_StartMouse() : false;\r
+\r
+       for (i = 0;i < MaxJoys;i++)\r
+               JoysPresent[i] = checkjoys? INL_StartJoy(i) : false;\r
+\r
+       IN_Started = true;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_Default() - Sets up default conditions for the Input Mgr\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_Default(boolean gotit,ControlType in)\r
+{\r
+       if\r
+       (\r
+               (!gotit)\r
+       ||      ((in == ctrl_Joystick1) && !JoysPresent[0])\r
+       ||      ((in == ctrl_Joystick2) && !JoysPresent[1])\r
+       ||      ((in == ctrl_Mouse) && !MousePresent)\r
+       )\r
+               in = ctrl_Keyboard1;\r
+       IN_SetControlType(0,in);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_Shutdown() - Shuts down the Input Mgr\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_Shutdown(void)\r
+{\r
+       word    i;\r
+\r
+       if (!IN_Started)\r
+               return;\r
+\r
+       INL_ShutMouse();\r
+       for (i = 0;i < MaxJoys;i++)\r
+               INL_ShutJoy(i);\r
+       INL_ShutKbd();\r
+\r
+       IN_Started = false;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_SetKeyHook() - Sets the routine that gets called by INL_KeyService()\r
+//                     everytime a real make/break code gets hit\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_SetKeyHook(void (*hook)())\r
+{\r
+       INL_KeyHook = hook;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_ClearKeyDown() - Clears the keyboard array\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_ClearKeysDown(void)\r
+{\r
+       int     i;\r
+\r
+       LastScan = sc_None;\r
+       LastASCII = key_None;\r
+       for (i = 0;i < NumCodes;i++)\r
+               Keyboard[i] = false;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_AdjustCursor() - Internal routine of common code from IN_ReadCursor()\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_AdjustCursor(CursorInfo *info,word buttons,int dx,int dy)\r
+{\r
+       if (buttons & (1 << 0))\r
+               info->button0 = true;\r
+       if (buttons & (1 << 1))\r
+               info->button1 = true;\r
+\r
+       info->x += dx;\r
+       info->y += dy;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_ReadCursor() - Reads the input devices and fills in the cursor info\r
+//             struct\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_ReadCursor(CursorInfo *info)\r
+{\r
+       word    i,\r
+                       buttons;\r
+       int             dx,dy;\r
+\r
+       info->x = info->y = 0;\r
+       info->button0 = info->button1 = false;\r
+\r
+       if (MousePresent)\r
+       {\r
+               buttons = INL_GetMouseButtons();\r
+               INL_GetMouseDelta(&dx,&dy);\r
+               INL_AdjustCursor(info,buttons,dx,dy);\r
+       }\r
+\r
+       for (i = 0;i < MaxJoys;i++)\r
+       {\r
+               if (!JoysPresent[i])\r
+                       continue;\r
+\r
+               buttons = INL_GetJoyButtons(i);\r
+               INL_GetJoyDelta(i,&dx,&dy,true);\r
+               dx /= 64;\r
+               dy /= 64;\r
+               INL_AdjustCursor(info,buttons,dx,dy);\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_ReadControl() - Reads the device associated with the specified\r
+//             player and fills in the control info struct\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_ReadControl(int player,ControlInfo *info)\r
+{\r
+                       boolean         realdelta=false;                                // MDM (GAMERS EDGE)\r
+                       byte            dbyte;\r
+                       word            buttons;\r
+                       int                     dx,dy;\r
+                       Motion          mx,my;\r
+                       ControlType     type;\r
+register       KeyboardDef     *def;\r
+\r
+       dx = dy = 0;\r
+       mx = my = motion_None;\r
+       buttons = 0;\r
+\r
+#if 0\r
+       if (DemoMode == demo_Playback)\r
+       {\r
+               dbyte = DemoBuffer[DemoOffset + 1];\r
+               my = (dbyte & 3) - 1;\r
+               mx = ((dbyte >> 2) & 3) - 1;\r
+               buttons = (dbyte >> 4) & 3;\r
+\r
+               if (!(--DemoBuffer[DemoOffset]))\r
+               {\r
+                       DemoOffset += 2;\r
+                       if (DemoOffset >= DemoSize)\r
+                               DemoMode = demo_PlayDone;\r
+               }\r
+\r
+               realdelta = false;\r
+       }\r
+       else if (DemoMode == demo_PlayDone)\r
+               Quit("Demo playback exceeded");\r
+       else\r
+#endif\r
+       {\r
+                                                                                                                       // MDM begin (GAMERS EDGE) - added this block\r
+               ControlTypeUsed = ctrl_None;\r
+\r
+               // Handle mouse input...\r
+               //\r
+               if ((MousePresent) && (ControlTypeUsed == ctrl_None))\r
+               {\r
+                       INL_GetMouseDelta(&dx,&dy);\r
+                       buttons = INL_GetMouseButtons();\r
+                       realdelta = true;\r
+                       if (dx || dy || buttons)\r
+                               ControlTypeUsed = ctrl_Mouse;\r
+               }\r
+\r
+               // Handle joystick input...\r
+               //\r
+               if ((JoystickCalibrated) && (ControlTypeUsed == ctrl_None))\r
+               {\r
+                       type = ctrl_Joystick1;\r
+                       INL_GetJoyDelta(type - ctrl_Joystick,&dx,&dy,false);\r
+                       buttons = INL_GetJoyButtons(type - ctrl_Joystick);\r
+                       realdelta = true;\r
+                       if (dx || dy || buttons)\r
+                               ControlTypeUsed = ctrl_Joystick;\r
+               }\r
+\r
+               // Handle keyboard input...\r
+               //\r
+               if (ControlTypeUsed == ctrl_None)\r
+               {\r
+                       type = ctrl_Keyboard1;\r
+                       def = &KbdDefs[type - ctrl_Keyboard];\r
+\r
+                       if (Keyboard[def->upleft])\r
+                               mx = motion_Left,my = motion_Up;\r
+                       else if (Keyboard[def->upright])\r
+                               mx = motion_Right,my = motion_Up;\r
+                       else if (Keyboard[def->downleft])\r
+                               mx = motion_Left,my = motion_Down;\r
+                       else if (Keyboard[def->downright])\r
+                               mx = motion_Right,my = motion_Down;\r
+\r
+                       if (Keyboard[def->up])\r
+                               my = motion_Up;\r
+                       else if (Keyboard[def->down])\r
+                               my = motion_Down;\r
+\r
+                       if (Keyboard[def->left])\r
+                               mx = motion_Left;\r
+                       else if (Keyboard[def->right])\r
+                               mx = motion_Right;\r
+\r
+                       if (Keyboard[def->button0])\r
+                               buttons += 1 << 0;\r
+                       if (Keyboard[def->button1])\r
+                               buttons += 1 << 1;\r
+                       realdelta = false;\r
+                       if (mx || my || buttons)\r
+                               ControlTypeUsed = ctrl_Keyboard;\r
+               }                                                                                                       // MDM end (GAMERS EDGE)\r
+       }\r
+\r
+       if (realdelta)\r
+       {\r
+               mx = (dx < 0)? motion_Left : ((dx > 0)? motion_Right : motion_None);\r
+               my = (dy < 0)? motion_Up : ((dy > 0)? motion_Down : motion_None);\r
+       }\r
+       else\r
+       {\r
+               dx = mx * 127;\r
+               dy = my * 127;\r
+       }\r
+\r
+       info->x = dx;\r
+       info->xaxis = mx;\r
+       info->y = dy;\r
+       info->yaxis = my;\r
+       info->button0 = buttons & (1 << 0);\r
+       info->button1 = buttons & (1 << 1);\r
+       info->dir = DirTable[((my + 1) * 3) + (mx + 1)];\r
+\r
+#if 0\r
+       if (DemoMode == demo_Record)\r
+       {\r
+               // Pack the control info into a byte\r
+               dbyte = (buttons << 4) | ((mx + 1) << 2) | (my + 1);\r
+\r
+               if\r
+               (\r
+                       (DemoBuffer[DemoOffset + 1] == dbyte)\r
+               &&      (DemoBuffer[DemoOffset] < 255)\r
+               )\r
+                       (DemoBuffer[DemoOffset])++;\r
+               else\r
+               {\r
+                       if (DemoOffset || DemoBuffer[DemoOffset])\r
+                               DemoOffset += 2;\r
+\r
+                       if (DemoOffset >= DemoSize)\r
+                               Quit("Demo buffer overflow");\r
+\r
+                       DemoBuffer[DemoOffset] = 1;\r
+                       DemoBuffer[DemoOffset + 1] = dbyte;\r
+               }\r
+       }\r
+#endif\r
+}\r
+\r
+#if 0\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_ReadControl() - Reads the device associated with the specified\r
+//             player and fills in the control info struct\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_ReadControl(int player,ControlInfo *info)\r
+{\r
+                       boolean         realdelta;\r
+                       byte            dbyte;\r
+                       word            buttons;\r
+                       int                     dx,dy;\r
+                       Motion          mx,my;\r
+                       ControlType     type;\r
+register       KeyboardDef     *def;\r
+\r
+       dx = dy = 0;\r
+       mx = my = motion_None;\r
+       buttons = 0;\r
+\r
+#if 0\r
+       if (DemoMode == demo_Playback)\r
+       {\r
+               dbyte = DemoBuffer[DemoOffset + 1];\r
+               my = (dbyte & 3) - 1;\r
+               mx = ((dbyte >> 2) & 3) - 1;\r
+               buttons = (dbyte >> 4) & 3;\r
+\r
+               if (!(--DemoBuffer[DemoOffset]))\r
+               {\r
+                       DemoOffset += 2;\r
+                       if (DemoOffset >= DemoSize)\r
+                               DemoMode = demo_PlayDone;\r
+               }\r
+\r
+               realdelta = false;\r
+       }\r
+       else if (DemoMode == demo_PlayDone)\r
+               Quit("Demo playback exceeded");\r
+       else\r
+#endif\r
+       {\r
+               switch (type = Controls[player])\r
+               {\r
+               case ctrl_Keyboard1:\r
+               case ctrl_Keyboard2:\r
+                       def = &KbdDefs[type - ctrl_Keyboard];\r
+\r
+                       if (Keyboard[def->upleft])\r
+                               mx = motion_Left,my = motion_Up;\r
+                       else if (Keyboard[def->upright])\r
+                               mx = motion_Right,my = motion_Up;\r
+                       else if (Keyboard[def->downleft])\r
+                               mx = motion_Left,my = motion_Down;\r
+                       else if (Keyboard[def->downright])\r
+                               mx = motion_Right,my = motion_Down;\r
+\r
+                       if (Keyboard[def->up])\r
+                               my = motion_Up;\r
+                       else if (Keyboard[def->down])\r
+                               my = motion_Down;\r
+\r
+                       if (Keyboard[def->left])\r
+                               mx = motion_Left;\r
+                       else if (Keyboard[def->right])\r
+                               mx = motion_Right;\r
+\r
+                       if (Keyboard[def->button0])\r
+                               buttons += 1 << 0;\r
+                       if (Keyboard[def->button1])\r
+                               buttons += 1 << 1;\r
+                       realdelta = false;\r
+                       break;\r
+               case ctrl_Joystick1:\r
+               case ctrl_Joystick2:\r
+                       INL_GetJoyDelta(type - ctrl_Joystick,&dx,&dy,false);\r
+                       buttons = INL_GetJoyButtons(type - ctrl_Joystick);\r
+                       realdelta = true;\r
+                       break;\r
+               case ctrl_Mouse:\r
+                       INL_GetMouseDelta(&dx,&dy);\r
+                       buttons = INL_GetMouseButtons();\r
+                       realdelta = true;\r
+                       break;\r
+               }\r
+       }\r
+\r
+       if (realdelta)\r
+       {\r
+               mx = (dx < 0)? motion_Left : ((dx > 0)? motion_Right : motion_None);\r
+               my = (dy < 0)? motion_Up : ((dy > 0)? motion_Down : motion_None);\r
+       }\r
+       else\r
+       {\r
+               dx = mx * 127;\r
+               dy = my * 127;\r
+       }\r
+\r
+       info->x = dx;\r
+       info->xaxis = mx;\r
+       info->y = dy;\r
+       info->yaxis = my;\r
+       info->button0 = buttons & (1 << 0);\r
+       info->button1 = buttons & (1 << 1);\r
+       info->dir = DirTable[((my + 1) * 3) + (mx + 1)];\r
+\r
+#if 0\r
+       if (DemoMode == demo_Record)\r
+       {\r
+               // Pack the control info into a byte\r
+               dbyte = (buttons << 4) | ((mx + 1) << 2) | (my + 1);\r
+\r
+               if\r
+               (\r
+                       (DemoBuffer[DemoOffset + 1] == dbyte)\r
+               &&      (DemoBuffer[DemoOffset] < 255)\r
+               )\r
+                       (DemoBuffer[DemoOffset])++;\r
+               else\r
+               {\r
+                       if (DemoOffset || DemoBuffer[DemoOffset])\r
+                               DemoOffset += 2;\r
+\r
+                       if (DemoOffset >= DemoSize)\r
+                               Quit("Demo buffer overflow");\r
+\r
+                       DemoBuffer[DemoOffset] = 1;\r
+                       DemoBuffer[DemoOffset + 1] = dbyte;\r
+               }\r
+       }\r
+#endif\r
+}\r
+#endif\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_SetControlType() - Sets the control type to be used by the specified\r
+//             player\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_SetControlType(int player,ControlType type)\r
+{\r
+       // DEBUG - check that requested type is present?\r
+       Controls[player] = type;\r
+}\r
+\r
+#if 0\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_StartDemoRecord() - Starts the demo recording, using a buffer the\r
+//             size passed. Returns if the buffer allocation was successful\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+IN_StartDemoRecord(word bufsize)\r
+{\r
+       if (!bufsize)\r
+               return(false);\r
+\r
+       MM_GetPtr((memptr *)&DemoBuffer,bufsize);\r
+       DemoMode = demo_Record;\r
+       DemoSize = bufsize & ~1;\r
+       DemoOffset = 0;\r
+       DemoBuffer[0] = DemoBuffer[1] = 0;\r
+\r
+       return(true);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_StartDemoPlayback() - Plays back the demo pointed to of the given size\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_StartDemoPlayback(byte /*_1seg*/ *buffer,word bufsize)\r
+{\r
+       DemoBuffer = buffer;\r
+       DemoMode = demo_Playback;\r
+       DemoSize = bufsize & ~1;\r
+       DemoOffset = 0;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_StopDemo() - Turns off demo mode\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_StopDemo(void)\r
+{\r
+       if ((DemoMode == demo_Record) && DemoOffset)\r
+               DemoOffset += 2;\r
+\r
+       DemoMode = demo_Off;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_FreeDemoBuffer() - Frees the demo buffer, if it's been allocated\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_FreeDemoBuffer(void)\r
+{\r
+       if (DemoBuffer)\r
+               MM_FreePtr((memptr *)&DemoBuffer);\r
+}\r
+#endif\r
+\r
+\r
+#if 0\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_GetScanName() - Returns a string containing the name of the\r
+//             specified scan code\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+byte *\r
+IN_GetScanName(ScanCode scan)\r
+{\r
+       byte            **p;\r
+       ScanCode        far *s;\r
+\r
+       for (s = ExtScanCodes,p = ExtScanNames;*s;p++,s++)\r
+               if (*s == scan)\r
+                       return(*p);\r
+\r
+       return(ScanNames[scan]);\r
+}\r
+#endif\r
+\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_WaitForKey() - Waits for a scan code, then clears LastScan and\r
+//             returns the scan code\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+ScanCode\r
+IN_WaitForKey(void)\r
+{\r
+       ScanCode        result;\r
+\r
+       while (!(result = LastScan))\r
+               ;\r
+       LastScan = 0;\r
+       return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_WaitForASCII() - Waits for an ASCII char, then clears LastASCII and\r
+//             returns the ASCII value\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+char\r
+IN_WaitForASCII(void)\r
+{\r
+       char            result;\r
+\r
+       while (!(result = LastASCII))\r
+               ;\r
+       LastASCII = '\0';\r
+       return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_AckBack() - Waits for either an ASCII keypress or a button press\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_AckBack(void)\r
+{\r
+       word    i;\r
+\r
+       while (!LastScan)\r
+       {\r
+               if (MousePresent)\r
+               {\r
+                       if (INL_GetMouseButtons())\r
+                       {\r
+                               while (INL_GetMouseButtons())\r
+                                       ;\r
+                               return;\r
+                       }\r
+               }\r
+\r
+               for (i = 0;i < MaxJoys;i++)\r
+               {\r
+                       if (JoysPresent[i])\r
+                       {\r
+                               if (IN_GetJoyButtonsDB(i))\r
+                               {\r
+                                       while (IN_GetJoyButtonsDB(i))\r
+                                               ;\r
+                                       return;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       IN_ClearKey(LastScan);\r
+       LastScan = sc_None;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_Ack() - Clears user input & then calls IN_AckBack()\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_Ack(void)\r
+{\r
+       word    i;\r
+\r
+       IN_ClearKey(LastScan);\r
+       LastScan = sc_None;\r
+\r
+       if (MousePresent)\r
+               while (INL_GetMouseButtons())\r
+                                       ;\r
+       for (i = 0;i < MaxJoys;i++)\r
+               if (JoysPresent[i])\r
+                       while (IN_GetJoyButtonsDB(i))\r
+                               ;\r
+\r
+       IN_AckBack();\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_IsUserInput() - Returns true if a key has been pressed or a button\r
+//             is down\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+IN_IsUserInput(void)\r
+{\r
+       boolean result;\r
+       word    i;\r
+\r
+       result = LastScan;\r
+\r
+       if (MousePresent)\r
+               if (INL_GetMouseButtons())\r
+                       result = true;\r
+\r
+       for (i = 0;i < MaxJoys;i++)\r
+               if (JoysPresent[i])\r
+                       if (INL_GetJoyButtons(i))\r
+                               result = true;\r
+\r
+       return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_UserInput() - Waits for the specified delay time (in ticks) or the\r
+//             user pressing a key or a mouse button. If the clear flag is set, it\r
+//             then either clears the key or waits for the user to let the mouse\r
+//             button up.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+IN_UserInput(dword delay,boolean clear)\r
+{\r
+       dword   lasttime;\r
+\r
+       lasttime = TimeCount;\r
+       do\r
+       {\r
+               if (IN_IsUserInput())\r
+               {\r
+                       if (clear)\r
+                               IN_AckBack();\r
+                       return(true);\r
+               }\r
+       } while (TimeCount - lasttime < delay);\r
+       return(false);\r
+}\r
diff --git a/16/cawat/16_in.h b/16/cawat/16_in.h
new file mode 100644 (file)
index 0000000..ba1e138
--- /dev/null
@@ -0,0 +1,214 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+//     ID Engine\r
+//     ID_IN.h - Header file for Input Manager\r
+//     v1.0d1\r
+//     By Jason Blochowiak\r
+//\r
+
+#include "lib_head.h"
+\r
+#ifndef        __TYPES__\r
+//#include "ID_Types.h"\r
+#endif\r
+\r
+#ifndef        __ID_IN__\r
+#define        __ID_IN__\r
+\r
+#ifdef __DEBUG__\r
+#define        __DEBUG_InputMgr__\r
+#endif\r
+\r
+#define        MaxPlayers      4\r
+#define        MaxKbds         2\r
+#define        MaxJoys         2\r
+#define        NumCodes        128\r
+\r
+typedef        byte            ScanCode;\r
+#define        sc_None                 0\r
+#define        sc_Bad                  0xff\r
+#define        sc_Return               0x1c\r
+#define        sc_Enter                sc_Return\r
+#define        sc_Escape               0x01\r
+#define        sc_Space                0x39\r
+#define        sc_BackSpace    0x0e\r
+#define        sc_Tab                  0x0f\r
+#define        sc_Alt                  0x38\r
+#define        sc_Control              0x1d\r
+#define        sc_CapsLock             0x3a\r
+#define        sc_LShift               0x2a\r
+#define        sc_RShift               0x36\r
+#define        sc_UpArrow              0x48\r
+#define        sc_DownArrow    0x50\r
+#define        sc_LeftArrow    0x4b\r
+#define        sc_RightArrow   0x4d\r
+#define        sc_Insert               0x52\r
+#define        sc_Delete               0x53\r
+#define        sc_Home                 0x47\r
+#define        sc_End                  0x4f\r
+#define        sc_PgUp                 0x49\r
+#define        sc_PgDn                 0x51\r
+#define        sc_F1                   0x3b\r
+#define        sc_F2                   0x3c\r
+#define        sc_F3                   0x3d\r
+#define        sc_F4                   0x3e\r
+#define        sc_F5                   0x3f\r
+#define        sc_F6                   0x40\r
+#define        sc_F7                   0x41\r
+#define        sc_F8                   0x42\r
+#define        sc_F9                   0x43\r
+#define        sc_F10                  0x44\r
+#define        sc_F11                  0x57\r
+#define        sc_F12                  0x59\r
+\r
+#define        sc_A                    0x1e\r
+#define        sc_B                    0x30\r
+#define        sc_C                    0x2e\r
+#define        sc_D                    0x20\r
+#define        sc_E                    0x12\r
+#define        sc_F                    0x21\r
+#define        sc_G                    0x22\r
+#define        sc_H                    0x23\r
+#define        sc_I                    0x17\r
+#define        sc_J                    0x24\r
+#define        sc_K                    0x25\r
+#define        sc_L                    0x26\r
+#define        sc_M                    0x32\r
+#define        sc_N                    0x31\r
+#define        sc_O                    0x18\r
+#define        sc_P                    0x19\r
+#define        sc_Q                    0x10\r
+#define        sc_R                    0x13\r
+#define        sc_S                    0x1f\r
+#define        sc_T                    0x14\r
+#define        sc_U                    0x16\r
+#define        sc_V                    0x2f\r
+#define        sc_W                    0x11\r
+#define        sc_X                    0x2d\r
+#define        sc_Y                    0x15\r
+#define        sc_Z                    0x2c\r
+\r
+#define        key_None                0\r
+#define        key_Return              0x0d\r
+#define        key_Enter               key_Return\r
+#define        key_Escape              0x1b\r
+#define        key_Space               0x20\r
+#define        key_BackSpace   0x08\r
+#define        key_Tab                 0x09\r
+#define        key_Delete              0x7f\r
+\r
+//     Stuff for the mouse\r
+#define        MReset          0\r
+#define        MButtons        3\r
+#define        MDelta          11\r
+\r
+#define        MouseInt        0x33\r
+#define        Mouse(x)        _AX = x,_dos_geninterrupt(MouseInt)\r
+\r
+typedef        enum            {\r
+                                               demo_Off,demo_Record,demo_Playback,demo_PlayDone\r
+                                       } Demo;\r
+typedef        enum            {\r
+                                               ctrl_None,                              // MDM (GAMERS EDGE) - added\r
+                                               ctrl_Keyboard,\r
+                                                       ctrl_Keyboard1 = ctrl_Keyboard,ctrl_Keyboard2,\r
+                                               ctrl_Joystick,\r
+                                                       ctrl_Joystick1 = ctrl_Joystick,ctrl_Joystick2,\r
+                                               ctrl_Mouse\r
+                                       } ControlType;\r
+typedef        enum            {\r
+                                               motion_Left = -1,motion_Up = -1,\r
+                                               motion_None = 0,\r
+                                               motion_Right = 1,motion_Down = 1\r
+                                       } Motion;\r
+typedef        enum            {\r
+                                               dir_North,dir_NorthEast,\r
+                                               dir_East,dir_SouthEast,\r
+                                               dir_South,dir_SouthWest,\r
+                                               dir_West,dir_NorthWest,\r
+                                               dir_None\r
+                                       } Direction;\r
+typedef        struct          {\r
+                                               boolean         button0,button1;\r
+                                               int                     x,y;\r
+                                               Motion          xaxis,yaxis;\r
+                                               Direction       dir;\r
+                                       } CursorInfo;\r
+typedef        CursorInfo      ControlInfo;\r
+typedef        struct          {\r
+                                               ScanCode        button0,button1,\r
+                                                                       upleft,         up,             upright,\r
+                                                                       left,                           right,\r
+                                                                       downleft,       down,   downright;\r
+                                       } KeyboardDef;\r
+typedef        struct          {\r
+                                               word            joyMinX,joyMinY,\r
+                                                                       threshMinX,threshMinY,\r
+                                                                       threshMaxX,threshMaxY,\r
+                                                                       joyMaxX,joyMaxY,\r
+                                                                       joyMultXL,joyMultYL,\r
+                                                                       joyMultXH,joyMultYH;\r
+                                       } JoystickDef;\r
+// Global variables\r
+extern boolean         Keyboard[],\r
+                                       MousePresent,\r
+                                       JoysPresent[];\r
+extern boolean         Paused;\r
+extern char            LastASCII;\r
+extern ScanCode        LastScan;\r
+extern KeyboardDef     KbdDefs[];\r
+extern JoystickDef     JoyDefs[];\r
+extern ControlType     Controls[MaxPlayers];\r
+\r
+extern boolean JoystickCalibrated;                             // MDM (GAMERS EDGE) - added\r
+extern ControlType ControlTypeUsed;                            // MDM (GAMERS EDGE) - added\r
+\r
+extern Demo            DemoMode;\r
+extern byte /*_seg*/   *DemoBuffer;\r
+extern word            DemoOffset,DemoSize;\r
+\r
+// Function prototypes\r
+#define        IN_KeyDown(code)        (Keyboard[(code)])\r
+#define        IN_ClearKey(code)       {Keyboard[code] = false;\\r
+                                                       if (code == LastScan) LastScan = sc_None;}\r
+\r
+// DEBUG - put names in prototypes\r
+extern void            IN_Startup(void),IN_Shutdown(void),\r
+                                       IN_Default(boolean gotit,ControlType in),\r
+                                       IN_SetKeyHook(void (*)()),\r
+                                       IN_ClearKeysDown(void),\r
+                                       IN_ReadCursor(CursorInfo *),\r
+                                       IN_ReadControl(int,ControlInfo *),\r
+                                       IN_SetControlType(int,ControlType),\r
+                                       IN_GetJoyAbs(word joy,word *xp,word *yp),\r
+                                       IN_SetupJoy(word joy,word minx,word maxx,\r
+                                                               word miny,word maxy),\r
+                                       IN_StartDemoPlayback(byte /*_seg*/ *buffer,word bufsize),\r
+                                       IN_StopDemo(void),IN_FreeDemoBuffer(void),\r
+                                       IN_Ack(void),IN_AckBack(void);\r
+extern boolean         IN_UserInput(dword delay,boolean clear),\r
+                                       IN_IsUserInput(void),\r
+                                       IN_StartDemoRecord(word bufsize);\r
+extern byte            *IN_GetScanName(ScanCode);\r
+extern char            IN_WaitForASCII(void);\r
+extern ScanCode        IN_WaitForKey(void);\r
+extern word            IN_GetJoyButtonsDB(word joy);\r
+\r
+#endif\r
diff --git a/16/cawat/16_mm.c b/16/cawat/16_mm.c
new file mode 100644 (file)
index 0000000..95e394b
--- /dev/null
@@ -0,0 +1,1116 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// NEWMM.C\r
+\r
+/*\r
+=============================================================================\r
+\r
+                       ID software memory manager\r
+                       --------------------------\r
+\r
+Primary coder: John Carmack\r
+\r
+RELIES ON\r
+---------\r
+Quit (char *error) function\r
+\r
+\r
+WORK TO DO\r
+----------\r
+MM_SizePtr to change the size of a given pointer\r
+\r
+Multiple purge levels utilized\r
+\r
+EMS / XMS unmanaged routines\r
+\r
+=============================================================================\r
+*/\r
+\r
+//#include "LIB_HEAD.H"
+#include "16_mm.h"
+\r
+/*\r
+=============================================================================\r
+\r
+                                                       LOCAL INFO\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define LOCKBIT                0x80    // if set in attributes, block cannot be moved\r
+#define PURGEBITS      3               // 0-3 level, 0= unpurgable, 3= purge first\r
+#define PURGEMASK      0xfffc\r
+#define BASEATTRIBUTES 0       // unlocked, non purgable\r
+\r
+#define MAXUMBS                10\r
+\r
+typedef struct mmblockstruct\r
+{\r
+       unsigned        start,length;\r
+       unsigned        attributes;\r
+       memptr          *useptr;        // pointer to the segment start\r
+       struct mmblockstruct far *next;\r
+} mmblocktype;\r
+\r
+\r
+//#define GETNEWBLOCK {if(!(mmnew=mmfree))Quit("MM_GETNEWBLOCK: No free blocks!");mmfree=mmfree->next;}\r
+#define GETNEWBLOCK {if(!mmfree)MML_ClearBlock();mmnew=mmfree;mmfree=mmfree->next;}\r
+#define FREEBLOCK(x) {*x->useptr=NULL;x->next=mmfree;mmfree=x;}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+mminfotype     mminfo;\r
+memptr         bufferseg;\r
+boolean                mmerror;\r
+\r
+void           (* beforesort) (void);\r
+void           (* aftersort) (void);\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+boolean                mmstarted;\r
+
+void huge      *hugeheap;\r
+void far       *farheap;\r
+void           *nearheap;\r
+\r
+mmblocktype    far mmblocks[MAXBLOCKS]\r
+                       ,far *mmhead,far *mmfree,far *mmrover,far *mmnew;\r
+\r
+boolean                bombonerror;\r
+\r
+unsigned       totalEMSpages,freeEMSpages,EMSpageframe,EMSpagesmapped,EMShandle;\r
+\r
+void           (* XMSaddr) (void);             // far pointer to XMS driver\r
+\r
+unsigned       numUMBs,UMBbase[MAXUMBS];\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MML_CheckForEMS\r
+=\r
+= Routine from p36 of Extending DOS\r
+=\r
+=======================\r
+*/\r
+\r
+boolean MML_CheckForEMS (void)\r
+{
+       boolean emmcfems;
+       char    emmname[] = "EMMXXXX0";
+
+       __asm {
+               mov     dx,OFF emmname\r
+               mov     ax,0x3d00\r
+               int     0x21            // try to open EMMXXXX0 device\r
+               jc      error\r
+\r
+               mov     bx,ax\r
+               mov     ax,0x4400\r
+\r
+               int     0x21            // get device info\r
+               jc      error\r
+\r
+               and     dx,0x80\r
+               jz      error\r
+\r
+               mov     ax,0x4407\r
+\r
+               int     0x21            // get status\r
+               jc      error\r
+               or      al,al\r
+               jz      error\r
+\r
+               mov     ah,0x3e\r
+               int     0x21            // close handle\r
+               jc      error
+               //\r
+               // EMS is good\r
+               //
+               mov     emmcfems,1
+               jmp End
+               error:
+               //\r
+               // EMS is bad\r
+               //\r
+               mov     emmcfems,0
+               End:
+       }
+       return(emmcfems);\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MML_SetupEMS\r
+=\r
+=======================\r
+*/\r
+\r
+void MML_SetupEMS (void)\r
+{\r
+       char    str[80],str2[10];\r
+       unsigned        error;\r
+\r
+       totalEMSpages = freeEMSpages = EMSpageframe = EMSpagesmapped = 0;\r
+\r
+       __asm {\r
+               mov     ah,EMS_STATUS\r
+               int     EMS_INT                                         // make sure EMS hardware is present\r
+               or      ah,ah\r
+               jnz     error\r
+\r
+               mov     ah,EMS_VERSION\r
+               int     EMS_INT\r
+               or      ah,ah\r
+               jnz     error\r
+               cmp     al,0x32                                         // only work on ems 3.2 or greater\r
+               jb      error\r
+\r
+               mov     ah,EMS_GETFRAME\r
+               int     EMS_INT                                         // find the page frame address\r
+               or      ah,ah\r
+               jnz     error\r
+               mov     [EMSpageframe],bx\r
+\r
+               mov     ah,EMS_GETPAGES\r
+               int     EMS_INT                                         // find out how much EMS is there\r
+               or      ah,ah\r
+               jnz     error\r
+               mov     [totalEMSpages],dx\r
+               mov     [freeEMSpages],bx\r
+               or      bx,bx\r
+               jz      noEMS                                           // no EMS at all to allocate\r
+\r
+               cmp     bx,4\r
+               jle     getpages                                        // there is only 1,2,3,or 4 pages\r
+               mov     bx,4                                            // we can't use more than 4 pages\r
+       }\r
+\r
+getpages:\r
+asm {\r
+       mov     [EMSpagesmapped],bx\r
+       mov     ah,EMS_ALLOCPAGES                       // allocate up to 64k of EMS\r
+       int     EMS_INT\r
+       or      ah,ah\r
+       jnz     error\r
+       mov     [EMShandle],dx\r
+       }\r
+       return;\r
+\r
+error:\r
+       error = _AH;\r
+       strcpy (str,"MML_SetupEMS: EMS error 0x");\r
+       itoa(error,str2,16);\r
+       strcpy (str,str2);\r
+       Quit (str);\r
+\r
+noEMS:\r
+;\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MML_ShutdownEMS\r
+=\r
+=======================\r
+*/\r
+\r
+void MML_ShutdownEMS (void)\r
+{\r
+       if (!EMShandle)\r
+               return;\r
+\r
+               {\r
+       mov     ah,EMS_FREEPAGES\r
+       mov     dx,[EMShandle]\r
+       int     EMS_INT\r
+       or      ah,ah\r
+       jz      ok\r
+       }\r
+\r
+       Quit ("MML_ShutdownEMS: Error freeing EMS!");\r
+\r
+ok:\r
+;\r
+}\r
+\r
+/*\r
+====================\r
+=\r
+= MM_MapEMS\r
+=\r
+= Maps the 64k of EMS used by memory manager into the page frame\r
+= for general use.  This only needs to be called if you are keeping\r
+= other things in EMS.\r
+=\r
+====================\r
+*/\r
+\r
+void MM_MapEMS (void)\r
+{\r
+       char    str[80],str2[10];\r
+       unsigned        error;\r
+       int     i;\r
+\r
+       for (i=0;i<EMSpagesmapped;i++)\r
+       {\r
+                       {\r
+               mov     ah,EMS_MAPPAGE\r
+               mov     bx,[i]                  // logical page\r
+               mov     al,bl                   // physical page\r
+               mov     dx,[EMShandle]  // handle\r
+               int     EMS_INT\r
+               or      ah,ah\r
+               jnz     error\r
+               }\r
+       }\r
+\r
+       return;\r
+\r
+error:\r
+       error = _AH;\r
+       strcpy (str,"MM_MapEMS: EMS error 0x");\r
+       itoa(error,str2,16);\r
+       strcpy (str,str2);\r
+       Quit (str);\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= MML_CheckForXMS\r
+=\r
+= Check for XMM driver\r
+=\r
+=======================\r
+*/\r
+\r
+boolean MML_CheckForXMS (void)\r
+{\r
+       numUMBs = 0;\r
+\r
+asm {\r
+       mov     ax,0x4300\r
+       int     0x2f                            // query status of installed diver\r
+       cmp     al,0x80\r
+       je      good\r
+       }\r
+       return false;\r
+good:\r
+       return true;\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MML_SetupXMS\r
+=\r
+= Try to allocate all upper memory block\r
+=\r
+=======================\r
+*/\r
+\r
+void MML_SetupXMS (void)\r
+{\r
+       unsigned        base,size;\r
+\r
+               {\r
+       mov     ax,0x4310\r
+       int     0x2f\r
+       mov     [WORD PTR XMSaddr],bx\r
+       mov     [WORD PTR XMSaddr+2],es         // function pointer to XMS driver\r
+       }\r
+\r
+getmemory:\r
+               {\r
+       mov     ah,XMS_ALLOCUMB\r
+       mov     dx,0xffff                                       // try for largest block possible\r
+       call    [DWORD PTR XMSaddr]\r
+       or      ax,ax\r
+       jnz     gotone\r
+\r
+       cmp     bl,0xb0                                         // error: smaller UMB is available\r
+       jne     done;\r
+\r
+       mov     ah,XMS_ALLOCUMB\r
+       call    [DWORD PTR XMSaddr]             // DX holds largest available UMB\r
+       or      ax,ax\r
+       jz      done                                            // another error...\r
+       }\r
+\r
+gotone:\r
+               {\r
+       mov     [base],bx\r
+       mov     [size],dx\r
+       }\r
+       MML_UseSpace (base,size);\r
+       mminfo.XMSmem += size*16;\r
+       UMBbase[numUMBs] = base;\r
+       numUMBs++;\r
+       if (numUMBs < MAXUMBS)\r
+               goto getmemory;\r
+\r
+done:;\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MML_ShutdownXMS\r
+=\r
+======================\r
+*/\r
+\r
+void MML_ShutdownXMS (void)\r
+{\r
+       int     i;\r
+       unsigned        base;\r
+\r
+       for (i=0;i<numUMBs;i++)\r
+       {\r
+               base = UMBbase[i];\r
+\r
+               mov     ah,XMS_FREEUMB\r
+               mov     dx,[base]\r
+               call    [DWORD PTR XMSaddr]\r
+       }\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= MML_UseSpace\r
+=\r
+= Marks a range of paragraphs as usable by the memory manager\r
+= This is used to mark space for the near heap, far heap, ems page frame,\r
+= and upper memory blocks\r
+=\r
+======================\r
+*/\r
+\r
+void MML_UseSpace (unsigned segstart, unsigned seglength)\r
+{\r
+       mmblocktype far *scan,far *last;\r
+       unsigned        oldend;\r
+       long            extra;\r
+\r
+       scan = last = mmhead;\r
+       mmrover = mmhead;               // reset rover to start of memory\r
+\r
+//\r
+// search for the block that contains the range of segments\r
+//\r
+       while (scan->start+scan->length < segstart)\r
+       {\r
+               last = scan;\r
+               scan = scan->next;\r
+       }\r
+\r
+//\r
+// take the given range out of the block\r
+//\r
+       oldend = scan->start + scan->length;\r
+       extra = oldend - (segstart+seglength);\r
+       if (extra < 0)\r
+               Quit ("MML_UseSpace: Segment spans two blocks!");\r
+\r
+       if (segstart == scan->start)\r
+       {\r
+               last->next = scan->next;                        // unlink block\r
+               FREEBLOCK(scan);\r
+               scan = last;\r
+       }\r
+       else\r
+               scan->length = segstart-scan->start;    // shorten block\r
+\r
+       if (extra > 0)\r
+       {\r
+               GETNEWBLOCK;\r
+               mmnew->next = scan->next;\r
+               scan->next = mmnew;\r
+               mmnew->start = segstart+seglength;\r
+               mmnew->length = extra;\r
+               mmnew->attributes = LOCKBIT;\r
+       }\r
+\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= MML_ClearBlock\r
+=\r
+= We are out of blocks, so free a purgable block\r
+=\r
+====================\r
+*/\r
+\r
+void MML_ClearBlock (void)\r
+{\r
+       mmblocktype far *scan,far *last;\r
+\r
+       scan = mmhead->next;\r
+\r
+       while (scan)\r
+       {\r
+               if (!(scan->attributes&LOCKBIT) && (scan->attributes&PURGEBITS) )\r
+               {\r
+                       MM_FreePtr(scan->useptr);\r
+                       return;\r
+               }\r
+               scan = scan->next;\r
+       }\r
+\r
+       Quit ("MM_ClearBlock: No purgable blocks!");\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+/*\r
+===================\r
+=\r
+= MM_Startup\r
+=\r
+= Grabs all space from turbo with malloc/farmalloc\r
+= Allocates bufferseg misc buffer\r
+=\r
+===================\r
+*/\r
+\r
+static char *ParmStrings[] = {"noems","noxms",""};\r
+\r
+void MM_Startup (void)\r
+{\r
+       int i;\r
+       unsigned        long length;\r
+       void far        *start;\r
+       unsigned        segstart,seglength,endfree;\r
+\r
+       if (mmstarted)\r
+               MM_Shutdown ();\r
+\r
+\r
+       mmstarted = true;\r
+       bombonerror = true;\r
+//\r
+// set up the linked list (everything in the free list;\r
+//\r
+       mmhead = NULL;\r
+       mmfree = &mmblocks[0];\r
+       for (i=0;i<MAXBLOCKS-1;i++)\r
+               mmblocks[i].next = &mmblocks[i+1];\r
+       mmblocks[i].next = NULL;\r
+\r
+//\r
+// locked block of all memory until we punch out free space\r
+//\r
+       GETNEWBLOCK;\r
+       mmhead = mmnew;                         // this will allways be the first node\r
+       mmnew->start = 0;\r
+       mmnew->length = 0xffff;\r
+       mmnew->attributes = LOCKBIT;\r
+       mmnew->next = NULL;\r
+       mmrover = mmhead;\r
+\r
+\r
+//\r
+// get all available near conventional memory segments\r
+//\r
+       length=coreleft();\r
+       start = (void far *)(nearheap = malloc(length));\r
+\r
+       length -= 16-(FP_OFF(start)&15);\r
+       length -= SAVENEARHEAP;\r
+       seglength = length / 16;                        // now in paragraphs\r
+       segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;\r
+       MML_UseSpace (segstart,seglength);\r
+       mminfo.nearheap = length;\r
+\r
+//\r
+// get all available far conventional memory segments\r
+//\r
+       length=farcoreleft();\r
+       start = farheap = farmalloc(length);\r
+       length -= 16-(FP_OFF(start)&15);\r
+       length -= SAVEFARHEAP;\r
+       seglength = length / 16;                        // now in paragraphs\r
+       segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;\r
+       MML_UseSpace (segstart,seglength);\r
+       mminfo.farheap = length;\r
+       mminfo.mainmem = mminfo.nearheap + mminfo.farheap;\r
+\r
+\r
+//\r
+// detect EMS and allocate up to 64K at page frame\r
+//\r
+       mminfo.EMSmem = 0;\r
+       for (i = 1;i < _argc;i++)\r
+       {\r
+               if ( US_CheckParm(_argv[i],ParmStrings) == 0)\r
+                       goto emsskip;                           // param NOEMS\r
+       }\r
+\r
+       if (MML_CheckForEMS())\r
+       {\r
+               MML_SetupEMS();                                 // allocate space\r
+               MML_UseSpace (EMSpageframe,EMSpagesmapped*0x400);\r
+               MM_MapEMS();                                    // map in used pages\r
+               mminfo.EMSmem = EMSpagesmapped*0x4000l;\r
+       }\r
+\r
+//\r
+// detect XMS and get upper memory blocks\r
+//\r
+emsskip:\r
+       mminfo.XMSmem = 0;\r
+       for (i = 1;i < _argc;i++)\r
+       {\r
+               if ( US_CheckParm(_argv[i],ParmStrings) == 0)\r
+                       goto xmsskip;                           // param NOXMS\r
+       }\r
+\r
+       if (MML_CheckForXMS())\r
+               MML_SetupXMS();                                 // allocate as many UMBs as possible\r
+\r
+//\r
+// allocate the misc buffer\r
+//\r
+xmsskip:\r
+       mmrover = mmhead;               // start looking for space after low block\r
+\r
+       MM_GetPtr (&bufferseg,BUFFERSIZE);\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= MM_Shutdown\r
+=\r
+= Frees all conventional, EMS, and XMS allocated\r
+=\r
+====================\r
+*/\r
+\r
+void MM_Shutdown (void)\r
+{\r
+  if (!mmstarted)\r
+       return;\r
+\r
+  farfree (farheap);\r
+  free (nearheap);\r
+  MML_ShutdownEMS ();\r
+  MML_ShutdownXMS ();\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= MM_GetPtr\r
+=\r
+= Allocates an unlocked, unpurgable block\r
+=\r
+====================\r
+*/\r
+\r
+void MM_GetPtr (memptr *baseptr,unsigned long size)\r
+{\r
+       mmblocktype far *scan,far *lastscan,far *endscan\r
+                               ,far *purge,far *next;\r
+       int                     search;\r
+       unsigned        needed,startseg;\r
+\r
+       needed = (size+15)/16;          // convert size from bytes to paragraphs\r
+\r
+       GETNEWBLOCK;                            // fill in start and next after a spot is found\r
+       mmnew->length = needed;\r
+       mmnew->useptr = baseptr;\r
+       mmnew->attributes = BASEATTRIBUTES;\r
+\r
+       for (search = 0; search<3; search++)\r
+       {\r
+       //\r
+       // first search:        try to allocate right after the rover, then on up\r
+       // second search:       search from the head pointer up to the rover\r
+       // third search:        compress memory, then scan from start\r
+               if (search == 1 && mmrover == mmhead)\r
+                       search++;\r
+\r
+               switch (search)\r
+               {\r
+               case 0:\r
+                       lastscan = mmrover;\r
+                       scan = mmrover->next;\r
+                       endscan = NULL;\r
+                       break;\r
+               case 1:\r
+                       lastscan = mmhead;\r
+                       scan = mmhead->next;\r
+                       endscan = mmrover;\r
+                       break;\r
+               case 2:\r
+                       MM_SortMem ();\r
+                       lastscan = mmhead;\r
+                       scan = mmhead->next;\r
+                       endscan = NULL;\r
+                       break;\r
+               }\r
+\r
+               startseg = lastscan->start + lastscan->length;\r
+\r
+               while (scan != endscan)\r
+               {\r
+                       if (scan->start - startseg >= needed)\r
+                       {\r
+                       //\r
+                       // got enough space between the end of lastscan and\r
+                       // the start of scan, so throw out anything in the middle\r
+                       // and allocate the new block\r
+                       //\r
+                               purge = lastscan->next;\r
+                               lastscan->next = mmnew;\r
+                               mmnew->start = *(unsigned *)baseptr = startseg;\r
+                               mmnew->next = scan;\r
+                               while ( purge != scan)\r
+                               {       // free the purgable block\r
+                                       next = purge->next;\r
+                                       FREEBLOCK(purge);\r
+                                       purge = next;           // purge another if not at scan\r
+                               }\r
+                               mmrover = mmnew;\r
+                               return; // good allocation!\r
+                       }\r
+\r
+                       //\r
+                       // if this block is purge level zero or locked, skip past it\r
+                       //\r
+                       if ( (scan->attributes & LOCKBIT)\r
+                               || !(scan->attributes & PURGEBITS) )\r
+                       {\r
+                               lastscan = scan;\r
+                               startseg = lastscan->start + lastscan->length;\r
+                       }\r
+\r
+\r
+                       scan=scan->next;                // look at next line\r
+               }\r
+       }\r
+\r
+       if (bombonerror)\r
+               Quit (OUT_OF_MEM_MSG,(size-mminfo.nearheap));\r
+       else\r
+               mmerror = true;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= MM_FreePtr\r
+=\r
+= Allocates an unlocked, unpurgable block\r
+=\r
+====================\r
+*/\r
+\r
+void MM_FreePtr (memptr *baseptr)\r
+{\r
+       mmblocktype far *scan,far *last;\r
+\r
+       last = mmhead;\r
+       scan = last->next;\r
+\r
+       if (baseptr == mmrover->useptr) // removed the last allocated block\r
+               mmrover = mmhead;\r
+\r
+       while (scan->useptr != baseptr && scan)\r
+       {\r
+               last = scan;\r
+               scan = scan->next;\r
+       }\r
+\r
+       if (!scan)\r
+               Quit ("MM_FreePtr: Block not found!");\r
+\r
+       last->next = scan->next;\r
+\r
+       FREEBLOCK(scan);\r
+}\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= MM_SetPurge\r
+=\r
+= Sets the purge level for a block (locked blocks cannot be made purgable)\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_SetPurge (memptr *baseptr, int purge)\r
+{\r
+       mmblocktype far *start;\r
+\r
+       start = mmrover;\r
+\r
+       do\r
+       {\r
+               if (mmrover->useptr == baseptr)\r
+                       break;\r
+\r
+               mmrover = mmrover->next;\r
+\r
+               if (!mmrover)\r
+                       mmrover = mmhead;\r
+               else if (mmrover == start)\r
+                       Quit ("MM_SetPurge: Block not found!");\r
+\r
+       } while (1);\r
+\r
+       mmrover->attributes &= ~PURGEBITS;\r
+       mmrover->attributes |= purge;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= MM_SetLock\r
+=\r
+= Locks / unlocks the block\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_SetLock (memptr *baseptr, boolean locked)\r
+{\r
+       mmblocktype far *start;\r
+\r
+       start = mmrover;\r
+\r
+       do\r
+       {\r
+               if (mmrover->useptr == baseptr)\r
+                       break;\r
+\r
+               mmrover = mmrover->next;\r
+\r
+               if (!mmrover)\r
+                       mmrover = mmhead;\r
+               else if (mmrover == start)\r
+                       Quit ("MM_SetLock: Block not found!");\r
+\r
+       } while (1);\r
+\r
+       mmrover->attributes &= ~LOCKBIT;\r
+       mmrover->attributes |= locked*LOCKBIT;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= MM_SortMem\r
+=\r
+= Throws out all purgable stuff and compresses movable blocks\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_SortMem (void)\r
+{\r
+       mmblocktype far *scan,far *last,far *next;\r
+       unsigned        start,length,source,dest,oldborder;\r
+       int                     playing;\r
+\r
+       //\r
+       // lock down a currently playing sound\r
+       //\r
+       playing = SD_SoundPlaying ();\r
+       if (playing)\r
+       {\r
+               switch (SoundMode)\r
+               {\r
+               case sdm_PC:\r
+                       playing += STARTPCSOUNDS;\r
+                       break;\r
+               case sdm_AdLib:\r
+                       playing += STARTADLIBSOUNDS;\r
+                       break;\r
+               }\r
+               MM_SetLock(&(memptr)audiosegs[playing],true);\r
+       }\r
+\r
+\r
+       SD_StopSound();\r
+//     oldborder = bordercolor;\r
+//     VW_ColorBorder (15);\r
+\r
+       if (beforesort)\r
+               beforesort();\r
+\r
+       scan = mmhead;\r
+\r
+       last = NULL;            // shut up compiler warning\r
+\r
+       while (scan)\r
+       {\r
+               if (scan->attributes & LOCKBIT)\r
+               {\r
+               //\r
+               // block is locked, so try to pile later blocks right after it\r
+               //\r
+                       start = scan->start + scan->length;\r
+               }\r
+               else\r
+               {\r
+                       if (scan->attributes & PURGEBITS)\r
+                       {\r
+                       //\r
+                       // throw out the purgable block\r
+                       //\r
+                               next = scan->next;\r
+                               FREEBLOCK(scan);\r
+                               last->next = next;\r
+                               scan = next;\r
+                               continue;\r
+                       }\r
+                       else\r
+                       {\r
+                       //\r
+                       // push the non purgable block on top of the last moved block\r
+                       //\r
+                               if (scan->start != start)\r
+                               {\r
+                                       length = scan->length;\r
+                                       source = scan->start;\r
+                                       dest = start;\r
+                                       while (length > 0xf00)\r
+                                       {\r
+                                               movedata(source,0,dest,0,0xf00*16);\r
+                                               length -= 0xf00;\r
+                                               source += 0xf00;\r
+                                               dest += 0xf00;\r
+                                       }\r
+                                       movedata(source,0,dest,0,length*16);\r
+\r
+                                       scan->start = start;\r
+                                       *(unsigned *)scan->useptr = start;\r
+                               }\r
+                               start = scan->start + scan->length;\r
+                       }\r
+               }\r
+\r
+               last = scan;\r
+               scan = scan->next;              // go to next block\r
+       }\r
+\r
+       mmrover = mmhead;\r
+\r
+       if (aftersort)\r
+               aftersort();\r
+\r
+//     VW_ColorBorder (oldborder);\r
+\r
+       if (playing)\r
+               MM_SetLock(&(memptr)audiosegs[playing],false);\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+#if 0\r
+/*\r
+=====================\r
+=\r
+= MM_ShowMemory\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_ShowMemory (void)\r
+{\r
+       mmblocktype far *scan;\r
+       unsigned color,temp;\r
+       long    end,owner;\r
+       char    scratch[80],str[10];\r
+\r
+       VW_SetDefaultColors();\r
+       VW_SetLineWidth(40);\r
+       temp = bufferofs;\r
+       bufferofs = 0;\r
+       VW_SetScreen (0,0);\r
+\r
+       scan = mmhead;\r
+\r
+       end = -1;\r
+\r
+//CA_OpenDebug ();\r
+\r
+       while (scan)\r
+       {\r
+               if (scan->attributes & PURGEBITS)\r
+                       color = 5;              // dark purple = purgable\r
+               else\r
+                       color = 9;              // medium blue = non purgable\r
+               if (scan->attributes & LOCKBIT)\r
+                       color = 12;             // red = locked\r
+               if (scan->start<=end)\r
+                       Quit ("MM_ShowMemory: Memory block order currupted!");\r
+               end = scan->start+scan->length-1;\r
+               VW_Hlin(scan->start,(unsigned)end,0,color);\r
+               VW_Plot(scan->start,0,15);\r
+               if (scan->next->start > end+1)\r
+                       VW_Hlin(end+1,scan->next->start,0,0);   // black = free\r
+\r
+#if 0\r
+strcpy (scratch,"Size:");\r
+ltoa ((long)scan->length*16,str,10);\r
+strcat (scratch,str);\r
+strcat (scratch,"\tOwner:0x");\r
+owner = (unsigned)scan->useptr;\r
+ultoa (owner,str,16);\r
+strcat (scratch,str);\r
+strcat (scratch,"\n");\r
+write (debughandle,scratch,strlen(scratch));\r
+#endif\r
+\r
+               scan = scan->next;\r
+       }\r
+\r
+//CA_CloseDebug ();\r
+\r
+       IN_Ack();\r
+       VW_SetLineWidth(64);\r
+       bufferofs = temp;\r
+}\r
+#endif\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MM_UnusedMemory\r
+=\r
+= Returns the total free space without purging\r
+=\r
+======================\r
+*/\r
+\r
+long MM_UnusedMemory (void)\r
+{\r
+       unsigned free;\r
+       mmblocktype far *scan;\r
+\r
+       free = 0;\r
+       scan = mmhead;\r
+\r
+       while (scan->next)\r
+       {\r
+               free += scan->next->start - (scan->start + scan->length);\r
+               scan = scan->next;\r
+       }\r
+\r
+       return free*16l;\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MM_TotalFree\r
+=\r
+= Returns the total free space with purging\r
+=\r
+======================\r
+*/\r
+\r
+long MM_TotalFree (void)\r
+{\r
+       unsigned free;\r
+       mmblocktype far *scan;\r
+\r
+       free = 0;\r
+       scan = mmhead;\r
+\r
+       while (scan->next)\r
+       {\r
+               if ((scan->attributes&PURGEBITS) && !(scan->attributes&LOCKBIT))\r
+                       free += scan->length;\r
+               free += scan->next->start - (scan->start + scan->length);\r
+               scan = scan->next;\r
+       }\r
+\r
+       return free*16l;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= MM_BombOnError\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_BombOnError (boolean bomb)\r
+{\r
+       bombonerror = bomb;\r
+}\r
+\r
+\r
diff --git a/16/cawat/16_mm.h b/16/cawat/16_mm.h
new file mode 100644 (file)
index 0000000..622a2b4
--- /dev/null
@@ -0,0 +1,136 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_MM.H
+
+#ifndef __ID_EXMM__\r
+#define __ID_EXMM__
+
+#include "lib_head.h"
+//#pragma hdrstop\r
+\r
+//#pragma warn -pro\r
+//#pragma warn -use\r
+\r
+#if 1          // 1 == Debug/Dev  ;  0 == Production/final\r
+#define OUT_OF_MEM_MSG "MM_GetPtr: Out of memory!\nYou were short :%ld bytes"\r
+#else\r
+#define OUT_OF_MEM_MSG "\npee\n"
+#endif\r
+\r
+\r
+#define SAVENEARHEAP   0x400           // space to leave in data segment\r
+#define SAVEFARHEAP            0                       // space to leave in far heap\r
+\r
+#define        BUFFERSIZE              0x1000          // miscelanious, allways available buffer\r
+\r
+#define MAXBLOCKS              600\r
+\r
+\r
+//--------\r
+\r
+#define        EMS_INT                 0x67\r
+\r
+#define        EMS_STATUS              0x40\r
+#define        EMS_GETFRAME    0x41\r
+#define        EMS_GETPAGES    0x42\r
+#define        EMS_ALLOCPAGES  0x43\r
+#define        EMS_MAPPAGE             0x44\r
+#define        EMS_FREEPAGES   0x45\r
+#define        EMS_VERSION             0x46\r
+\r
+//--------\r
+\r
+#define        XMS_VERSION             0x00\r
+\r
+#define        XMS_ALLOCHMA    0x01\r
+#define        XMS_FREEHMA             0x02\r
+\r
+#define        XMS_GENABLEA20  0x03\r
+#define        XMS_GDISABLEA20 0x04\r
+#define        XMS_LENABLEA20  0x05\r
+#define        XMS_LDISABLEA20 0x06\r
+#define        XMS_QUERYA20    0x07\r
+\r
+#define        XMS_QUERYREE    0x08\r
+#define        XMS_ALLOC               0x09\r
+#define        XMS_FREE                0x0A\r
+#define        XMS_MOVE                0x0B\r
+#define        XMS_LOCK                0x0C\r
+#define        XMS_UNLOCK              0x0D\r
+#define        XMS_GETINFO             0x0E\r
+#define        XMS_RESIZE              0x0F\r
+\r
+#define        XMS_ALLOCUMB    0x10\r
+#define        XMS_FREEUMB             0x11\r
+\r
+//==========================================================================\r
+\r
+typedef void /*_seg*/ * memptr;\r
+\r
+typedef struct\r
+{\r
+       long    nearheap,farheap,EMSmem,XMSmem,mainmem;\r
+} mminfotype;\r
+\r
+//==========================================================================\r
+\r
+extern mminfotype      mminfo;\r
+extern memptr          bufferseg;\r
+extern boolean         mmerror;\r
+\r
+extern void            (* beforesort) (void);\r
+extern void            (* aftersort) (void);\r
+\r
+//==========================================================================\r
+\r
+void MM_Startup (void);\r
+void MM_Shutdown (void);\r
+void MM_MapEMS (void);\r
+\r
+void MM_GetPtr (memptr *baseptr,unsigned long size);\r
+void MM_FreePtr (memptr *baseptr);\r
+\r
+void MM_SetPurge (memptr *baseptr, int purge);\r
+void MM_SetLock (memptr *baseptr, boolean locked);\r
+void MM_SortMem (void);\r
+\r
+void MM_ShowMemory (void);\r
+\r
+long MM_UnusedMemory (void);\r
+long MM_TotalFree (void);\r
+\r
+void MM_BombOnError (boolean bomb);
+
+//==========================================================================\r
+\r
+//\r
+// local prototypes\r
+//\r
+\r
+boolean                MML_CheckForEMS (void);\r
+void           MML_ShutdownEMS (void);\r
+void           MM_MapEMS (void);\r
+boolean        MML_CheckForXMS (void);\r
+void           MML_ShutdownXMS (void);\r
+void           MML_UseSpace (unsigned segstart, unsigned seglength);\r
+void           MML_ClearBlock (void);\r
+\r
+//==========================================================================
+
+#endif
diff --git a/16/cawat/ARMGAME.DSK b/16/cawat/ARMGAME.DSK
new file mode 100644 (file)
index 0000000..1b741b0
Binary files /dev/null and b/16/cawat/ARMGAME.DSK differ
diff --git a/16/cawat/ARMGAME.PRJ b/16/cawat/ARMGAME.PRJ
new file mode 100644 (file)
index 0000000..bea242c
Binary files /dev/null and b/16/cawat/ARMGAME.PRJ differ
diff --git a/16/cawat/AUDIOARM.H b/16/cawat/AUDIOARM.H
new file mode 100644 (file)
index 0000000..46b813c
--- /dev/null
@@ -0,0 +1,91 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+/////////////////////////////////////////////////\r
+//\r
+// MUSE Header for .ARM\r
+// Created Mon Jun 08 16:14:17 1992\r
+//\r
+/////////////////////////////////////////////////\r
+\r
+#define NUMSOUNDS              35\r
+#define NUMSNDCHUNKS           106\r
+\r
+//\r
+// Sound names & indexes\r
+//\r
+typedef enum {\r
+               HITWALLSND,              // 0\r
+               WARPUPSND,               // 1\r
+               WARPDOWNSND,             // 2\r
+               GETBOLTSND,              // 3\r
+               GETNUKESND,              // 4\r
+               GETPOTIONSND,            // 5\r
+               GETKEYSND,               // 6\r
+               GETSCROLLSND,            // 7\r
+               GETPOINTSSND,            // 8\r
+               USEBOLTSND,              // 9\r
+               USENUKESND,              // 10\r
+               USEPOTIONSND,            // 11\r
+               USEKEYSND,               // 12\r
+               NOITEMSND,               // 13\r
+               WALK1SND,                // 14\r
+               WALK2SND,                // 15\r
+               TAKEDAMAGESND,           // 16\r
+               MONSTERMISSSND,          // 17\r
+               GAMEOVERSND,             // 18\r
+               SHOOTSND,                // 19\r
+               BIGSHOOTSND,             // 20\r
+               SHOOTWALLSND,            // 21\r
+               SHOOTMONSTERSND,         // 22\r
+               TAKEDMGHURTSND,          // 23\r
+               BALLBOUNCESND,           // 24\r
+               NOWAYSND,                // 25\r
+               WARPSND,                 // 26\r
+               HIT_GATESND,             // 27\r
+               GETGEMSND,               // 28\r
+               BOOMSND,                 // 29\r
+               GRELM_DEADSND,           // 30\r
+               FREEZETIMESND,           // 31\r
+               TIMERETURNSND,           // 32\r
+               TICKSND,                 // 33\r
+               BODY_EXPLODESND,         // 34\r
+               LASTSOUND\r
+            } soundnames;\r
+\r
+//\r
+// Base offsets\r
+//\r
+#define STARTPCSOUNDS          0\r
+#define STARTADLIBSOUNDS       35\r
+#define STARTDIGISOUNDS                70\r
+#define STARTMUSIC             105\r
+\r
+//\r
+// Music names & indexes\r
+//\r
+typedef enum {\r
+               TOOHOT_MUS,              // 0\r
+               LASTMUSIC\r
+            } musicnames;\r
+\r
+/////////////////////////////////////////////////\r
+//\r
+// Thanks for playing with MUSE!\r
+//\r
+/////////////////////////////////////////////////\r
diff --git a/16/cawat/C5_ACT1.C b/16/cawat/C5_ACT1.C
new file mode 100644 (file)
index 0000000..dcd65e3
--- /dev/null
@@ -0,0 +1,820 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// C3_PLAY.C\r
+\r
+#include "DEF.H"\r
+#pragma hdrstop\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#if 0\r
+#define MSHOTDAMAGE    2\r
+#define MSHOTSPEED     10000\r
+\r
+#define ESHOTDAMAGE    1\r
+#define ESHOTSPEED     5000\r
+\r
+#define SSHOTDAMAGE    3\r
+#define SSHOTSPEED     6500\r
+\r
+#define RANDOM_ATTACK 20\r
+#endif\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+boolean ShootPlayer (objtype *ob, short obclass, short speed, statetype *state);\r
+void T_ShootPlayer(objtype *ob);\r
+\r
+short zombie_base_delay;\r
+\r
+short other_x[] = {0,39,39,0},\r
+               other_y[] = {0,0,27,27};\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+dirtype dirtable[9] = {northwest,north,northeast,west,nodir,east,\r
+       southwest,south,southeast};\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                 BONUS ITEMS\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern statetype       s_boltbonus2;\r
+extern statetype       s_nukebonus2;\r
+extern statetype       s_boltbonus3;\r
+extern statetype       s_nukebonus3;\r
+\r
+statetype s_boltbonus = {BOLTOBJPIC,8,NULL,&s_boltbonus2};\r
+statetype s_boltbonus2 = {BOLT2OBJPIC,8,NULL,&s_boltbonus3};\r
+statetype s_boltbonus3 = {BOLT3OBJPIC,8,NULL,&s_boltbonus};\r
+\r
+statetype s_nukebonus = {NUKEOBJPIC,8,NULL,&s_nukebonus2};\r
+statetype s_nukebonus2 = {NUKE2OBJPIC,8,NULL,&s_nukebonus3};\r
+statetype s_nukebonus3 = {NUKE3OBJPIC,8,NULL,&s_nukebonus};\r
+\r
+statetype s_potionbonus = {POTIONOBJPIC,0,NULL,&s_potionbonus};\r
+//statetype s_rkey2bonus = {RKEY2PIC,0,NULL,&s_rkey2bonus};\r
+statetype s_rkeybonus = {RKEYOBJPIC,0,NULL,&s_rkeybonus};\r
+statetype s_ykeybonus = {YKEYOBJPIC,0,NULL,&s_ykeybonus};\r
+statetype s_gkeybonus = {GKEYOBJPIC,0,NULL,&s_gkeybonus};\r
+statetype s_bkeybonus = {BKEYOBJPIC,0,NULL,&s_bkeybonus};\r
+//////////////////////////statetype s_scrollbonus = {SCROLLOBJPIC,0,NULL,&s_scrollbonus};\r
+statetype s_chestbonus = {CHESTOBJPIC,0,NULL,&s_chestbonus};\r
+//statetype s_goalbonus = {NEMESISPIC,0,NULL,&s_goalbonus};\r
+\r
+extern statetype s_waterchestbonus2;\r
+statetype s_waterchestbonus1 = {O_WATER_CHEST1PIC,8,NULL,&s_waterchestbonus2};\r
+statetype s_waterchestbonus2 = {O_WATER_CHEST2PIC,8,NULL,&s_waterchestbonus1};\r
+\r
+extern statetype s_rgem2bonus;\r
+extern statetype s_ygem2bonus;\r
+extern statetype s_ggem2bonus;\r
+extern statetype s_bgem2bonus;\r
+extern statetype s_pgem2bonus;\r
+\r
+statetype s_rgem1bonus = {RGEM1PIC,30,NULL,&s_rgem2bonus};\r
+statetype s_ygem1bonus = {YGEM1PIC,30,NULL,&s_ygem2bonus};\r
+statetype s_ggem1bonus = {GGEM1PIC,30,NULL,&s_ggem2bonus};\r
+statetype s_bgem1bonus = {BGEM1PIC,30,NULL,&s_bgem2bonus};\r
+statetype s_pgem1bonus = {PGEM1PIC,30,NULL,&s_pgem2bonus};\r
+\r
+statetype s_rgem2bonus = {RGEM2PIC,30,NULL,&s_rgem1bonus};\r
+statetype s_ygem2bonus = {YGEM2PIC,30,NULL,&s_ygem1bonus};\r
+statetype s_ggem2bonus = {GGEM2PIC,30,NULL,&s_ggem1bonus};\r
+statetype s_bgem2bonus = {BGEM2PIC,30,NULL,&s_bgem1bonus};\r
+statetype s_pgem2bonus = {PGEM2PIC,30,NULL,&s_pgem1bonus};\r
+\r
+statetype s_bonus_die = {0,8,NULL,NULL};\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnBonus\r
+=\r
+===============\r
+*/\r
+\r
+void SpawnBonus (int tilex, int tiley, int number)\r
+{\r
+       extern unsigned gcolor;\r
+\r
+       statetype *state;\r
+\r
+       switch (number)\r
+       {\r
+               case B_BOLT:                    state = &s_boltbonus;           break;\r
+               case B_NUKE:                    state = &s_nukebonus;           break;\r
+               case B_POTION:                  state = &s_potionbonus;         break;\r
+\r
+               case B_RKEY:                    state = &s_rkeybonus;           break;\r
+               case B_YKEY:                    state = &s_ykeybonus;           break;\r
+               case B_GKEY:                    state = &s_gkeybonus;           break;\r
+               case B_BKEY:                    state = &s_bkeybonus;           break;\r
+\r
+               case B_RGEM:                    state = &s_rgem1bonus;          break;\r
+               case B_YGEM:                    state = &s_ygem1bonus;          break;\r
+               case B_GGEM:                    state = &s_ggem1bonus;          break;\r
+               case B_BGEM:                    state = &s_bgem1bonus;          break;\r
+               case B_PGEM:                    state = &s_pgem1bonus;          break;\r
+\r
+               case B_CHEST:\r
+                       if (gcolor == 0x0101)\r
+                               state = &s_waterchestbonus1;\r
+                       else\r
+                               state = &s_chestbonus;\r
+               break;\r
+\r
+#if 0\r
+               case B_SCROLL1:\r
+               case B_SCROLL2:\r
+               case B_SCROLL3:\r
+               case B_SCROLL4:\r
+               case B_SCROLL5:\r
+               case B_SCROLL6:\r
+               case B_SCROLL7:\r
+               case B_SCROLL8:                 state = &s_scrollbonus;         break;\r
+#endif\r
+\r
+//             case B_RKEY2:                   state = &s_rkey2bonus;          break;\r
+\r
+               default:\r
+                       Quit("SpawnBonus(): INVALID BONUS");\r
+               break;\r
+       }\r
+\r
+       SpawnNewObj (tilex,tiley,state,TILEGLOBAL/2);\r
+//     new->tileobject = true;\r
+       new->temp1 = number;\r
+       new->obclass = bonusobj;\r
+\r
+       switch (number)\r
+       {\r
+               case B_POTION:\r
+               case B_CHEST:\r
+               case B_BOLT:\r
+               case B_NUKE:\r
+                       new->flags |= of_shootable;\r
+               break;\r
+\r
+               default:\r
+                       new->flags &= ~of_shootable;\r
+               break;\r
+       }\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnTombstone\r
+=\r
+===============\r
+*/\r
+\r
+statetype s_tombs[3] = {{TOMB1PIC,8,NULL,&s_tombs[0]},\r
+                                                               {TOMB2PIC,8,NULL,&s_tombs[1]},\r
+                                                               {TOMB3PIC,8,NULL,&s_tombs[2]}};\r
+\r
+void SpawnTombstone (int tilex, int tiley, int shape)\r
+{\r
+       statetype *state=&s_tombs[shape];\r
+\r
+       SpawnNewObj (tilex,tiley,state,TILEGLOBAL/2);\r
+//     new->tileobject = true;\r
+       new->obclass = realsolidobj;\r
+       new->flags |= of_shootable;\r
+}\r
+\r
+\r
+/*\r
+============================================================================\r
+\r
+                                                                       FREEZE TIME OBJECT\r
+\r
+============================================================================\r
+*/\r
+\r
+extern statetype s_ftimebonus;\r
+extern statetype s_ftimebonus2;\r
+\r
+statetype s_ftimebonus = {TIMEOBJ1PIC,6,NULL,&s_ftimebonus2};\r
+statetype s_ftimebonus2 = {TIMEOBJ2PIC,6,NULL,&s_ftimebonus};\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnFTime\r
+=\r
+===============\r
+*/\r
+void SpawnFTime(int tilex, int tiley)\r
+{\r
+       SpawnNewObj(tilex,tiley,&s_ftimebonus,TILEGLOBAL/2);\r
+//     new->tileobject = true;\r
+       new->obclass = freezeobj;\r
+       new->flags |= of_shootable;\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                         EXPLODING WALL\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+void T_WallDie (objtype *ob);\r
+\r
+extern statetype s_walldie1;\r
+extern statetype s_walldie2;\r
+extern statetype s_walldie3;\r
+extern statetype s_walldie4;\r
+extern statetype s_walldie5;\r
+extern statetype s_walldie6;\r
+\r
+statetype s_walldie1 = {0,20,NULL,&s_walldie2};\r
+statetype s_walldie2 = {0,-1,T_WallDie,&s_walldie3};\r
+statetype s_walldie3 = {0,20,NULL,&s_walldie4};\r
+statetype s_walldie4 = {0,-1,T_WallDie,&s_walldie5};\r
+statetype s_walldie5 = {0,20,NULL,&s_walldie6};\r
+statetype s_walldie6 = {0,-1,T_WallDie,NULL};\r
+\r
+\r
+/*\r
+================\r
+=\r
+= ExplodeWall\r
+=\r
+================\r
+*/\r
+\r
+void ExplodeWall (int tilex, int tiley)\r
+{\r
+       extern unsigned gcolor;\r
+       unsigned tilenum;\r
+\r
+       DSpawnNewObj (tilex,tiley,&s_walldie1,0);\r
+       if (new == &dummyobj)\r
+               return;\r
+       new->obclass = inertobj;\r
+       new->active = always;\r
+       if (gcolor == 0x0101)\r
+               tilenum = WATEREXP;\r
+       else\r
+               tilenum = WALLEXP;\r
+       (unsigned)actorat[new->tilex][new->tiley] = tilemap[new->tilex][new->tiley] =\r
+               *(mapsegs[0]+farmapylookup[new->tiley]+new->tilex) = tilenum;\r
+       *(mapsegs[2]+farmapylookup[new->tiley]+new->tilex) &= 0xFF;\r
+}\r
+\r
+\r
+/*\r
+================\r
+=\r
+= T_WallDie\r
+=\r
+================\r
+*/\r
+\r
+void T_WallDie (objtype *ob)\r
+{\r
+       extern unsigned gcolor;\r
+       unsigned tile,other,spot,x,y;\r
+\r
+       if (++ob->temp1 == 3)\r
+               tile = 0;\r
+       else\r
+               if (gcolor == 0x0101)\r
+                       tile = WATEREXP-1 + ob->temp1;\r
+               else\r
+                       tile = WALLEXP-1 + ob->temp1;\r
+       x = ob->tilex;\r
+       y = ob->tiley;\r
+\r
+       (unsigned)actorat[x][y] = tilemap[x][y] = *(mapsegs[0]+farmapylookup[y]+x) = tile;\r
+\r
+       if (ob->temp1 == 1)\r
+       {\r
+       //\r
+       // blow up nearby walls\r
+       //\r
+               spot = (*(mapsegs[2]+farmapylookup[y]+(x-1))) >> 8;\r
+               if (spot == EXP_WALL_CODE)\r
+                       ExplodeWall (x-1,y);\r
+               spot = (*(mapsegs[2]+farmapylookup[y]+(x+1))) >> 8;\r
+               if (spot == EXP_WALL_CODE)\r
+                       ExplodeWall (x+1,y);\r
+               spot = (*(mapsegs[2]+farmapylookup[y-1]+x)) >> 8;\r
+               if (spot == EXP_WALL_CODE)\r
+                       ExplodeWall (x,y-1);\r
+               spot = (*(mapsegs[2]+farmapylookup[y+1]+x)) >> 8;\r
+               if (spot == EXP_WALL_CODE)\r
+                       ExplodeWall (x,y+1);\r
+       }\r
+}\r
+/*\r
+=============================================================================\r
+\r
+                                                               OBJ_WARP GATE\r
+\r
+=============================================================================\r
+*/\r
+\r
+void T_Gate (objtype *ob);\r
+\r
+extern statetype s_obj_gate1;\r
+extern statetype s_obj_gate2;\r
+extern statetype s_obj_gate3;\r
+extern statetype s_obj_gate4;\r
+\r
+statetype s_obj_gate1 = {OBJ_WARP1PIC,10,T_Gate,&s_obj_gate2};\r
+statetype s_obj_gate2 = {OBJ_WARP2PIC,10,T_Gate,&s_obj_gate3};\r
+statetype s_obj_gate3 = {OBJ_WARP3PIC,10,T_Gate,&s_obj_gate4};\r
+statetype s_obj_gate4 = {OBJ_WARP4PIC,10,T_Gate,&s_obj_gate1};\r
+\r
+extern statetype s_anthill;\r
+statetype s_anthill = {ANT_HILLPIC, 20, T_Gate, &s_anthill};\r
+\r
+//---------------------------------------------------------------------------\r
+//     SpawnWarp()\r
+//\r
+// TYPE : Type param is the gate number (1-what ever) will link you to\r
+//                      gate of that type.\r
+//---------------------------------------------------------------------------\r
+void SpawnWarp (int tilex, int tiley, int type)\r
+{\r
+\r
+       if (type)\r
+               SpawnNewObj (tilex,tiley,&s_obj_gate1,TILEGLOBAL/3);\r
+       else\r
+               SpawnNewObj (tilex,tiley,&s_anthill,TILEGLOBAL/3);\r
+       new->obclass = gateobj;\r
+       new->temp1 = type;\r
+}\r
+\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= T_Gate\r
+=\r
+===============\r
+*/\r
+\r
+#define STATUSCOLOR    4\r
+\r
+void T_Gate (objtype *ob)\r
+{\r
+       objtype *check;\r
+       unsigned        temp,spot;\r
+\r
+       if (CheckHandAttack (ob) && !playstate)\r
+       {\r
+               // make\r
+               //\r
+//             spot = (*(mapsegs[2]+farmapylookup[ob->tiley+1]+ob->tilex)) >> 8;\r
+//             if (spot--)\r
+//                     if (gamestate.keys[spot])\r
+//                             TakeKey(spot);\r
+//                     else\r
+//                             return;\r
+\r
+               //\r
+               // warp\r
+               //\r
+\r
+//             temp = bufferofs;\r
+//             bufferofs = 0;\r
+//             VW_Bar (26,4,232,9,STATUSCOLOR);                // clear text description\r
+//             bufferofs = temp;\r
+\r
+//             IN_ClearKeysDown ();\r
+               if (ob->temp1)\r
+               {\r
+                       //\r
+                       // teleport inside level\r
+                       //\r
+\r
+                       for (check=player->next;check;check=check->next)\r
+                               if (check->obclass==gateobj && check->temp1==ob->temp1 &&\r
+                                       check != ob)\r
+                               {\r
+                                       player->x = check->x;\r
+                                       player->y = check->y;\r
+                                       Thrust (player->angle,TILEGLOBAL/2);            // move forwards\r
+                                       Thrust (player->angle,TILEGLOBAL/2);            // move forwards\r
+                                       Thrust (player->angle,TILEGLOBAL/2);            // move forwards\r
+                                       fizzlein=true;\r
+                                       SD_PlaySound(WARPSND);\r
+                               }\r
+               }\r
+               else\r
+               {\r
+                       //\r
+                       // teleport out of level\r
+                       //\r
+\r
+                       playstate = ex_warped;\r
+                       spot = (*(mapsegs[2]+farmapylookup[ob->tiley+1]+ob->tilex)) >> 8;\r
+                       gamestate.mapon=spot;\r
+                       SD_PlaySound(WARPUPSND);\r
+               }\r
+       }\r
+}\r
+\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                       FAT DEMON\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define FATCLOUDDAMAGE 2\r
+\r
+void T_FatDemon (objtype *ob);\r
+void T_CheckCnt(objtype *ob);\r
+void ExplodeSound(objtype *ob);\r
+\r
+extern statetype s_fatdemon_pause;\r
+extern statetype s_fatdemon_walk1;\r
+extern statetype s_fatdemon_walk2;\r
+extern statetype s_fatdemon_walk3;\r
+extern statetype s_fatdemon_walk4;\r
+extern statetype s_fatdemon_attack1;\r
+extern statetype s_fatdemon_attack2;\r
+extern statetype s_fatdemon_blowup2;\r
+extern statetype s_fatdemon_blowup3;\r
+extern statetype s_fatdemon_blowup4;\r
+extern statetype s_fatdemon_blowup5;\r
+extern statetype s_fatdemon_blowup6;\r
+extern statetype s_fatdemon_blowup7;\r
+extern statetype s_fatdemon_explode;\r
+extern statetype s_fatdemon_feet;\r
+\r
+statetype s_fatdemon_pause = {FATDEMON_WALK1PIC,40,NULL,&s_fatdemon_walk2};\r
+\r
+statetype s_fatdemon_walk1 = {FATDEMON_WALK1PIC,13,T_FatDemon,&s_fatdemon_walk2};\r
+statetype s_fatdemon_walk2 = {FATDEMON_WALK2PIC,13,T_FatDemon,&s_fatdemon_walk3};\r
+statetype s_fatdemon_walk3 = {FATDEMON_WALK3PIC,13,T_FatDemon,&s_fatdemon_walk4};\r
+statetype s_fatdemon_walk4 = {FATDEMON_WALK4PIC,13,T_FatDemon,&s_fatdemon_walk1};\r
+\r
+statetype s_fatdemon_attack1 = {FATDEMON_ATTACK1PIC,20,NULL,&s_fatdemon_attack2};\r
+statetype s_fatdemon_attack2 = {FATDEMON_ATTACK2PIC,20,T_DoDamage,&s_fatdemon_pause};\r
+\r
+statetype s_fatdemon_ouch = {FATDEMON_OUCHPIC,14,NULL,&s_fatdemon_walk1};\r
+\r
+statetype s_fatdemon_blowup1 = {FATDEMON_BLOWUP1PIC,25,NULL,&s_fatdemon_blowup2};\r
+statetype s_fatdemon_blowup2 = {FATDEMON_BLOWUP2PIC,25,NULL,&s_fatdemon_blowup3};\r
+statetype s_fatdemon_blowup3 = {FATDEMON_BLOWUP1PIC,15,NULL,&s_fatdemon_blowup4};\r
+statetype s_fatdemon_blowup4 = {FATDEMON_BLOWUP2PIC,15,NULL,&s_fatdemon_blowup5};\r
+statetype s_fatdemon_blowup5 = {FATDEMON_BLOWUP1PIC,6,NULL,&s_fatdemon_blowup6};\r
+statetype s_fatdemon_blowup6 = {FATDEMON_BLOWUP2PIC,6,T_CheckCnt,&s_fatdemon_blowup5};\r
+statetype s_fatdemon_blowup7 = {FATDEMON_BLOWUP3PIC,30,NULL,&s_fatdemon_explode};\r
+\r
+\r
+statetype s_fatdemon_explode = {FATDEMON_EXPLODEPIC,40,ExplodeSound,&s_fatdemon_feet};\r
+statetype s_fatdemon_feet = {FATDEMON_FEETPIC,30,NULL,&s_fatdemon_feet};\r
+\r
+#define cnt            ob->temp1\r
+#define cloud_delay    ob->temp2\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnFatDemon\r
+=\r
+===============\r
+*/\r
+\r
+void SpawnFatDemon (int tilex, int tiley)\r
+{\r
+       SpawnNewObj(tilex,tiley,&s_fatdemon_walk1,35*PIXRADIUS);\r
+       new->speed = 2500;\r
+       new->obclass = fatdemonobj;\r
+       new->flags |= of_shootable;\r
+       new->hitpoints = EasyHitPoints(10);\r
+       new->temp1 = 25;        //used to "shake" the fat dude??????\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= T_FatDemon\r
+=\r
+===============\r
+*/\r
+\r
+void T_FatDemon (objtype *ob)\r
+{\r
+       if (Chase(ob,true) || (random(1000)<RANDOM_ATTACK))\r
+       {\r
+               ob->state = &s_fatdemon_attack1;\r
+               ob->ticcount = ob->state->tictime;\r
+               return;\r
+       }\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= T_DecCnt\r
+=\r
+===============\r
+*/\r
+\r
+void T_CheckCnt (objtype *ob)\r
+{\r
+       ob->temp1--;\r
+       if (!ob->temp1)\r
+       {\r
+               ob->state = &s_fatdemon_blowup7;\r
+               ob->ticcount = ob->state->tictime;\r
+       }\r
+}\r
+\r
+/*\r
+===============\r
+=\r
+= ExplodeSound\r
+=\r
+===============\r
+*/\r
+void ExplodeSound(objtype *ob)\r
+{\r
+       if (ob->temp1 != 666)                                           // Has this think been called already?\r
+       {\r
+               SD_PlaySound(BODY_EXPLODESND);\r
+               ob->temp1 = 666;                        // Has now!\r
+       }\r
+}\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                               WATER DRAGON\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern statetype s_dragon_shot1;\r
+extern statetype s_dragon_shot2;\r
+\r
+\r
+void T_Dragon(objtype *ob);\r
+void T_DragonShoot(objtype *ob);\r
+\r
+\r
+statetype s_wet_bubbles1 = {DRAGON_BUBBLES1PIC,13,T_Dragon,&s_wet_bubbles2};\r
+statetype s_wet_bubbles2 = {DRAGON_BUBBLES2PIC,15,T_Dragon,&s_wet_bubbles1};\r
+statetype s_wet_bubbles3 = {0,35,T_Dragon,&s_wet_bubbles1};\r
+\r
+statetype s_wet_peek = {DRAGON_EYESPIC,45,NULL,&s_wet_bubbles1};\r
+\r
+statetype s_wet_rise1 = {DRAGON_BUBBLES2PIC,15,NULL,&s_wet_rise3};\r
+statetype s_wet_rise3 = {DRAGON_EYESPIC,20,NULL,&s_wet_rise4};\r
+statetype s_wet_rise4 = {DRAGON_RISE1PIC,20,NULL,&s_wet_rise5};\r
+statetype s_wet_rise5 = {DRAGON_RISE2PIC,20,NULL,&s_wet_walk1};\r
+\r
+statetype s_wet_sink1 = {DRAGON_RISE2PIC,20,NULL,&s_wet_sink2};\r
+statetype s_wet_sink2 = {DRAGON_RISE1PIC,20,NULL,&s_wet_sink3};\r
+statetype s_wet_sink3 = {DRAGON_EYESPIC,20,NULL,&s_wet_bubbles1};\r
+\r
+statetype s_wet_walk1 = {DRAGON_WALK1PIC,12,T_Dragon,&s_wet_walk2};\r
+statetype s_wet_walk2 = {DRAGON_WALK2PIC,12,T_Dragon,&s_wet_walk3};\r
+statetype s_wet_walk3 = {DRAGON_WALK3PIC,12,T_Dragon,&s_wet_walk4};\r
+statetype s_wet_walk4 = {DRAGON_WALK4PIC,12,T_Dragon,&s_wet_walk1};\r
+\r
+statetype s_wet_attack1 = {DRAGON_ATTACK1PIC,10,NULL,&s_wet_attack2};\r
+statetype s_wet_attack2 = {DRAGON_ATTACK2PIC,10,NULL,&s_wet_attack3};\r
+statetype s_wet_attack3 = {DRAGON_ATTACK2PIC,10,NULL,&s_wet_attack4};\r
+statetype s_wet_attack4 = {DRAGON_ATTACK3PIC,10,T_DragonShoot,&s_wet_walk1};\r
+\r
+statetype s_wet_ouch = {DRAGON_OUCHPIC,10,T_Dragon,&s_wet_walk1};\r
+\r
+statetype s_wet_die1 = {DRAGON_DEATH1PIC,27,NULL,&s_wet_die2};\r
+statetype s_wet_die2 = {DRAGON_DEATH2PIC,29,NULL,&s_wet_die3};\r
+statetype s_wet_die3 = {DRAGON_DEATH3PIC,44,NULL,&s_wet_die4};\r
+statetype s_wet_die4 = {DRAGON_BUBBLES2PIC,26,NULL,&s_wet_die5};\r
+statetype s_wet_die5 = {DRAGON_BUBBLES1PIC,23,NULL,NULL};\r
+\r
+statetype s_dragon_shot1 = {PSHOT1PIC,8,&T_ShootPlayer,&s_dragon_shot2};\r
+statetype s_dragon_shot2 = {PSHOT2PIC,8,&T_ShootPlayer,&s_dragon_shot1};\r
+\r
+\r
+typedef enum {wt_BUBBLES,wt_WALK,wt_CORNER1,wt_CORNER2,wt_CORNER3,wt_CORNER4} DragonTypes;\r
+\r
+\r
+#define WD_TIMEREMAIN  (ob->temp1)\r
+#define WD_STAGE       (ob->temp2)\r
+#define WATER_DRAGON_LEAVE     0x04\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnDragon\r
+=\r
+===============\r
+*/\r
+void SpawnDragon(int tilex, int tiley)\r
+{\r
+       objtype *ob;\r
+       SpawnNewObj(tilex,tiley,&s_wet_bubbles1,PIXRADIUS*35);\r
+       ob=new;\r
+\r
+       WD_STAGE = wt_BUBBLES;\r
+       WD_TIMEREMAIN = 80;\r
+\r
+       new->obclass = wetobj;\r
+       new->speed = 1000;\r
+       new->flags &= ~of_shootable;\r
+       new->hitpoints = EasyHitPoints(20);\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= T_Dragon\r
+=\r
+===============\r
+*/\r
+\r
+void T_Dragon(objtype *ob)\r
+{\r
+       switch (WD_STAGE)\r
+       {\r
+               case wt_BUBBLES:\r
+                       ob->flags &= ~of_shootable;\r
+                       if (Chase(ob,true))\r
+                       {\r
+                               // RISE & GOTO WALK STAGE\r
+                               //\r
+\r
+                               WD_STAGE = wt_WALK;\r
+                               WD_TIMEREMAIN = 60*8+random(60*5);\r
+                               ob->state = &s_wet_rise1;\r
+                               ob->speed = 2200;\r
+                               ob->ticcount = ob->state->tictime;\r
+                       }\r
+                       else\r
+                       {\r
+                               // DEC COUNTER - And check for WALK\r
+                               //\r
+                               if ((WD_TIMEREMAIN-=realtics) < 0)\r
+                               {\r
+                                       // RISE & GOTO WALK STAGE\r
+                                       //\r
+\r
+                                       WD_STAGE = wt_WALK;\r
+                                       WD_TIMEREMAIN = 60*8+random(60*5);\r
+                                       ob->state = &s_wet_rise1;\r
+                                       ob->speed = 2200;\r
+                                       ob->ticcount = ob->state->tictime;\r
+                               }\r
+                               else\r
+                               if (random(1000)<5)\r
+                               {\r
+                                       // RANDOM PEEK UP OUT OF WATER\r
+                                       //\r
+\r
+                                       ob->state=&s_wet_peek;\r
+                                       ob->ticcount = ob->state->tictime;\r
+                               }\r
+                       }\r
+                       break;\r
+\r
+\r
+               case wt_WALK:\r
+                       ob->flags |= of_shootable;\r
+\r
+                       if (Chase(ob,true) || (CheckHandAttack(ob)))\r
+\r
+                       {\r
+                                       ob->flags |= WATER_DRAGON_LEAVE;\r
+                                       WD_STAGE = random(wt_CORNER3) + 2;\r
+                                       WD_TIMEREMAIN = 60*2+(random(6)*60);\r
+                                       ob->state = &s_wet_bubbles1;\r
+                                       ob->ticcount = ob->state->tictime;\r
+                       }\r
+                       else\r
+                               if (AngleNearPlayer(ob) != -1)\r
+                               {\r
+                                       ob->state = &s_wet_attack1;\r
+                                       ob->ticcount = ob->state->tictime;\r
+                               }\r
+\r
+                       else\r
+                       {\r
+                               // DEC COUNTER - And check for SINK\r
+                               //\r
+                               if ((WD_TIMEREMAIN-=realtics) < 0)\r
+                               {\r
+                                       // SINK & GOTO BUBBLE STAGE\r
+                                       //\r
+\r
+                                       WD_STAGE = wt_BUBBLES;\r
+                                       WD_TIMEREMAIN = 60*2+random(60*2);\r
+                                       ob->state = &s_wet_sink1;\r
+                                       ob->speed = 1200;\r
+                                       ob->ticcount = ob->state->tictime;\r
+                                       ob->flags &= ~of_shootable;\r
+                               }\r
+\r
+                       }\r
+                       break;\r
+               case wt_CORNER1:\r
+               case wt_CORNER2:\r
+               case wt_CORNER3:\r
+               case wt_CORNER4:\r
+                       ob->flags &= ~of_shootable;\r
+                       if ((WD_TIMEREMAIN -= realtics) < 0)\r
+                       {\r
+                               WD_STAGE = wt_BUBBLES;\r
+                               ob->flags &= ~WATER_DRAGON_LEAVE;\r
+                       }\r
+                       else\r
+                       {\r
+                               fixed tempx,tempy;\r
+                               unsigned temp_tilex,temp_tiley;\r
+\r
+                               tempx = player->x;\r
+                               tempy = player->y;\r
+                               temp_tilex = player->tilex;\r
+                               temp_tiley = player->tiley;\r
+\r
+                               player->x = ((long)other_x[WD_STAGE-2]<<TILESHIFT)+TILEGLOBAL/2;\r
+                               player->y = ((long)other_y[WD_STAGE-2]<<TILESHIFT)+TILEGLOBAL/2;\r
+                               player->tilex = other_x[WD_STAGE-2];\r
+                               player->tiley = other_y[WD_STAGE-2];\r
+\r
+\r
+                               Chase(ob,true);\r
+\r
+                               player->x = tempx;\r
+                               player->y = tempy;\r
+                               player->tilex = temp_tilex;\r
+                               player->tiley = temp_tiley;\r
+                       }\r
+                       break;\r
+       }\r
+}\r
+\r
+/*\r
+===============\r
+=\r
+= T_DragonShoot\r
+=\r
+===============\r
+*/\r
+void T_DragonShoot (objtype *ob)\r
+{\r
+       ShootPlayer(ob,dshotobj,10000,&s_dragon_shot1);\r
+}\r
diff --git a/16/cawat/C5_ACT2.C b/16/cawat/C5_ACT2.C
new file mode 100644 (file)
index 0000000..fd8633a
--- /dev/null
@@ -0,0 +1,827 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// C3_PLAY.C\r
+\r
+#include "DEF.H"\r
+#pragma hdrstop\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+void SpawnSkeleton(int tilex, int tiley);\r
+\r
+#if 0\r
+#define MSHOTDAMAGE    2\r
+#define MSHOTSPEED     10000\r
+\r
+#define ESHOTDAMAGE    1\r
+#define ESHOTSPEED     5000\r
+\r
+#define SSHOTDAMAGE    3\r
+#define SSHOTSPEED     6500\r
+\r
+#define RANDOM_ATTACK 20\r
+#endif\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+boolean ShootPlayer (objtype *ob, short obclass, short speed, statetype *state);\r
+void T_ShootPlayer(objtype *ob);\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                               SKELETON IN WALL\r
+\r
+=============================================================================\r
+*/\r
+\r
+void T_WallSkeleton(objtype *ob);\r
+\r
+statetype s_wallskel = {0,40,T_WallSkeleton,&s_wallskel};\r
+statetype s_wallskel2 = {0,1,NULL,NULL};\r
+\r
+\r
+enum wskel_modes {ws_wall1,ws_wall2,ws_wall3,ws_exit};\r
+//enum wskel_modes {ws_wall1,ws_exit};\r
+\r
+#define wskel_mode     ob->temp1\r
+#define wskel_delay    ob->temp2\r
+#define wskel_base     ob->angle\r
+#define wskel_wallx    ob->hitpoints\r
+#define wskel_wally    ob->speed\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnWallSkeleton\r
+=\r
+===============\r
+*/\r
+void SpawnWallSkeleton(int tilex, int tiley)\r
+{\r
+       char xofs[] = {0,0,-1,+1};\r
+       char yofs[] = {-1,+1,0,0};\r
+\r
+       objtype *ob;\r
+       int wallx=tilex,wally=tiley,wallbase,wallmode,loop;\r
+       unsigned tile,current_delay;\r
+\r
+       for (loop=0; loop<4; loop++)\r
+       {\r
+               tile = *(mapsegs[0]+farmapylookup[tiley+yofs[loop]]+tilex+xofs[loop]);\r
+               switch (tile)\r
+               {\r
+//                     case WALL_SKELETON_CODE:\r
+//                     case WALL_SKELETON_CODE+1:\r
+//                     case WALL_SKELETON_CODE+2:\r
+//                             wallmode = ws_wall1+(tile-WALL_SKELETON_CODE);\r
+//                             wallbase = WALL_SKELETON_CODE;\r
+//                             goto foundtile;\r
+//                     break;\r
+\r
+                       case 66:\r
+                       case 68:\r
+//                     case 21:\r
+                               wallmode = ws_wall1+(tile-66);\r
+                               wallbase = 66;\r
+                               goto foundtile;\r
+//                     break;\r
+\r
+                       case 67:\r
+                       case 69:\r
+                               wallmode = ws_wall1+(tile-67);\r
+                               wallbase = 67;\r
+                               goto foundtile;\r
+//                     break;\r
+               }\r
+       }\r
+\r
+       return;\r
+foundtile:;\r
+\r
+       wallx += xofs[loop];\r
+       wally += yofs[loop];\r
+\r
+       SpawnNewObj(tilex,tiley,&s_wallskel,PIXRADIUS*35);\r
+       ob = new;\r
+       new->obclass = wallskelobj;\r
+       new->speed = 1900;\r
+       new->flags &= ~of_shootable;\r
+       new->hitpoints = 12;\r
+\r
+//     new->tilex = wallx;\r
+//     new->tiley = wally;\r
+       wskel_wallx = wallx;\r
+       wskel_wally = wally;\r
+       wskel_base = wallbase;\r
+       new->active = no;\r
+\r
+       wskel_mode = wallmode;\r
+\r
+       tile = *(mapsegs[2]+farmapylookup[wally]+wallx);\r
+       if (tile)\r
+               wskel_delay = (tile>>8)*30;\r
+       else\r
+       {\r
+               current_delay = (2*60)+random(4*60);\r
+               wskel_delay = zombie_base_delay+current_delay;\r
+               zombie_base_delay += current_delay;\r
+               if (zombie_base_delay > 8*60)\r
+                       zombie_base_delay = 0;\r
+       }\r
+}\r
+\r
+/*\r
+===============\r
+=\r
+= T_WallSkeleton\r
+=\r
+===============\r
+*/\r
+void T_WallSkeleton(objtype *ob)\r
+{\r
+       int x=wskel_wallx,y=wskel_wally;\r
+\r
+       wskel_delay -= realtics;\r
+       if (wskel_delay > 0)\r
+               return;\r
+\r
+       switch (wskel_mode)\r
+       {\r
+               case ws_wall2:\r
+                       if ((wskel_base == 66) || (wskel_base == 67))\r
+                               wskel_mode++;\r
+               case ws_wall1:\r
+               case ws_wall3:\r
+                       (unsigned)actorat[x][y]\r
+                               = tilemap[x][y]\r
+                               = *(mapsegs[0]+farmapylookup[y]+x)\r
+                               = wskel_base+(wskel_mode-ws_wall1);\r
+\r
+                       wskel_mode++;\r
+                       wskel_delay = (120);\r
+                       ob->active = always;\r
+               break;\r
+\r
+               case ws_exit:\r
+                       (unsigned)actorat[x][y]\r
+                               = tilemap[x][y]\r
+                               = *(mapsegs[0]+farmapylookup[y]+x)\r
+                               = 21;\r
+//                             = wskel_base;\r
+                       ob->tilex = ob->x >> TILESHIFT;\r
+                       ob->tiley = ob->y >> TILESHIFT;\r
+\r
+                       ob->obclass = skeletonobj;\r
+                       ob->speed = 2036;\r
+                       ob->flags |= of_shootable;\r
+                       ob->hitpoints = 12;\r
+                       ob->state = &s_skel_1;\r
+                       ob->ticcount = ob->state->tictime;\r
+               break;\r
+       }\r
+}\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                               SKELETONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+void T_Skeleton(objtype *ob);\r
+\r
+\r
+\r
+\r
+statetype s_skel_pause = {SKELETON_1PIC,40,NULL,&s_skel_2};\r
+\r
+statetype s_skel_1 = {SKELETON_1PIC,10,T_Skeleton,&s_skel_2};\r
+statetype s_skel_2 = {SKELETON_2PIC,10,T_Skeleton,&s_skel_3};\r
+statetype s_skel_3 = {SKELETON_3PIC,10,T_Skeleton,&s_skel_4};\r
+statetype s_skel_4 = {SKELETON_4PIC,10,T_Skeleton,&s_skel_1};\r
+\r
+statetype s_skel_attack1 = {SKELETON_ATTACK_1PIC,12,NULL,&s_skel_attack2};\r
+statetype s_skel_attack2 = {SKELETON_ATTACK_2PIC,12,NULL,&s_skel_attack3};\r
+statetype s_skel_attack3 = {SKELETON_ATTACK_3PIC,12,T_DoDamage,&s_skel_pause};\r
+\r
+statetype s_skel_ouch = {SKELETON_OUCHPIC,8,NULL,&s_skel_1};\r
+\r
+statetype s_skel_die1 = {SKELETON_OUCHPIC,18,NULL,&s_skel_die2};\r
+statetype s_skel_die2 = {SKELETON_DEATH_1PIC,18,NULL,&s_skel_die3};\r
+statetype s_skel_die3 = {SKELETON_DEATH_2PIC,18,NULL,&s_skel_die3};\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnSkeleton\r
+=\r
+===============\r
+*/\r
+void SpawnSkeleton(int tilex, int tiley)\r
+{\r
+       SpawnNewObj(tilex,tiley,&s_skel_1,PIXRADIUS*35);\r
+       new->obclass = skeletonobj;\r
+       new->speed = 2036;\r
+       new->flags |= of_shootable;\r
+       new->hitpoints = EasyHitPoints(12);\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= T_Skeleton\r
+=\r
+===============\r
+*/\r
+\r
+void T_Skeleton(objtype *ob)\r
+{\r
+       if (Chase (ob,true) || (random(1000)<RANDOM_ATTACK))\r
+       {\r
+               ob->state = &s_skel_attack1;\r
+               ob->ticcount = ob->state->tictime;\r
+               return;\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                               EYE\r
+\r
+=============================================================================\r
+*/\r
+\r
+void T_EyeMage (objtype *ob);\r
+boolean T_EyeShoot (objtype *ob, boolean eyeshot);\r
+void T_EyeShootPlayer (objtype *ob);\r
+\r
+extern statetype s_eye_shootplayer_1;\r
+extern statetype s_eye_shootplayer_2;\r
+\r
+statetype s_eye_pause = {EYE_WALK1PIC,40,NULL,&s_eye_2};\r
+\r
+statetype s_eye_1 = {EYE_WALK1PIC,20,T_EyeMage,&s_eye_2};\r
+statetype s_eye_2 = {EYE_WALK2PIC,20,T_EyeMage,&s_eye_3};\r
+statetype s_eye_3 = {EYE_WALK3PIC,20,T_EyeMage,&s_eye_4};\r
+statetype s_eye_4 = {EYE_WALK2PIC,20,T_EyeMage,&s_eye_1};\r
+statetype s_eye_shootplayer_1 = {EYE_SCOWLPIC,1,T_EyeShootPlayer,&s_eye_shootplayer_2};\r
+statetype s_eye_shootplayer_2 = {EYE_SCOWLPIC,20,NULL,&s_eye_1};\r
+\r
+statetype s_eye_ouch = {EYE_OUCH1PIC,8,NULL,&s_eye_ouch2};\r
+statetype s_eye_ouch2 = {EYE_OUCH2PIC,8,NULL,&s_eye_1};\r
+\r
+statetype s_eye_die1 = {EYE_DEATH1PIC,22,NULL,&s_eye_die2};\r
+statetype s_eye_die2 = {EYE_DEATH2PIC,22,NULL,&s_eye_die3};\r
+statetype s_eye_die3 = {EYE_DEATH2PIC,22,NULL,NULL};\r
+\r
+extern statetype s_eshot2;\r
+\r
+statetype s_eshot1 = {EYE_SHOT1PIC,8,&T_ShootPlayer,&s_eshot2};\r
+statetype s_eshot2 = {EYE_SHOT2PIC,8,&T_ShootPlayer,&s_eshot1};\r
+\r
+#define eye_mode       ob->temp1\r
+#define eye_delay      ob->temp2\r
+\r
+\r
+//-------------------------------------------------------------------------\r
+// SpawnEye()\r
+//-------------------------------------------------------------------------\r
+void SpawnEye(int tilex, int tiley)\r
+{\r
+       objtype *ob;\r
+\r
+       SpawnNewObj(tilex,tiley,&s_eye_1,PIXRADIUS*35);\r
+       ob = new;\r
+       new->obclass = eyeobj;\r
+       new->speed = 1200;\r
+       new->flags |= of_shootable;\r
+       new->hitpoints = EasyHitPoints(15);\r
+       eye_mode = em_other1;\r
+}\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// T_EyeShootPlayer\r
+//---------------------------------------------------------------------------\r
+void T_EyeShootPlayer (objtype *ob)\r
+{\r
+       ShootPlayer(ob,eshotobj,ESHOTSPEED,&s_eshot1);\r
+}\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                       SUCCUBUS\r
+\r
+=============================================================================\r
+*/\r
+\r
+void T_Succubus (objtype *ob);\r
+void T_SuccubusShot (objtype *ob);\r
+\r
+extern statetype s_succubus_pause;\r
+extern statetype s_succubus_walk1;\r
+extern statetype s_succubus_walk2;\r
+extern statetype s_succubus_walk3;\r
+extern statetype s_succubus_walk4;\r
+extern statetype s_succubus_shot1;\r
+extern statetype s_succubus_attack1;\r
+extern statetype s_succubus_attack2;\r
+extern statetype s_succubus_attack3;\r
+extern statetype s_succubus_death1;\r
+extern statetype s_succubus_death2;\r
+\r
+statetype s_succubus_pause = {SUCCUBUS_WALK2PIC,10,NULL,&s_succubus_walk3};\r
+\r
+statetype s_succubus_walk1 = {SUCCUBUS_WALK1PIC,10,T_EyeMage,&s_succubus_walk2};\r
+statetype s_succubus_walk2 = {SUCCUBUS_WALK2PIC,10,T_EyeMage,&s_succubus_walk3};\r
+statetype s_succubus_walk3 = {SUCCUBUS_WALK3PIC,10,T_EyeMage,&s_succubus_walk4};\r
+statetype s_succubus_walk4 = {SUCCUBUS_WALK4PIC,10,T_EyeMage,&s_succubus_walk1};\r
+\r
+statetype s_succubus_attack1 = {SUCCUBUS_ATTACK1PIC,15,NULL,&s_succubus_attack2};\r
+statetype s_succubus_attack2 = {SUCCUBUS_ATTACK1PIC,-1,T_SuccubusShot,&s_succubus_attack3};\r
+statetype s_succubus_attack3 = {SUCCUBUS_ATTACK2PIC,15,NULL,&s_succubus_pause};\r
+\r
+statetype s_succubus_ouch = {SUCCUBUS_OUCHPIC,15,NULL,&s_succubus_walk1};\r
+\r
+statetype s_succubus_death1 = {SUCCUBUS_DEATH1PIC,55,NULL,&s_succubus_death2};\r
+statetype s_succubus_death2 = {SUCCUBUS_DEATH2PIC,20,NULL,&s_succubus_death2};\r
+\r
+statetype s_succubus_shot1 = {SUCCUBUS_SHOT1PIC,12,&T_ShootPlayer,&s_succubus_shot1};\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnSuccubus\r
+=\r
+===============\r
+*/\r
+\r
+void SpawnSuccubus (int tilex, int tiley)\r
+{\r
+       SpawnNewObj(tilex,tiley,&s_succubus_walk1,PIXRADIUS*30);\r
+       new->obclass = succubusobj;\r
+       new->speed = 2500;\r
+       new->flags |= of_shootable;\r
+       new->hitpoints = EasyHitPoints(12);\r
+}\r
+\r
+/*\r
+===============\r
+=\r
+= T_SuccubusShot\r
+=\r
+===============\r
+*/\r
+\r
+void T_SuccubusShot (objtype *ob)\r
+{\r
+       ShootPlayer(ob,sshotobj,ob->temp1 ? MSHOTSPEED : SSHOTSPEED,&s_succubus_shot1);\r
+//     ob->state = &s_succubus_attack3;\r
+//     ob->ticcount = ob->temp1 ? 7 : ob->state->tictime;\r
+}\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                       MAGE\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+void T_MageShoot (objtype *ob);\r
+\r
+extern statetype s_magepause;\r
+\r
+extern statetype s_mage1;\r
+extern statetype s_mage2;\r
+\r
+extern statetype s_mageattack1;\r
+extern statetype s_mageattack2;\r
+extern statetype s_mageattack3;\r
+\r
+extern statetype s_mageouch;\r
+\r
+extern statetype s_magedie1;\r
+extern statetype s_magedie2;\r
+\r
+\r
+statetype s_magepause = {MAGE1PIC,10,NULL,&s_mage2};\r
+\r
+statetype s_mage1 = {MAGE1PIC,20,T_EyeMage,&s_mage2};\r
+statetype s_mage2 = {MAGE2PIC,20,T_EyeMage,&s_mage1};\r
+\r
+//statetype s_mageattack1 = {MAGEATTACKPIC,20,NULL,&s_mageattack2};\r
+//statetype s_mageattack2 = {MAGEATTACKPIC,-1,T_MageShoot,&s_mageattack3};\r
+statetype s_mageattack3 = {MAGEATTACKPIC,30,NULL,&s_magepause};\r
+\r
+statetype s_mageouch = {MAGEOUCHPIC,10,NULL,&s_mage1};\r
+\r
+statetype s_magedie1 = {MAGEDIE1PIC,20,NULL,&s_magedie2};\r
+statetype s_magedie2 = {MAGEDIE2PIC,0,NULL,&s_magedie2};\r
+\r
+\r
+statetype s_mshot1 = {PSHOT1PIC,8,&T_ShootPlayer,&s_mshot2};\r
+statetype s_mshot2 = {PSHOT2PIC,8,&T_ShootPlayer,&s_mshot1};\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnMage\r
+=\r
+===============\r
+*/\r
+\r
+void SpawnMage (int tilex, int tiley)\r
+{\r
+       SpawnNewObj(tilex,tiley,&s_mage1,PIXRADIUS*35);\r
+       new->obclass = mageobj;\r
+       new->speed = 3072;\r
+       new->flags |= of_shootable;\r
+       new->hitpoints = EasyHitPoints(12);\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= T_EyeMage\r
+=\r
+= **********\r
+= ***NOTE*** This routine controls the thinks for the Eye, Mage, and Succubus.\r
+= **********\r
+=\r
+===============\r
+*/\r
+\r
+void T_EyeMage (objtype *ob)\r
+{\r
+       fixed tempx,tempy;\r
+       unsigned temp_tilex,temp_tiley;\r
+       int angle;\r
+\r
+       eye_delay -= realtics;\r
+       if (eye_delay < 0)\r
+       {\r
+               eye_mode = random(em_dummy);\r
+               eye_delay = (10*60);\r
+       }\r
+\r
+       tempx = player->x;\r
+       tempy = player->y;\r
+       temp_tilex = player->tilex;\r
+       temp_tiley = player->tiley;\r
+\r
+\r
+       switch (eye_mode)\r
+       {\r
+               case em_other1:\r
+               case em_other2:\r
+               case em_other3:\r
+               case em_other4:\r
+                       player->x = ((long)other_x[eye_mode]<<TILESHIFT)+TILEGLOBAL/2;\r
+                       player->y = ((long)other_y[eye_mode]<<TILESHIFT)+TILEGLOBAL/2;\r
+                       player->tilex = other_x[eye_mode];\r
+                       player->tiley = other_y[eye_mode];\r
+               break;\r
+       }\r
+\r
+       if (Chase(ob,true))\r
+               eye_delay = 0;\r
+\r
+       player->x = tempx;\r
+       player->y = tempy;\r
+       player->tilex = temp_tilex;\r
+       player->tiley = temp_tiley;\r
+\r
+\r
+       if (ob->obclass == mageobj)                                     // do the mage shot\r
+       {\r
+               if (!random(10))\r
+                       if (ShootPlayer(ob,mshotobj,MSHOTSPEED,&s_mshot1))\r
+                       {\r
+                               ob->state = &s_mageattack3;\r
+                               ob->ticcount = ob->state->tictime;\r
+                       }\r
+       }\r
+       else\r
+               if (ob->obclass == succubusobj)                         // do the succubus shot\r
+               {\r
+                       angle = AngleNearPlayer(ob);        // make sure angle is correct\r
+                                                                                                                       //          - see AngleNearPlayer\r
+                       if (!random(5) && (angle != -1))   // if correct angle and random # attack\r
+                       {\r
+                               ob->state = &s_succubus_attack1;    // change state to attack\r
+                               ob->ticcount = ob->state->tictime;      // init ticcount - otherwise\r
+                       }                                                                                                       //  object may get hung in a\r
+               }                                                                                                               //  endless state\r
+\r
+               else\r
+               {\r
+                       angle = AngleNearPlayer(ob);                    // do the eye shot\r
+\r
+                       if (!random(2) && (angle != -1))\r
+                       {\r
+                               ob->state = &s_eye_shootplayer_1;\r
+                               ob->ticcount = ob->state->tictime;\r
+                       }\r
+               }\r
+\r
+}\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                       BUNNY\r
+\r
+=============================================================================\r
+*/\r
+\r
+void T_HarmlessBunnyWalk(objtype *ob);\r
+void T_Bunny(objtype *ob);\r
+\r
+extern statetype s_bunny_left1;\r
+extern statetype s_bunny_left2;\r
+extern statetype s_bunny_left3;\r
+extern statetype s_bunny_right1;\r
+extern statetype s_bunny_right2;\r
+extern statetype s_bunny_right3;\r
+extern statetype s_bunny_meta1;\r
+extern statetype s_bunny_meta2;\r
+extern statetype s_bunny_walk1;\r
+extern statetype s_bunny_walk2;\r
+extern statetype s_bunny_attack1;\r
+extern statetype s_bunny_attack2;\r
+extern statetype s_bunny_pause;\r
+extern statetype s_bunny_death1;\r
+extern statetype s_bunny_death2;\r
+extern statetype s_bunny_death3;\r
+\r
+statetype s_bunny_left1 = {BUNNY_LEFT1PIC, 55, NULL, &s_bunny_left2};\r
+statetype s_bunny_left2 = {BUNNY_LEFT1PIC, 10, T_HarmlessBunnyWalk, &s_bunny_left1};\r
+statetype s_bunny_left3 = {BUNNY_LEFT2PIC, 30, NULL, &s_bunny_left1};\r
+\r
+statetype s_bunny_right1 = {BUNNY_RIGHT1PIC, 55, NULL, &s_bunny_right2};\r
+statetype s_bunny_right2 = {BUNNY_RIGHT1PIC, 10, T_HarmlessBunnyWalk, &s_bunny_right1};\r
+statetype s_bunny_right3 = {BUNNY_RIGHT2PIC, 30, NULL, &s_bunny_right1};\r
+\r
+statetype s_bunny_meta1 = {BUNNY_META1PIC, 30, NULL, &s_bunny_meta2};\r
+statetype s_bunny_meta2 = {BUNNY_META2PIC, 30, NULL, &s_bunny_walk1};\r
+\r
+statetype s_bunny_walk1 = {BUNNY_WALK1PIC, 25, T_Bunny, &s_bunny_walk2};\r
+statetype s_bunny_walk2 = {BUNNY_WALK2PIC, 25, T_Bunny, &s_bunny_walk1};\r
+\r
+statetype s_bunny_attack1 = {BUNNY_WALK1PIC, 25, NULL, &s_bunny_attack2};\r
+statetype s_bunny_attack2 = {BUNNY_WALK2PIC, 25, T_DoDamage, &s_bunny_walk1};\r
+\r
+statetype s_bunny_ouch = {BUNNY_OUCHPIC, 30, NULL, &s_bunny_pause};\r
+statetype s_bunny_pause = {BUNNY_WALK1PIC, 50, T_Bunny, &s_bunny_walk2};\r
+\r
+statetype s_bunny_death1 = {BUNNY_OUCHPIC, 40, NULL, &s_bunny_death2};\r
+statetype s_bunny_death2 = {BUNNY_DEATH1PIC, 50, NULL, &s_bunny_death3};\r
+statetype s_bunny_death3 = {BUNNY_DEATH2PIC, 20, NULL, &s_bunny_death3};\r
+\r
+\r
+#define bunny_dir_hop  ob->temp1\r
+#define bunny_delay    ob->temp2\r
+#define LEFTSIDE       0x8             // 1=left 0=right --side showing\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnBunny\r
+=\r
+===============\r
+*/\r
+\r
+void SpawnBunny (int tilex, int tiley)\r
+{\r
+       SpawnNewObj(tilex,tiley,&s_bunny_left1,PIXRADIUS*35);\r
+       new->obclass =  hbunnyobj;\r
+       new->speed = 1947;\r
+       new->temp1 = (random(3))+2;\r
+       new->temp2 = random(30);\r
+       new->flags &= ~of_shootable;\r
+       new->flags |= LEFTSIDE;                         //left side showing}\r
+}\r
+\r
+/*\r
+===============\r
+=\r
+= T_HarmlessBunnyWalk\r
+=\r
+===============\r
+*/\r
+\r
+\r
+void T_HarmlessBunnyWalk(objtype *ob)\r
+{\r
+       int valid_dir[8][2] = {{6,5}, {7,6}, {4,7}, {5,4}, {3,2}, {0,3}, {1,0}, {2,1}};\r
+       long move;\r
+       dirtype player_dir;\r
+       fixed old_x, old_y;\r
+       unsigned old_tilex, old_tiley;\r
+       long old_distance;\r
+\r
+\r
+       ob->temp2 -= realtics;\r
+       if (ob->temp2 <= 0)\r
+       {\r
+               if (CheckHandAttack(ob))\r
+               {\r
+                       ob->temp2 = -1;\r
+                       return;\r
+               }\r
+\r
+               actorat[ob->tilex][ob->tiley] = 0;\r
+               ob->x = ((long)ob->tilex<<TILESHIFT)+TILEGLOBAL/2;\r
+               ob->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;\r
+               ob->distance = TILEGLOBAL;\r
+               ob->state = &s_bunny_meta1;\r
+               ob->ticcount = ob->state->tictime;\r
+               ob->obclass = bunnyobj;\r
+               ob->flags |= of_shootable;\r
+               ob->hitpoints = EasyHitPoints(10);\r
+               ob->dir = nodir;\r
+               ChaseThink(ob,true);                                    // JTR - testing..\r
+               return;\r
+       }\r
+\r
+       // The direction of the player isn't updated so it must be\r
+       // calculated.  This is done so the correct side (left/right)\r
+       // of the bunny will be showed.\r
+\r
+       if ((player->angle > 337) || (player->angle <= 22))\r
+               player_dir = east;\r
+       else\r
+               if (player->angle <= 67)\r
+                       player_dir = northeast;\r
+               else\r
+                       if (player->angle <= 112)\r
+                player_dir = north;\r
+                       else\r
+                if (player->angle <= 157)\r
+                        player_dir = northwest;\r
+                else\r
+                        if (player->angle <= 202)\r
+                                player_dir = west;\r
+                        else\r
+                                if (player->angle <= 247)\r
+                         player_dir = southwest;\r
+                                else\r
+                        if (player->angle <= 292)\r
+                                player_dir = south;\r
+                        else\r
+                                if (player->angle <= 337)\r
+                                        player_dir = southeast;\r
+       if (ob->temp1)\r
+               ob->temp1--;\r
+       else\r
+               ob->temp1 = (random(3))+2;\r
+       if (ob->flags & LEFTSIDE)\r
+       {\r
+               if (ob->temp1)\r
+               {\r
+                       if (valid_dir[player_dir][0] != ob->dir)\r
+                       {\r
+                               ob->dir = valid_dir[player_dir][0];\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       ob->state = &s_bunny_right1;\r
+                       ob->ticcount = ob->state->tictime;\r
+                       ob->flags &= ~LEFTSIDE;\r
+                       ob->dir = valid_dir[player_dir][1];\r
+                       return;\r
+               }\r
+       }\r
+       else\r
+       {\r
+               if (ob->temp1)\r
+               {\r
+                       if (valid_dir[player_dir][1] != ob->dir)\r
+                       {\r
+                               ob->dir = valid_dir[player_dir][1];\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       ob->state = &s_bunny_left1;\r
+                       ob->ticcount = ob->state->tictime;\r
+                       ob->flags |= LEFTSIDE;\r
+                       ob->dir = valid_dir[player_dir][2];\r
+                       return;\r
+               }\r
+       }\r
+\r
+       move = ob->speed*tics;\r
+\r
+       do\r
+       {\r
+               old_distance    = ob->distance;\r
+               old_x           = ob->x;\r
+               old_y           = ob->y;\r
+               old_tilex       = ob->tilex;\r
+               old_tiley       = ob->tiley;\r
+\r
+               MoveObj (ob, move);\r
+\r
+               ob->tilex = ob->x >> TILESHIFT;\r
+               ob->tiley = ob->y >> TILESHIFT;\r
+\r
+               if (ob->tilex == old_tilex && ob->tiley == old_tiley)\r
+               {\r
+                       break;\r
+               }\r
+               else\r
+                       if (actorat[ob->tilex][ob->tiley] == 0)\r
+                       {\r
+                               actorat[old_tilex][old_tiley] = 0;\r
+                               actorat[ob->tilex][ob->tiley] = ob;\r
+                               ob->distance = TILEGLOBAL;\r
+                       }\r
+                       else\r
+                       {\r
+                               ob->distance    = old_distance;\r
+                               ob->x           = old_x;\r
+                               ob->y           = old_y;\r
+                               ob->tilex       = old_tilex;\r
+                               ob->tiley       = old_tiley;\r
+                               return;\r
+                       }\r
+\r
+        } while (0);\r
+\r
+       CalcBounds (ob);\r
+\r
+       if (ob->flags & LEFTSIDE)\r
+               ob->state = &s_bunny_left3;\r
+       else\r
+               ob->state = &s_bunny_right3;\r
+       ob->ticcount = ob->state->tictime;\r
+}\r
+\r
+/*\r
+===============\r
+=\r
+= T_Bunny\r
+=\r
+===============\r
+*/\r
+\r
+void T_Bunny(objtype *ob)\r
+{\r
+       if (Chase (ob, true) || (random(1000)<RANDOM_ATTACK))\r
+       {\r
+               ob->state = &s_bunny_attack1;\r
+               ob->ticcount = ob->state->tictime;\r
+               return;\r
+       }\r
+}\r
diff --git a/16/cawat/C5_ACT3.C b/16/cawat/C5_ACT3.C
new file mode 100644 (file)
index 0000000..79378d4
--- /dev/null
@@ -0,0 +1,1218 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// C3_PLAY.C\r
+\r
+#include "DEF.H"\r
+#pragma hdrstop\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#if 0\r
+#define MSHOTDAMAGE    2\r
+#define MSHOTSPEED     10000\r
+\r
+#define ESHOTDAMAGE    1\r
+#define ESHOTSPEED     5000\r
+\r
+#define SSHOTDAMAGE    3\r
+#define SSHOTSPEED     6500\r
+\r
+#define RANDOM_ATTACK 20\r
+#endif\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+boolean ShootPlayer (objtype *ob, short obclass, short speed, statetype *state);\r
+void T_ShootPlayer(objtype *ob);\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                               RED DEMON\r
+\r
+=============================================================================\r
+*/\r
+\r
+void T_RedDemon (objtype *ob);\r
+void T_RedDemonCheckCnt (objtype *ob);\r
+\r
+extern statetype s_red_demonpause;\r
+\r
+extern statetype s_red_demon1;\r
+extern statetype s_red_demon2;\r
+extern statetype s_red_demon3;\r
+extern statetype s_red_demon4;\r
+\r
+extern statetype s_red_demonattack1;\r
+extern statetype s_red_demonattack2;\r
+extern statetype s_red_demonattack3;\r
+\r
+extern statetype s_red_demonouch;\r
+\r
+extern statetype s_red_demondie1;\r
+extern statetype s_red_demondie2;\r
+extern statetype s_red_demondie3;\r
+extern statetype s_red_demondie4;\r
+\r
+statetype s_red_demonpause = {RED_DEMON1PIC,30,NULL,&s_red_demon2};\r
+\r
+statetype s_red_demon1 = {RED_DEMON1PIC,20,T_RedDemon,&s_red_demon2};\r
+statetype s_red_demon2 = {RED_DEMON2PIC,20,T_RedDemon,&s_red_demon3};\r
+statetype s_red_demon3 = {RED_DEMON3PIC,20,T_RedDemon,&s_red_demon4};\r
+statetype s_red_demon4 = {RED_DEMON4PIC,20,T_RedDemon,&s_red_demon1};\r
+\r
+statetype s_red_demonattack1 = {RED_DEMONATTACK1PIC,20,NULL,&s_red_demonattack2};\r
+statetype s_red_demonattack2 = {RED_DEMONATTACK2PIC,20,NULL,&s_red_demonattack3};\r
+statetype s_red_demonattack3 = {RED_DEMONATTACK3PIC,30,T_DoDamage,&s_red_demon2};\r
+\r
+statetype s_red_demonouch = {RED_DEMONOUCHPIC,30,NULL,&s_red_demon1};\r
+\r
+statetype s_red_demondie1 = {RED_DEMONOUCHPIC,9,NULL,&s_red_demondie2};\r
+statetype s_red_demondie2 = {RED_DEMONDIE1PIC,9,T_RedDemonCheckCnt,&s_red_demondie1};\r
+statetype s_red_demondie3 = {RED_DEMONDIE2PIC,20,NULL,&s_red_demondie4};\r
+statetype s_red_demondie4 = {RED_DEMONDIE3PIC,10,NULL,&s_red_demondie4};\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnRedDemon\r
+=\r
+===============\r
+*/\r
+\r
+void SpawnRedDemon (int tilex, int tiley)\r
+{\r
+       SpawnNewObj(tilex,tiley,&s_red_demon1,PIXRADIUS*35);\r
+       new->obclass = reddemonobj;\r
+       new->speed = 2048;\r
+       new->flags |= of_shootable;\r
+       new->hitpoints = EasyHitPoints(50);\r
+       new->temp1 = 25;\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= T_RedDemon\r
+=\r
+===============\r
+*/\r
+\r
+void T_RedDemon (objtype *ob)\r
+{\r
+       if (Chase (ob,true) || (random(1000)<RANDOM_ATTACK))\r
+       {\r
+               ob->state = &s_red_demonattack1;\r
+               ob->ticcount = ob->state->tictime;\r
+               return;\r
+       }\r
+}\r
+\r
+/*\r
+===============\r
+=\r
+= T_RedDemonCheckCnt\r
+=\r
+===============\r
+*/\r
+\r
+void T_RedDemonCheckCnt (objtype *ob)\r
+{\r
+       ob->temp1--;\r
+       if (!ob->temp1)\r
+       {\r
+               ob->state = &s_red_demondie3;\r
+               ob->ticcount = ob->state->tictime;\r
+       }\r
+}\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                       GRELMINAR\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+void T_Grelminar (objtype *ob);\r
+void T_GrelminarShoot (objtype *ob);\r
+void T_Grelm_DropKey(objtype *ob);\r
+\r
+extern statetype s_grelpause;\r
+\r
+extern statetype s_grel1;\r
+extern statetype s_grel2;\r
+\r
+extern statetype s_grelattack1;\r
+extern statetype s_grelattack2;\r
+extern statetype s_grelattack3;\r
+\r
+extern statetype s_grelouch;\r
+\r
+extern statetype s_greldie1;\r
+extern statetype s_greldie2;\r
+extern statetype s_greldie3;\r
+extern statetype s_greldie4;\r
+extern statetype s_greldie5;\r
+extern         statetype s_greldie5a;\r
+extern statetype s_greldie6;\r
+\r
+\r
+statetype s_grelpause = {GREL1PIC,50,NULL,&s_grel2};\r
+\r
+statetype s_grel1 = {GREL1PIC,20,T_Grelminar,&s_grel2};\r
+statetype s_grel2 = {GREL2PIC,20,T_Grelminar,&s_grel1};\r
+\r
+//statetype s_grelattack1 = {GRELATTACKPIC,20,NULL,&s_grelattack2};\r
+//statetype s_grelattack2 = {GRELATTACKPIC,-1,T_GrelminarShoot,&s_grelattack3};\r
+statetype s_grelattack3 = {GRELATTACKPIC,30,NULL,&s_grelpause};\r
+\r
+statetype s_grelouch = {GRELHITPIC,6,NULL,&s_grel1};\r
+\r
+statetype s_greldie1 = {GRELDIE1PIC,22,NULL,&s_greldie2};\r
+statetype s_greldie2 = {GRELDIE2PIC,22,NULL,&s_greldie3};\r
+statetype s_greldie3 = {GRELDIE3PIC,22,NULL,&s_greldie4};\r
+statetype s_greldie4 = {GRELDIE4PIC,22,NULL,&s_greldie5};\r
+statetype s_greldie5 = {GRELDIE5PIC,22,NULL,&s_greldie5a};\r
+statetype s_greldie5a = {GRELDIE5PIC,1,T_Grelm_DropKey,&s_greldie6};\r
+statetype s_greldie6 = {GRELDIE6PIC,0,NULL,&s_greldie6};\r
+\r
+\r
+extern statetype s_gshot1;\r
+\r
+statetype s_gshot1 = {SKULL_SHOTPIC,8,T_ShootPlayer,&s_gshot1};\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnGrelminar\r
+=\r
+===============\r
+*/\r
+\r
+void SpawnGrelminar (int tilex, int tiley)\r
+{\r
+       unsigned Grel_Hard;\r
+       unsigned DropKey;\r
+\r
+       SpawnNewObj(tilex,tiley,&s_grel1,PIXRADIUS*35);\r
+       new->obclass = grelmobj;\r
+       new->speed = 2048;\r
+       new->flags |= of_shootable;\r
+\r
+       //\r
+       // if Grelminar is to drop a key the info-plane byte to the right\r
+       //              should have a 1 in the highbyte, else he will not drop the key.\r
+       //\r
+       DropKey = *(mapsegs[2]+farmapylookup[tiley]+tilex+1);\r
+       if (DropKey)\r
+               new->temp1 = DropKey>>8;\r
+       else\r
+               new->temp1 = 0;\r
+\r
+       //\r
+       // The info-plane byte below Grelminar will determine how powerful\r
+       //              Grelminar is.  If nothing is there, he is the most powerful.\r
+       //                      -- affected are the hit points and the shot damage.\r
+       //      The hit points are controlled here, the shot damage is controlled\r
+       //      within the spawning of the shot.  See ShootPlayer for more info.\r
+       //\r
+       Grel_Hard = *(mapsegs[2]+farmapylookup[tiley+1]+tilex);\r
+       if (Grel_Hard)\r
+       {\r
+               new->temp2 = Grel_Hard>>8;\r
+               new->hitpoints = EasyHitPoints((new->temp2 * 10));\r
+       }\r
+       else\r
+               new->hitpoints = EasyHitPoints(100);\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= T_Grelminar\r
+=\r
+===============\r
+*/\r
+\r
+void T_Grelminar (objtype *ob)\r
+{\r
+       Chase (ob,false);\r
+\r
+       if (!random(10))\r
+               if (ShootPlayer(ob,gshotobj,ob->temp2,&s_gshot1))\r
+               {\r
+                       ob->state = &s_grelattack3;\r
+                       ob->ticcount = ob->state->tictime;\r
+               }\r
+       if (CheckHandAttack(ob))\r
+               TakeDamage (ob->temp2*3);\r
+\r
+}\r
+\r
+\r
+//=================================\r
+//\r
+// T_Grelm_DropKey\r
+//\r
+//=================================\r
+void T_Grelm_DropKey(objtype *ob)\r
+{\r
+       if (!(ob->temp1))\r
+       {\r
+               ob->state = NULL;\r
+               return;\r
+       }\r
+\r
+       SpawnBonus(ob->tilex,ob->tiley,B_RKEY);\r
+       SD_PlaySound(GRELM_DEADSND);\r
+       ob->temp1 = false;\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                       BAT\r
+\r
+=============================================================================\r
+*/\r
+\r
+void T_Bat (objtype *ob);\r
+void T_BatPast (objtype *ob);\r
+\r
+extern statetype s_bat1;\r
+extern statetype s_bat2;\r
+extern statetype s_bat3;\r
+extern statetype s_bat4;\r
+\r
+extern statetype s_batdie1;\r
+extern statetype s_batdie2;\r
+\r
+\r
+statetype s_bat1 = {BAT1PIC,6,T_Bat,&s_bat2};\r
+statetype s_bat2 = {BAT2PIC,6,T_Bat,&s_bat3};\r
+statetype s_bat3 = {BAT3PIC,6,T_Bat,&s_bat4};\r
+statetype s_bat4 = {BAT4PIC,6,T_Bat,&s_bat1};\r
+\r
+statetype s_batpast = {BAT4PIC,80,T_BatPast,&s_bat1};\r
+\r
+statetype s_batdie1 = {BATDIE1PIC,18,NULL,&s_batdie2};\r
+statetype s_batdie2 = {BATDIE2PIC,18,NULL,NULL};\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnBat\r
+=\r
+===============\r
+*/\r
+\r
+void SpawnBat (int tilex, int tiley)\r
+{\r
+       SpawnNewObj(tilex,tiley,&s_bat1,PIXRADIUS*35);\r
+       new->obclass = batobj;\r
+       new->flags |= of_shootable;\r
+\r
+       new->hitpoints = 1;\r
+       new->speed = 2000;\r
+}\r
+\r
+\r
+/*\r
+==================================\r
+=\r
+= BatChaseThink\r
+=\r
+==================================\r
+*/\r
+\r
+void BatChaseThink (objtype *obj)\r
+{\r
+       int deltax,deltay;\r
+\r
+       deltax=player->tilex - obj->tilex;\r
+       deltay=player->tiley - obj->tiley;\r
+\r
+       if (deltax>0)\r
+               deltax = 2;\r
+       else if (deltax<0)\r
+               deltax = 0;\r
+       else deltax = 1;\r
+\r
+       if (deltay>0)\r
+               deltay = 2;\r
+       else if (deltay<0)\r
+               deltay = 0;\r
+       else deltay = 1;\r
+\r
+       obj->dir = dirtable[deltay*3+deltax];\r
+       if (Walk(obj))\r
+               return;\r
+\r
+       obj->dir = dirtable[3+deltax];\r
+       if (Walk(obj))\r
+               return;\r
+\r
+       obj->dir = dirtable[deltay*3+1];\r
+       if (Walk(obj))\r
+               return;\r
+\r
+       obj->dir = nodir;\r
+}\r
+\r
+\r
+void BatRunThink (objtype *obj)\r
+{\r
+       int deltax,deltay;\r
+\r
+       deltax=player->tilex - obj->tilex;\r
+       deltay=player->tiley - obj->tiley;\r
+\r
+       if (deltax>=0)\r
+               deltax = 0;\r
+       else\r
+               deltax = 2;\r
+\r
+       if (deltay>=0)\r
+               deltay = 0;\r
+       else\r
+               deltay = 2;\r
+\r
+       obj->dir = dirtable[deltay*3+deltax];\r
+       if (Walk(obj))\r
+               return;\r
+\r
+       obj->dir = dirtable[3+deltax];\r
+       if (Walk(obj))\r
+               return;\r
+\r
+       obj->dir = dirtable[deltay*3+1];\r
+       Walk(obj);\r
+}\r
+\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= T_Bat\r
+=\r
+===============\r
+*/\r
+\r
+void T_Bat (objtype *ob)\r
+{\r
+       long move;\r
+       long deltax,deltay,size;\r
+\r
+       move = ob->speed*tics;\r
+       size = (long)ob->size + player->size + move;\r
+\r
+\r
+       do\r
+       {\r
+               deltax = ob->x - player->x;\r
+               deltay = ob->y - player->y;\r
+\r
+               if (deltax <= size && deltax >= -size\r
+               && deltay <= size && deltay >= -size && !ob->temp1)\r
+               {\r
+                       TakeDamage (4);\r
+                       ob->temp1 = 2;\r
+               }\r
+\r
+               if (move < ob->distance)\r
+               {\r
+                       MoveObj (ob,move);\r
+                       break;\r
+               }\r
+\r
+               actorat[ob->tilex][ob->tiley] = 0;      // pick up marker from goal\r
+               if (ob->dir == nodir)\r
+                       ob->dir = north;\r
+\r
+               ob->x = ((long)ob->tilex<<TILESHIFT)+TILEGLOBAL/2;\r
+               ob->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;\r
+               move -= ob->distance;\r
+\r
+               if (ob->temp1)\r
+               {\r
+                       Walk (ob);                              // go straight\r
+                       if (!--ob->temp1)\r
+                       {\r
+                               ob->state = &s_batpast;\r
+                               ob->ticcount = ob->state->tictime;\r
+                       }\r
+               }\r
+               else\r
+                       BatChaseThink (ob);             // head towards player\r
+\r
+               actorat[ob->tilex][ob->tiley] = ob;     // set down a new goal marker\r
+       } while (0);    // just once\r
+       CalcBounds (ob);\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= T_BatPast\r
+=\r
+===============\r
+*/\r
+\r
+void T_BatPast (objtype *ob)\r
+{\r
+       long move;\r
+       long deltax,deltay,size;\r
+\r
+       move = ob->speed*tics;\r
+\r
+       do\r
+       {\r
+               if (move < ob->distance)\r
+               {\r
+                       MoveObj (ob,move);\r
+                       break;\r
+               }\r
+               actorat[ob->tilex][ob->tiley] = 0;      // pick up marker from goal\r
+\r
+               ob->x = ((long)ob->tilex<<TILESHIFT)+TILEGLOBAL/2;\r
+               ob->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;\r
+               move -= ob->distance;\r
+\r
+               BatRunThink (ob);\r
+\r
+               actorat[ob->tilex][ob->tiley] = ob;     // set down a new goal marker\r
+       } while (0);    //(move)\r
+       CalcBounds (ob);\r
+}\r
+\r
+\r
+void T_ChaseThink(objtype *obj);\r
+void T_AwakeThink(objtype *obj);\r
+\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                       GODESS\r
+\r
+=============================================================================\r
+*/\r
+\r
+void T_Godess (objtype *ob);\r
+\r
+\r
+extern statetype s_godesspause;\r
+\r
+extern statetype s_godess_statue1;\r
+extern statetype s_godess_statue2;\r
+\r
+extern statetype s_godess1;\r
+extern statetype s_godess2;\r
+extern statetype s_godess3;\r
+\r
+extern statetype s_godessattack1;\r
+extern statetype s_godessattack2;\r
+extern statetype s_godessattack3;\r
+\r
+extern statetype s_godessouch;\r
+\r
+extern statetype s_godessdie1;\r
+extern statetype s_godessdie2;\r
+extern statetype s_godessdie3;\r
+\r
+\r
+statetype s_godesspause = {GODESS_WALK1PIC,25,NULL,&s_godess2};\r
+\r
+statetype s_godess_statue1 = {GODESS_STATUEPIC,20,T_ChaseThink,&s_godess_statue1};\r
+statetype s_godess_statue2 = {GODESS_STATUEPIC,1,T_AwakeThink,&s_godess1};\r
+\r
+statetype s_godess1 = {GODESS_WALK1PIC,20,T_ChaseThink,&s_godess2};\r
+statetype s_godess2 = {GODESS_WALK2PIC,20,T_ChaseThink,&s_godess3};\r
+statetype s_godess3 = {GODESS_WALK3PIC,20,T_ChaseThink,&s_godess1};\r
+\r
+statetype s_godessattack1 = {GODESS_ATTACK1PIC,10,NULL,&s_godessattack2};//20\r
+statetype s_godessattack2 = {GODESS_ATTACK2PIC,8,NULL,&s_godessattack3};//20\r
+statetype s_godessattack3 = {GODESS_ATTACK3PIC,10,T_DoDamage,&s_godesspause};//30\r
+\r
+statetype s_godessouch = {GODESS_OUCHPIC,10,NULL,&s_godess1};\r
+\r
+statetype s_godessdie1 = {GODESS_DEATH1PIC,65,NULL,&s_godessdie2};\r
+statetype s_godessdie2 = {GODESS_DEATH2PIC,30,NULL,&s_godessdie2};\r
+\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnGodess\r
+=\r
+===============\r
+*/\r
+\r
+void SpawnGodess (int tilex, int tiley)\r
+{\r
+       objtype *ob;\r
+       short current_zombie_delay;\r
+       unsigned tile;\r
+\r
+       SpawnNewObj(tilex,tiley,&s_godess_statue1,PIXRADIUS*35);\r
+       ob = new;\r
+       zombie_mode = zm_wait_for_dark;\r
+\r
+       tile = *(mapsegs[2]+farmapylookup[tiley+1]+tilex);\r
+       if (tile)\r
+               zombie_delay = (tile>>8)*30;\r
+       else\r
+       {\r
+               current_zombie_delay = (2*60)+random(4*60);\r
+               zombie_delay = zombie_base_delay+current_zombie_delay;\r
+               zombie_base_delay += current_zombie_delay;\r
+               if (zombie_base_delay > 8*60)\r
+                       zombie_base_delay = 0;\r
+       }\r
+\r
+       new->obclass = realsolidobj;//godessobj;\r
+       new->speed = 3000;\r
+       new->flags |= of_shootable;\r
+       new->flags &= ~of_tree;\r
+//     new->hitpoints = EasyHitPoints(10);\r
+}\r
+\r
+\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                       ANT\r
+\r
+=============================================================================\r
+*/\r
+\r
+void T_Ant(objtype *ob);\r
+\r
+statetype s_ant_wait = {ANT_EGG1PIC,10,T_ChaseThink,&s_ant_wait};\r
+\r
+statetype s_ant_egg = {ANT_EGG2PIC,45,T_AwakeThink,&s_ant_walk1};\r
+\r
+statetype s_ant_walk1 = {ANT_WALK1PIC,20,T_ChaseThink,&s_ant_walk2};\r
+statetype s_ant_walk2 = {ANT_WALK2PIC,20,T_ChaseThink,&s_ant_walk3};\r
+statetype s_ant_walk3 = {ANT_WALK3PIC,20,T_ChaseThink,&s_ant_walk1};\r
+\r
+statetype s_ant_attack1 = {ANT_ATTACKPIC,20,NULL,&s_ant_pause};\r
+\r
+statetype s_ant_pause  = {ANT_WALK2PIC,15,T_DoDamage,&s_ant_walk1};\r
+\r
+statetype s_ant_ouch = {ANT_WALK1PIC,15,NULL,&s_ant_walk1};\r
+\r
+statetype s_ant_die1 = {ANT_DEATH1PIC,40,NULL,&s_ant_die2};\r
+statetype s_ant_die2 = {ANT_DEATH2PIC,10,NULL,&s_ant_die3};\r
+statetype s_ant_die3 = {ANT_DEATH3PIC,10,NULL,&s_ant_die2};\r
+\r
+#define ant_mode       ob->temp1\r
+#define ant_delay      ob->temp2\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnAnt\r
+=\r
+===============\r
+*/\r
+void SpawnAnt(int tilex, int tiley)\r
+{\r
+       objtype *ob;\r
+       unsigned tile;\r
+       SpawnNewObj(tilex,tiley,&s_ant_wait,PIXRADIUS*35);\r
+       ob = new;\r
+\r
+       tile = *(mapsegs[2]+farmapylookup[tiley+1]+tilex);\r
+       if (tile)\r
+               ant_delay = (tile>>8)*30;\r
+       else\r
+               ant_delay = 2*60+random(5*60);\r
+\r
+       ant_mode = zm_wait_for_dark;\r
+\r
+       new->obclass = antobj;\r
+       new->speed = 1900;\r
+       new->flags &= ~of_shootable;\r
+       new->hitpoints = EasyHitPoints(15);\r
+}\r
+\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                       ZOMBIE\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern statetype s_zombie_rise1;\r
+extern statetype s_zombie_rise2;\r
+extern statetype s_zombie_rise3;\r
+extern statetype s_zombie_rise4;\r
+\r
+extern statetype s_zombie_alive1;\r
+extern statetype s_zombie_alive2;\r
+extern statetype s_zombie_alive3;\r
+\r
+//extern statetype s_zombie_attack1;\r
+\r
+extern statetype s_zombie_death1;\r
+extern statetype s_zombie_death2;\r
+extern statetype s_zombie_death3;\r
+\r
+void T_Zombie (objtype *ob);\r
+void T_ZombieRisen(objtype *obj);\r
+\r
+statetype s_zombie_risen = {ZOMB_WALK3PIC,1,T_AwakeThink,&s_zombie_alive1};\r
+\r
+statetype s_zombie_pause = {ZOMB_WALK1PIC,20,NULL,&s_zombie_alive1};\r
+\r
+statetype s_zombie_inground = {0,13,T_ChaseThink,&s_zombie_inground};\r
+\r
+statetype s_zombie_rise1 = {ZOMB_APPEAR1PIC,24,NULL,&s_zombie_rise2};\r
+statetype s_zombie_rise2 = {ZOMB_APPEAR2PIC,24,NULL,&s_zombie_rise3};\r
+statetype s_zombie_rise3 = {ZOMB_APPEAR3PIC,24,NULL,&s_zombie_rise4};\r
+statetype s_zombie_rise4 = {ZOMB_APPEAR4PIC,24,NULL,&s_zombie_risen};\r
+\r
+statetype s_zombie_alive1 = {ZOMB_WALK1PIC,13,T_ChaseThink,&s_zombie_alive2};\r
+statetype s_zombie_alive2 = {ZOMB_WALK2PIC,13,T_ChaseThink,&s_zombie_alive3};\r
+statetype s_zombie_alive3 = {ZOMB_WALK3PIC,13,T_ChaseThink,&s_zombie_alive1};\r
+\r
+statetype s_zombie_death1 = {ZOMB_DIE1PIC,16,NULL,&s_zombie_death2};\r
+statetype s_zombie_death2 = {ZOMB_DIE2PIC,16,NULL,&s_zombie_death3};\r
+statetype s_zombie_death3 = {ZOMB_DIE3PIC,16,NULL,&s_zombie_death3};\r
+\r
+statetype s_zombie_attack  = {ZOMB_ATTACKPIC,15,T_DoDamage,&s_zombie_pause};\r
+//statetype s_zombie_attack1 = {ZOMB_ATTACKPIC,15,NULL,&s_zombie_pause};\r
+\r
+statetype s_zombie_ouch = {ZOMB_OUCHPIC,15,NULL,&s_zombie_alive1};\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+// SpawnZombie()\r
+//--------------------------------------------------------------------------\r
+void SpawnZombie (int tilex, int tiley)\r
+{\r
+       objtype *ob;\r
+       short current_zombie_delay;\r
+       unsigned tile;\r
+\r
+       SpawnNewObj(tilex,tiley,&s_zombie_inground,35*PIXRADIUS);\r
+       ob = new;\r
+       zombie_mode = zm_wait_for_dark;\r
+\r
+       tile = *(mapsegs[2]+farmapylookup[tiley+1]+tilex);\r
+       if (tile)\r
+               zombie_delay = (tile>>8)*30;\r
+       else\r
+       {\r
+               current_zombie_delay = (2*60)+random(4*60);\r
+               zombie_delay = zombie_base_delay+current_zombie_delay;\r
+               zombie_base_delay += current_zombie_delay;\r
+               if (zombie_base_delay > 8*60)\r
+                       zombie_base_delay = 0;\r
+       }\r
+\r
+       new->speed = 2500;\r
+       new->obclass = zombieobj;\r
+       new->hitpoints = EasyHitPoints(8);\r
+       new->active = yes;\r
+       new->flags &= ~of_shootable;\r
+}\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                       TREE\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern statetype s_tree_pause;\r
+extern statetype s_tree_idle;\r
+extern statetype s_tree_awakening1;\r
+extern statetype s_tree_awakening2;\r
+extern statetype s_tree_walk1;\r
+extern statetype s_tree_walk2;\r
+extern statetype s_tree_walk3;\r
+extern statetype s_tree_death1;\r
+extern statetype s_tree_death2;\r
+extern statetype s_tree_death3;\r
+extern statetype s_tree_death4;\r
+extern statetype s_tree_death5;\r
+extern statetype s_tree_attack1;\r
+extern statetype s_tree_attack2;\r
+extern statetype s_tree_attack3;\r
+extern statetype s_tree_ouch;\r
+\r
+void T_Tree (objtype *ob);\r
+void T_DeathThink(objtype *ob);\r
+\r
+statetype s_tree_pause = {TREE_WALK1PIC,25,NULL,&s_tree_walk2};\r
+\r
+statetype s_tree_idle = {TREE_IDLEPIC,13,T_ChaseThink,&s_tree_idle};\r
+\r
+statetype s_tree_awakening1 = {TREE_AWAKENINGPIC,1,T_AwakeThink,&s_tree_awakening2};\r
+statetype s_tree_awakening2 = {TREE_AWAKENINGPIC,50,NULL,&s_tree_walk1};\r
+\r
+statetype s_tree_walk1 = {TREE_WALK1PIC,13,T_ChaseThink,&s_tree_walk2};\r
+statetype s_tree_walk2 = {TREE_WALK2PIC,13,T_ChaseThink,&s_tree_walk1};\r
+\r
+statetype s_tree_death1 = {TREE_DEATH1PIC,45,NULL,&s_tree_death2};\r
+statetype s_tree_death2 = {TREE_DEATH2PIC,25,NULL,&s_tree_death3};\r
+statetype s_tree_death3 = {TREE_DEATH1PIC,15,T_DeathThink,&s_tree_death4};\r
+statetype s_tree_death4 = {TREE_DEATH2PIC,15,T_DeathThink,&s_tree_death5};\r
+statetype s_tree_death5 = {TREE_DEATH3PIC,15,T_DeathThink,&s_tree_death3};\r
+\r
+statetype s_tree_attack1  = {TREE_ATTACK1PIC,15,T_DoDamage,&s_tree_attack2};\r
+statetype s_tree_attack2  = {TREE_ATTACK2PIC,15,T_DoDamage,&s_tree_attack3};\r
+statetype s_tree_attack3  = {TREE_ATTACK3PIC,15,T_DoDamage,&s_tree_pause};\r
+\r
+statetype s_tree_ouch = {TREE_AWAKENINGPIC,15,NULL,&s_tree_walk1};\r
+\r
+\r
+#define zombie_mode    ob->temp1\r
+#define zombie_delay   ob->temp2\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+// SpawnTree()\r
+//--------------------------------------------------------------------------\r
+void SpawnTree(int tilex, int tiley)\r
+{\r
+       objtype *ob;\r
+       short current_zombie_delay;\r
+       unsigned tile;\r
+\r
+       SpawnNewObj(tilex,tiley,&s_tree_idle,35*PIXRADIUS);\r
+       ob = new;\r
+       zombie_mode = zm_wait_for_dark;\r
+\r
+       tile = *(mapsegs[2]+farmapylookup[tiley+1]+tilex);\r
+       if (tile)\r
+               zombie_delay = (tile>>8)*30;\r
+       else\r
+       {\r
+               current_zombie_delay = (2*60)+random(4*60);\r
+               zombie_delay = zombie_base_delay+current_zombie_delay;\r
+               zombie_base_delay += current_zombie_delay;\r
+               if (zombie_base_delay > 8*60)\r
+                       zombie_base_delay = 0;\r
+       }\r
+\r
+       new->speed = 2500;\r
+       new->obclass = realsolidobj;\r
+//     new->hitpoints = EasyHitPoints(12);\r
+       new->active = yes;\r
+       new->flags |= of_shootable;\r
+       new->flags |= of_tree;\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// T_DeathThink()\r
+//--------------------------------------------------------------------------\r
+void T_DeathThink(objtype *ob)\r
+{\r
+       char num;\r
+\r
+       if ((ob->ticcount - realtics) <= 0)\r
+       {\r
+               num = random(2);\r
+               switch (ob->temp1)\r
+               {\r
+                       case 3:\r
+                               if (num)\r
+                                       ob->state = &s_tree_death4;\r
+                               else\r
+                                       ob->state = &s_tree_death5;\r
+                               ob->temp1++;\r
+                       break;\r
+\r
+                       case 4:\r
+                               if (num)\r
+                                       ob->state = &s_tree_death3;\r
+                               else\r
+                                       ob->state = &s_tree_death5;\r
+                               ob->temp1++;\r
+                       break;\r
+\r
+                       case 5:\r
+                               if (num)\r
+                                       ob->state = &s_tree_death3;\r
+                               else\r
+                                       ob->state = &s_tree_death4;\r
+                               ob->temp1 = 3;\r
+                       break;\r
+               }\r
+               ob->ticcount = ob->state->tictime;\r
+       }\r
+\r
+\r
+\r
+       if (CheckHandAttack(ob))\r
+               TakeDamage (1);\r
+}\r
+\r
+\r
+//////////////////////////////////////////////////////////////////////////\r
+//\r
+//     GENERAL THINK ROUTINES USED BY THE ZOMBIE, TREE, ANT, AND GODESS\r
+//             ----trying to cut down on the code size----\r
+//\r
+//////////////////////////////////////////////////////////////////////////\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+// T_ChaseThink()\r
+//--------------------------------------------------------------------------\r
+void T_ChaseThink(objtype *ob)\r
+{\r
+       switch (zombie_mode)\r
+       {\r
+               case zm_wait_for_dark:\r
+#if 0\r
+                       if (gamestate.mapon == 0)\r
+                       {\r
+                               if (BGFLAGS & BGF_NIGHT)\r
+                                       zombie_mode = zm_wait_to_rise;\r
+                       }\r
+                       else\r
+#endif\r
+                               zombie_mode = zm_wait_to_rise;\r
+               break;\r
+\r
+               case zm_wait_to_rise:\r
+                       if (zombie_delay < 0)\r
+                       {\r
+                               if ((ob->tilex == player->tilex) && (ob->tiley == player->tiley))\r
+                                       break;\r
+                               if (CheckHandAttack(ob))\r
+                                       break;\r
+\r
+                               ob->active = always;\r
+                               switch (ob->obclass)\r
+                               {\r
+                                       case zombieobj:\r
+                                               ob->state = &s_zombie_rise1;\r
+                                       break;\r
+\r
+                                       case antobj:\r
+                                               ob->state = &s_ant_egg;\r
+                                       break;\r
+\r
+                                       case realsolidobj:      //tree and godess\r
+                                               if (ob->flags & of_tree)\r
+                                                       ob->state = &s_tree_awakening1;\r
+                                               else\r
+                                                       ob->state = &s_godess_statue2;\r
+                                       break;\r
+                               }\r
+                               ob->ticcount = ob->state->tictime;\r
+                               zombie_mode = zm_active;\r
+                       }\r
+                       else\r
+                               zombie_delay -= tics;\r
+\r
+               break;\r
+\r
+               case zm_active:\r
+                       if (Chase (ob,true) || (random(1000)<RANDOM_ATTACK))\r
+                       {\r
+                               switch (ob->obclass)\r
+                               {\r
+                                       case zombieobj:\r
+                                               ob->state = &s_zombie_attack;\r
+                                       break;\r
+\r
+                                       case antobj:\r
+                                               ob->state = &s_ant_attack1;\r
+                                       break;\r
+\r
+                                       case treeobj:\r
+                                               ob->state = &s_tree_attack1;\r
+                                       break;\r
+\r
+                                       case godessobj:\r
+                                               ob->state = &s_godessattack1;\r
+                                       break;\r
+                               }\r
+                               ob->ticcount = ob->state->tictime;\r
+                               return;\r
+                       }\r
+               break;\r
+       }\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// T_AwakeThink()\r
+//--------------------------------------------------------------------------\r
+void T_AwakeThink(objtype *obj)\r
+{\r
+       if (obj->obclass == realsolidobj)\r
+       {\r
+               if (obj->flags & of_tree)\r
+                       obj->obclass = treeobj;\r
+               else\r
+                       obj->obclass = godessobj;\r
+               obj->hitpoints = EasyHitPoints(12);\r
+       }\r
+       else\r
+               obj->flags |= of_shootable;\r
+}\r
+\r
+\r
+\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+// ShootPlayer()\r
+//--------------------------------------------------------------------------\r
+boolean ShootPlayer(objtype *ob, short obclass, short speed, statetype *state)\r
+{\r
+       int angle = AngleNearPlayer(ob);\r
+\r
+       if (angle == -1)\r
+               return(false);\r
+\r
+       DSpawnNewObjFrac (ob->x,ob->y,state,PIXRADIUS*35);\r
+       new->obclass = obclass;\r
+       new->active = always;\r
+       new->angle = angle;\r
+\r
+       //\r
+       //      If the shot is Grelminar's, then determine the power of the shot.\r
+       //      The shot speed is hard-wired as 10000.  But the shot power is\r
+       //              determined by speed.  Speed now contains "Grelminar's level of\r
+       //              hardness" and this is multiplied by 3 to get the shot power.\r
+       //\r
+       if (obclass == gshotobj)\r
+       {\r
+               new->speed = 10000;\r
+               new->temp1 = speed*3;\r
+       }\r
+       else\r
+               new->speed = speed;\r
+\r
+\r
+       return(true);\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// T_ShootPlayer()\r
+//--------------------------------------------------------------------------\r
+void T_ShootPlayer(objtype *ob)\r
+{\r
+       objtype *check;\r
+       long xmove,ymove,speed;\r
+\r
+       speed = ob->speed*tics;\r
+\r
+       xmove = FixedByFrac(speed,costable[ob->angle]);\r
+       ymove = -FixedByFrac(speed,sintable[ob->angle]);\r
+\r
+       if (ShotClipMove(ob,xmove,ymove))\r
+       {\r
+               ob->state = &s_pshot_exp1;\r
+               ob->ticcount = ob->state->tictime;\r
+               return;\r
+       }\r
+\r
+       ob->tilex = ob->x >> TILESHIFT;\r
+       ob->tiley = ob->y >> TILESHIFT;\r
+\r
+\r
+// check for collision with wall\r
+//\r
+       if (tilemap[ob->tilex][ob->tiley])\r
+       {\r
+               SD_PlaySound (SHOOTWALLSND);\r
+               ob->state = &s_pshot_exp1;\r
+               ob->ticcount = s_pshot_exp1.tictime;\r
+               return;\r
+       }\r
+\r
+\r
+\r
+// check for collision with player\r
+//\r
+       if ( ob->xl <= player->xh\r
+       && ob->xh >= player->xl\r
+       && ob->yl <= player->yh\r
+       && ob->yh >= player->yl)\r
+       {\r
+               switch (ob->obclass)\r
+               {\r
+                       case eshotobj:\r
+                               TakeDamage (ESHOTDAMAGE);\r
+                       break;\r
+\r
+                       case mshotobj:\r
+                               TakeDamage (MSHOTDAMAGE);\r
+                       break;\r
+\r
+                       case gshotobj:\r
+                               TakeDamage (ob->temp1);         // the damage of Grelminar's shot -\r
+                       break;                                                          //   see Grelminar's spawning\r
+\r
+                       case sshotobj:\r
+                               TakeDamage(SSHOTDAMAGE);\r
+                       break;\r
+\r
+                       case dshotobj:\r
+                               TakeDamage(7);\r
+                       break;\r
+               }\r
+               ob->state = NULL;\r
+               return;\r
+       }\r
+\r
+// check for collision with other solid and realsolid objects.\r
+//  Great terminology!! -- solid objects really aren't solid\r
+//                      -- realsolid objects ARE solid\r
+//     if ((actorat[ob->tilex][ob->tiley]) && (actorat[ob->tilex][ob->tiley]->obclass != ob->obclass))\r
+       if (((actorat[ob->tilex][ob->tiley]->obclass == realsolidobj) ||\r
+                (actorat[ob->tilex][ob->tiley]->obclass == solidobj)) &&\r
+                (actorat[ob->tilex][ob->tiley]->flags & of_shootable))\r
+       {\r
+                       ob->state = &s_pshot_exp1;\r
+                       ob->ticcount = s_pshot_exp1.tictime;\r
+                       return;\r
+       }\r
+\r
+\r
+// check for collision with player\r
+//\r
+       for (check = player->next; check; check=check->next)\r
+               if ((ob->flags & of_shootable) && ob->obclass != mageobj\r
+               && ob->xl <= check->xh\r
+               && ob->xh >= check->xl\r
+               && ob->yl <= check->yh\r
+               && ob->yh >= check->yl)\r
+               {\r
+                       switch (ob->obclass)\r
+                       {\r
+                               case eshotobj:\r
+                                       ShootActor (check,ESHOTDAMAGE);\r
+                               break;\r
+\r
+                               case mshotobj:\r
+                                       ShootActor (check,MSHOTDAMAGE);\r
+                               break;\r
+\r
+                               case gshotobj:\r
+                                       ShootActor (check,25);          //NOLAN--check on me!!!!!!!\r
+                               break;\r
+\r
+                               case pshotobj:\r
+                                       ShootActor (check,25);\r
+                               break;\r
+\r
+                               case sshotobj:\r
+                                       ShootActor(check, SSHOTDAMAGE);\r
+                               break;\r
+\r
+                               case dshotobj:\r
+                                       ShootActor(check, 7);\r
+                               break;\r
+                       }\r
+                       ob->state = &s_pshot_exp1;\r
+                       ob->ticcount = s_pshot_exp1.tictime;\r
+                       return;\r
+               }\r
+}\r
+\r
+//-------------------------------------------------------------------------\r
+// AngleNearPlayer()\r
+//-------------------------------------------------------------------------\r
+int AngleNearPlayer(objtype *ob)\r
+{\r
+       int angle=-1;\r
+       int xdiff = ob->tilex-player->tilex;\r
+       int ydiff = ob->tiley-player->tiley;\r
+\r
+       if (ob->tiley == player->tiley)\r
+       {\r
+               if (ob->tilex < player->tilex)\r
+                       angle = 0;\r
+               else\r
+                       angle = 180;\r
+       }\r
+       else\r
+       if (ob->tilex == player->tilex)\r
+       {\r
+               if (ob->tiley < player->tiley)\r
+                       angle = 270;\r
+               else\r
+                       angle = 90;\r
+       }\r
+       else\r
+       if (xdiff == ydiff)\r
+               if (ob->tilex < player->tilex)\r
+               {\r
+                       if (ob->tiley < player->tiley)\r
+                               angle = 315;\r
+                       else\r
+                               angle = 45;\r
+               }\r
+               else\r
+               {\r
+                       if (ob->tiley < player->tiley)\r
+                               angle = 225;\r
+                       else\r
+                               angle = 135;\r
+               }\r
+\r
+       return(angle);\r
+}\r
diff --git a/16/cawat/C5_ACT4.C b/16/cawat/C5_ACT4.C
new file mode 100644 (file)
index 0000000..676e0b7
--- /dev/null
@@ -0,0 +1,414 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// C4_PLAY.C\r
+\r
+#include "DEF.H"\r
+#pragma hdrstop\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+\r
+//-------------------------------------------------------------------------\r
+//\r
+//                             ARCH OBJECTS\r
+//\r
+//-------------------------------------------------------------------------\r
+\r
+\r
+\r
+//-------------------------------------------------------------------------\r
+//                             ARCH\r
+//-------------------------------------------------------------------------\r
+\r
+void SpawnArch(int tilex, int tiley, int num);\r
+\r
+extern statetype s_arch_1;\r
+extern statetype s_arch_2;\r
+extern statetype s_arch_3;\r
+extern statetype s_arch_4;\r
+extern statetype s_arch_5;\r
+extern statetype s_arch_6;\r
+extern statetype s_arch_7;\r
+extern statetype s_arch_8;\r
+extern statetype s_arch_9;\r
+extern statetype s_arch_10;\r
+extern statetype s_arch_11;\r
+extern statetype s_arch_12;\r
+extern statetype s_arch_13;\r
+\r
+statetype s_arch_1 = {ARCH1PIC, 20, NULL, &s_arch_1};\r
+statetype s_arch_2 = {ARCH2PIC, 20, NULL, &s_arch_2};\r
+statetype s_arch_3 = {ARCH3PIC, 20, NULL, &s_arch_3};\r
+statetype s_arch_4 = {ARCH4PIC, 20, NULL, &s_arch_4};\r
+statetype s_arch_5 = {ARCH5PIC, 20, NULL, &s_arch_5};\r
+statetype s_arch_6 = {ARCH6PIC, 20, NULL, &s_arch_6};\r
+statetype s_arch_7 = {ARCH7PIC, 20, NULL, &s_arch_7};\r
+statetype s_arch_8 = {ARCH8PIC, 20, NULL, &s_arch_8};\r
+statetype s_arch_9 = {ARCH9PIC, 20, NULL, &s_arch_9};\r
+statetype s_arch_10 = {ARCH10PIC, 20, NULL, &s_arch_10};\r
+statetype s_arch_11 = {ARCH11PIC, 20, NULL, &s_arch_11};\r
+statetype s_arch_12 = {ARCH12PIC, 20, NULL, &s_arch_12};\r
+statetype s_arch_13 = {ARCH13PIC, 20, NULL, &s_arch_13};\r
+\r
+void SpawnArch (int tilex, int tiley, int num)\r
+{\r
+       statetype       *objstate;\r
+\r
+\r
+       switch (num)\r
+       {\r
+               case 1:\r
+                       objstate = &s_arch_1;\r
+               break;\r
+               case 2:\r
+                       objstate = &s_arch_2;\r
+               break;\r
+               case 3:\r
+                       objstate = &s_arch_3;\r
+               break;\r
+               case 4:\r
+                       objstate = &s_arch_4;\r
+               break;\r
+               case 5:\r
+                       objstate = &s_arch_5;\r
+               break;\r
+               case 6:\r
+                       objstate = &s_arch_6;\r
+               break;\r
+               case 7:\r
+                       objstate = &s_arch_7;\r
+               break;\r
+               case 8:\r
+                       objstate = &s_arch_8;\r
+               break;\r
+               case 9:\r
+                       objstate = &s_arch_9;\r
+               break;\r
+               case 10:\r
+                       objstate = &s_arch_10;\r
+               break;\r
+               case 11:\r
+                       objstate = &s_arch_11;\r
+               break;\r
+               case 12:\r
+                       objstate = &s_arch_12;\r
+               break;\r
+               case 13:\r
+                       objstate = &s_arch_13;\r
+               break;\r
+       }\r
+       ASpawnNewObj(tilex,tiley,objstate,PIXRADIUS*35);\r
+       new->obclass = solidobj;\r
+       new->flags &= ~of_shootable;\r
+}\r
+\r
+\r
+//-------------------------------------------------------------------------\r
+//\r
+//                             MISC OBJECTS\r
+//\r
+//-------------------------------------------------------------------------\r
+\r
+\r
+//-------------------------------------------------------------------------\r
+//             COLUMN, SULPHUR GAS HOLE, FIRE POT, FOUNTAIN\r
+//-------------------------------------------------------------------------\r
+\r
+\r
+void SpawnMiscObjects(int tilex, int tiley, int num);\r
+\r
+extern statetype s_column;\r
+extern statetype s_sulphur_gas_1;\r
+extern statetype s_sulphur_gas_2;\r
+extern statetype s_sulphur_gas_3;\r
+extern statetype s_fire_pot_1;\r
+extern statetype s_fire_pot_2;\r
+extern statetype s_fountain;\r
+\r
+statetype s_column = {COLUMNPIC, 20, NULL, &s_column};\r
+statetype s_sulphur_gas_1 = {SULPHUR_GAS_1PIC, 20, NULL, &s_sulphur_gas_2};\r
+statetype s_sulphur_gas_2 = {SULPHUR_GAS_2PIC, 20, NULL, &s_sulphur_gas_3};\r
+statetype s_sulphur_gas_3 = {SULPHUR_GAS_3PIC, 20, NULL, &s_sulphur_gas_1};\r
+statetype s_fire_pot_1 = {FIRE_POT_1PIC, 20, NULL, &s_fire_pot_2};\r
+statetype s_fire_pot_2 = {FIRE_POT_2PIC, 20, NULL, &s_fire_pot_1};\r
+statetype s_fountain = {WFOUNTAINPIC, 20, NULL, &s_fountain};\r
+\r
+\r
+void SpawnMiscObjects(int tilex, int tiley, int num)\r
+{\r
+       statetype       *objstate;\r
+\r
+       switch (num)\r
+       {\r
+               case 1:\r
+                       objstate = &s_column;\r
+               break;\r
+\r
+               case 2:\r
+                       objstate = &s_sulphur_gas_1;\r
+               break;\r
+\r
+               case 3:\r
+                       objstate = &s_fire_pot_1;\r
+               break;\r
+\r
+               case 4:\r
+                       objstate = &s_fountain;\r
+               break;\r
+       }\r
+\r
+       SpawnNewObj(tilex,tiley,objstate,PIXRADIUS*35);\r
+       new->obclass = realsolidobj;\r
+       if (num == 2)\r
+               new->flags &= ~of_shootable;\r
+       else\r
+               new->flags |= of_shootable;\r
+}\r
+\r
+\r
+\r
+\r
+\r
+\r
+#if 0\r
+void SpawnColumn(int tilex, int tiley);\r
+\r
+extern statetype s_column;\r
+statetype s_column = {COLUMNPIC, 20, NULL, &s_column};\r
+\r
+void SpawnColumn(int tilex, int tiley)\r
+{\r
+       SpawnNewObj(tilex,tiley,&s_column,PIXRADIUS*35);\r
+       new->obclass = realsolidobj;\r
+       new->flags |= of_shootable;\r
+}\r
+\r
+\r
+//-------------------------------------------------------------------------\r
+//                             SULPHUR GAS\r
+//-------------------------------------------------------------------------\r
+\r
+void SpawnSulphurGas(int tilex, int tiley);\r
+\r
+extern statetype s_sulphur_gas_1;\r
+extern statetype s_sulphur_gas_2;\r
+extern statetype s_sulphur_gas_3;\r
+\r
+statetype s_sulphur_gas_1 = {SULPHUR_GAS_1PIC, 20, NULL, &s_sulphur_gas_2};\r
+statetype s_sulphur_gas_2 = {SULPHUR_GAS_2PIC, 20, NULL, &s_sulphur_gas_3};\r
+statetype s_sulphur_gas_3 = {SULPHUR_GAS_3PIC, 20, NULL, &s_sulphur_gas_1};\r
+\r
+void SpawnSulphurGas(int tilex, int tiley)\r
+{\r
+       SpawnNewObj(tilex,tiley,&s_sulphur_gas_1,PIXRADIUS*35);\r
+       new->obclass = realsolidobj;\r
+       new->flags &= ~of_shootable;\r
+}\r
+\r
+\r
+//-------------------------------------------------------------------------\r
+//                             FIRE POT\r
+//-------------------------------------------------------------------------\r
+\r
+void SpawnFirePot(int tilex, int tiley);\r
+\r
+extern statetype s_fire_pot_1;\r
+extern statetype s_fire_pot_2;\r
+\r
+statetype s_fire_pot_1 = {FIRE_POT_1PIC, 20, NULL, &s_fire_pot_2};\r
+statetype s_fire_pot_2 = {FIRE_POT_2PIC, 20, NULL, &s_fire_pot_1};\r
+\r
+void SpawnFirePot(int tilex, int tiley)\r
+{\r
+       SpawnNewObj(tilex,tiley,&s_fire_pot_1,PIXRADIUS*35);\r
+       new->obclass = realsolidobj;\r
+       new->flags |= of_shootable;\r
+\r
+}\r
+\r
+//------------------------------------------------------------------------\r
+//                             FOUNTAIN\r
+//------------------------------------------------------------------------\r
+\r
+void SpawnFountain(int tilex, int tiley);\r
+\r
+extern statetype s_fountain;\r
+statetype s_fountain = {WFOUNTAINPIC, 20, NULL, &s_fountain};\r
+\r
+void SpawnFountain(int tilex, int tiley)\r
+{\r
+       SpawnNewObj(tilex,tiley,&s_fountain,PIXRADIUS*35);\r
+       new->obclass = realsolidobj;\r
+       new->flags |= of_shootable;\r
+}\r
+\r
+#endif\r
+\r
+\r
+//------------------------------------------------------------------------\r
+//                             FORCE FIELD\r
+//------------------------------------------------------------------------\r
+\r
+void SpawnForceField(int tilex, int tiley);\r
+void T_ForceField(objtype *ob);\r
+void T_ForceFieldRemove(objtype *ob);\r
+\r
+extern statetype s_force_field_1;\r
+extern statetype s_force_field_2;\r
+extern statetype s_force_field_3;\r
+extern statetype s_force_field_4;\r
+extern statetype s_force_field_die1;\r
+\r
+statetype s_force_field_1 = {FORCE_FIELD_1PIC, 10, T_ForceField, &s_force_field_2};\r
+statetype s_force_field_2 = {FORCE_FIELD_2PIC, 10, T_ForceField, &s_force_field_3};\r
+statetype s_force_field_3 = {FORCE_FIELD_3PIC, 10, T_ForceField, &s_force_field_4};\r
+statetype s_force_field_4 = {FORCE_FIELD_4PIC, 10, T_ForceField, &s_force_field_1};\r
+\r
+statetype s_force_field_die = {0,0,T_ForceFieldRemove,&s_force_field_die1};\r
+statetype s_force_field_die1 = {0,0,NULL,NULL};\r
+\r
+void SpawnForceField(int tilex, int tiley)\r
+{\r
+       SpawnNewObj(tilex,tiley,&s_force_field_1,PIXRADIUS*35);\r
+       new->obclass = solidobj;\r
+       new->hitpoints = EasyHitPoints(20);\r
+       new->flags |= of_forcefield;            //sets bit 7 :: makes it nonsolid, but also detectable\r
+                                                                                               //              without adding another object type!\r
+       new->flags |= of_shootable;\r
+}\r
+\r
+void T_ForceField(objtype *ob)\r
+{\r
+       long move,deltax,deltay,size;\r
+\r
+       size = (long)ob->size + player->size;\r
+\r
+       deltax = ob->x - player->x;\r
+       deltay = ob->y - player->y;\r
+\r
+       if (deltax <= size && deltax >= -size\r
+       && deltay <= size && deltay >= -size)\r
+               TakeDamage (20);\r
+\r
+}\r
+\r
+void T_ForceFieldRemove(objtype *ob)\r
+{\r
+       actorat[ob->tilex][ob->tiley] = 0;\r
+}\r
+\r
+\r
+//------------------------------------------------------------------------\r
+//                     SKELETON HANGING FROM CEILING\r
+//------------------------------------------------------------------------\r
+\r
+void SpawnSkeletonHanging(int tilex, int tiley);\r
+void T_SkelHangThink(objtype *ob);\r
+\r
+extern statetype s_skeleton_hanging;\r
+statetype s_skeleton_hanging = {SKEL_HANGPIC, 20, T_SkelHangThink, &s_skeleton_hanging};\r
+\r
+void SpawnSkeletonHanging(int tilex, int tiley)\r
+{\r
+       unsigned tile;\r
+\r
+       SpawnNewObj(tilex,tiley,&s_skeleton_hanging,PIXRADIUS*35);\r
+       new->obclass = solidobj;\r
+\r
+       tile = *(mapsegs[2]+farmapylookup[tiley+1]+tilex);\r
+       if (tile)\r
+               new->temp1 = (tile>>8)*30;\r
+       else\r
+               new->temp1 = (3*60)+random(4*60);\r
+\r
+       new->flags |= of_shootable;\r
+}\r
+\r
+void T_SkelHangThink(objtype *ob)\r
+{\r
+       ob->temp1 -= realtics;\r
+       if (ob->temp1 <= 0)\r
+       {\r
+               ob->state = &s_skel_1;\r
+               ob->ticcount = ob->state->tictime;\r
+               ob->obclass = skeletonobj;\r
+               ob->speed = 2036;\r
+               ob->flags |= of_shootable;\r
+               ob->hitpoints = EasyHitPoints(12);\r
+       }\r
+}\r
+\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+//     EasyHitPoints\r
+//\r
+//     Checks to see if the player has selected the easy mode for playing.\r
+//     If so then the normal hit points are cut in half.\r
+//     This is when the object is spawned.\r
+//\r
+//     Parms\r
+//             NrmHitPts - the normal hit points\r
+//\r
+//     Returns\r
+//             Half of NrmHitPts\r
+//\r
+/////////////////////////////////////////////////////////////////////////////\r
+\r
+int EasyHitPoints(int NrmHitPts)\r
+{\r
+       if (EASYMODEON)                         // Wimpy, Wimpy, Wimpy!!!!!\r
+       {\r
+               return(NrmHitPts/4);\r
+       }\r
+       else\r
+               return(NrmHitPts);\r
+}\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+//     EasyDoDamage\r
+//\r
+//     Checks to see if the player has selected the easy mode for playing.\r
+//     If so then the normal amount of damage is cut in half.\r
+//     This is called each time a monster does damage.\r
+//\r
+//     Parms\r
+//             Damage - the normal damage taken\r
+//\r
+//     Returns\r
+//             Half of Damage\r
+//\r
+/////////////////////////////////////////////////////////////////////////////\r
+\r
+int EasyDoDamage(int Damage)\r
+{\r
+       if (EASYMODEON)                         // Wimpy, Wimpy, Wimpy!!!!!\r
+               return(Damage/2);\r
+       else\r
+               return(Damage);\r
+}\r
+\r
diff --git a/16/cawat/C5_ASM.ASM b/16/cawat/C5_ASM.ASM
new file mode 100644 (file)
index 0000000..a4390b2
--- /dev/null
@@ -0,0 +1,248 @@
+; Catacomb Armageddon Source Code\r
+; Copyright (C) 1993-2014 Flat Rock Software\r
+;\r
+; This program is free software; you can redistribute it and/or modify\r
+; it under the terms of the GNU General Public License as published by\r
+; the Free Software Foundation; either version 2 of the License, or\r
+; (at your option) any later version.\r
+;\r
+; This program is distributed in the hope that it will be useful,\r
+; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+; GNU General Public License for more details.\r
+;\r
+; You should have received a copy of the GNU General Public License along\r
+; with this program; if not, write to the Free Software Foundation, Inc.,\r
+; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+IDEAL\r
+\r
+MODEL  MEDIUM,C\r
+\r
+INCLUDE        "ID_ASM.EQU"\r
+\r
+VIEWWIDTH      =       (40*8)                  ;33\r
+GC_INDEX       =       03CEh\r
+\r
+DATASEG\r
+EVEN\r
+\r
+;=================== Tables filled in by DrawVWall ==========================\r
+\r
+;\r
+; wallheight has the height (scale number) of that collumn of scaled wall\r
+; it is pre bounded to 1-MAXSCALE (the actuial height on screen is 2*height)\r
+;\r
+wallheight     dw      VIEWWIDTH dup (?)\r
+\r
+;\r
+; wallwidth has the pixel width (1-7) of that collumn\r
+;\r
+wallwidth      dw      VIEWWIDTH dup (?)\r
+\r
+;\r
+; wallseg has the segment of the wall picture\r
+;\r
+wallseg                dw      VIEWWIDTH dup (?)\r
+\r
+;\r
+; wallofs has the offset of the wall picture\r
+;\r
+wallofs                dw      VIEWWIDTH dup (?)\r
+\r
+;============================================================================\r
+\r
+;\r
+; screenbyte is just position/8\r
+;\r
+LABEL          screenbyte      WORD\r
+pos    =       0\r
+REPT           VIEWWIDTH\r
+                       dw      pos/8\r
+pos    =       pos+1\r
+ENDM\r
+\r
+;\r
+; screenbit is (position&7)*16\r
+;\r
+LABEL          screenbit       WORD\r
+pos    =       0\r
+REPT           VIEWWIDTH\r
+                       dw      (pos AND 7)*16\r
+pos    =       pos+1\r
+ENDM\r
+\r
+;\r
+; Use offset: screenbit[]+pixwidth*2\r
+; acess from bitmasks-2+offset for one biased pixwidth\r
+; the low byte of bitmasks is for the first screen byte, the high byte\r
+; is the bitmask for the second screen byte (if non 0)\r
+;\r
+\r
+bitmasks       dw      0080h,00c0h,00e0h,00f0h,00f8h,00fch,00feh,00ffh\r
+                       dw      0040h,0060h,0070h,0078h,007ch,007eh,007fh,807fh\r
+                       dw      0020h,0030h,0038h,003ch,003eh,003fh,803fh,0c03fh\r
+                       dw      0010h,0018h,001ch,001eh,001fh,801fh,0c01fh,0e01fh\r
+                       dw      0008h,000ch,000eh,000fh,800fh,0c00fh,0e00fh,0f00fh\r
+                       dw      0004h,0006h,0007h,8007h,0c007h,0e007h,0f007h,0f807h\r
+                       dw      0002h,0003h,8003h,0c003h,0e003h,0f003h,0f803h,0fc03h\r
+                       dw      0001h,8001h,0c001h,0e001h,0f001h,0f801h,0fc01h,0fe01h\r
+\r
+\r
+;\r
+; wallscalecall is a far pointer to the start of a compiled scaler\r
+; The low word will never change, while the high word is set to\r
+; compscaledirectory[scale]\r
+;\r
+wallscalecall  dd      (65*6)                  ; offset of t_compscale->code[0]\r
+\r
+\r
+PUBLIC wallheight,wallwidth,wallseg,wallofs,screenbyte,screenbit\r
+PUBLIC bitmasks,wallscalecall\r
+\r
+\r
+EXTRN  scaledirectory:WORD                     ; array of MAXSCALE segment pointers to\r
+                                                                       ; compiled scalers\r
+EXTRN  screenseg:WORD                          ; basically just 0xa000\r
+EXTRN  bufferofs:WORD                          ; offset of the current work screen\r
+EXTRN ylookup:WORD\r
+EXTRN screenpage:WORD\r
+\r
+CODESEG\r
+\r
+;============================================================================\r
+;\r
+; ScaleWalls\r
+;\r
+; AX   AL is scratched in bit mask setting and scaling\r
+; BX   table index\r
+; CX   pixwidth*2\r
+; DX   GC_INDEX\r
+; SI   offset into wall data to scale from, allways 0,64,128,...4032\r
+; DI    byte at top of screen that the collumn is contained in\r
+; BP   x pixel * 2, index into VIEWWIDTH wide tables\r
+; DS   segment of the wall data to texture map\r
+; ES   screenseg\r
+; SS   addressing DGROUP variables\r
+;\r
+;============================================================================\r
+\r
+PROC   ScaleWalls\r
+PUBLIC ScaleWalls\r
+USES   SI,DI,BP\r
+\r
+       xor     bp,bp                                           ; start at location 0 in the tables\r
+       mov     dx,GC_INDEX+1\r
+       mov     es,[screenseg]\r
+\r
+;\r
+; scale one collumn of data, possibly across two bytes\r
+;\r
+nextcollumn:\r
+\r
+       mov     bx,[wallheight+bp]                      ; height of walls (1-MAXSCALE)\r
+       shl     bx,1\r
+       mov     ax,[ss:scaledirectory+bx]       ; segment of the compiled scaler\r
+       mov [WORD PTR ss:wallscalecall+2],ax\r
+\r
+       mov     cx,[wallwidth+bp]\r
+       or      cx,cx\r
+       jnz     okwidth\r
+       mov     cx,2\r
+       jmp     next\r
+\r
+okwidth:\r
+       shl     cx,1\r
+       mov     ds,[wallseg+bp]\r
+       mov     si,[wallofs+bp]\r
+\r
+       mov     di,[screenbyte+bp]                      ; byte at the top of the scaled collumn\r
+       add     di,[ss:bufferofs]                       ; offset of current page flip\r
+       mov     bx,[screenbit+bp]                       ; 0-7 << 4\r
+       add     bx,cx\r
+       mov     ax,[ss:bitmasks-2+bx]\r
+       out     dx,al                                           ; set bit mask register\r
+       call [DWORD PTR ss:wallscalecall]               ; scale the line of pixels\r
+       or      ah,ah                                           ; is there anything in the second byte?\r
+       jnz     secondbyte\r
+;\r
+; next\r
+;\r
+next:\r
+       add     bp,cx\r
+       cmp     bp,VIEWWIDTH*2\r
+       jb      nextcollumn\r
+       jmp     done\r
+\r
+;\r
+; draw a second byte for vertical strips that cross two bytes\r
+;\r
+secondbyte:\r
+       mov     al,ah\r
+       inc     di                                                              ; next byte over\r
+       out     dx,al                                                   ; set bit mask register\r
+       call [DWORD PTR ss:wallscalecall]       ; scale the line of pixels\r
+;\r
+; next\r
+;\r
+       add     bp,cx\r
+       cmp     bp,VIEWWIDTH*2\r
+       jb      nextcollumn\r
+\r
+done:\r
+       mov     ax,ss\r
+       mov     ds,ax\r
+       ret\r
+\r
+ENDP\r
+\r
+;---------------------------------------------------------------------------\r
+;\r
+; RadarBlip()\r
+;\r
+; Displays a 'blip' (1 pixel wide X 2 pixel high) on the radar at\r
+; an (X,Y) relative to (RADAR_X,RADAR_Y) (defined below...)\r
+;\r
+;---------------------------------------------------------------------------\r
+\r
+PROC   RadarBlip x:WORD, y:WORD, color:WORD\r
+USES   SI,DI\r
+PUBLIC RadarBlip\r
+\r
+       mov     ax,[screenseg]\r
+\r
+       mov     es,ax\r
+       xor     di,di\r
+\r
+       lea     si,[ylookup]\r
+       add     si,[y]\r
+       add     si,[y]\r
+       add     di,[si]\r
+\r
+       mov     ax,[x]\r
+       shr     ax,1\r
+       shr     ax,1\r
+       shr     ax,1\r
+       add     di,ax\r
+\r
+       mov     ax,[x]\r
+       and     ax,7\r
+       mov     cl,al\r
+       mov     ah,080h\r
+       shr     ah,cl\r
+       cli\r
+       mov     al,GC_BITMASK\r
+       mov     dx,GC_INDEX\r
+       out     dx,ax\r
+       sti\r
+\r
+       mov     ax,[color]\r
+       mov     ah,[es:di]                                              ; read into latches\r
+       mov     [es:di],al                                              ; write latches / color bit\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+END\r
+\r
diff --git a/16/cawat/C5_DEBUG.C b/16/cawat/C5_DEBUG.C
new file mode 100644 (file)
index 0000000..d8e2b1a
--- /dev/null
@@ -0,0 +1,799 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// C3_DEBUG.C\r
+\r
+#include "DEF.H"\r
+#include "gelib.h"\r
+#pragma hdrstop\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define DEBUG_OVERHEAD 0\r
+\r
+\r
+#define VIEWTILEX      20\r
+#define VIEWTILEY      (VIEWHEIGHT/16)\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+short colordelay=0;\r
+boolean autofire=false;\r
+int    maporgx;\r
+int    maporgy;\r
+enum {mapview,tilemapview,actoratview,visview,mapseg2,lastview}        viewtype;\r
+\r
+void ViewMap (void);\r
+\r
+//===========================================================================\r
+\r
+#if 0\r
+/*\r
+================\r
+=\r
+= PicturePause\r
+=\r
+================\r
+*/\r
+\r
+void PicturePause (void)\r
+{\r
+       int     y;\r
+       unsigned        source;\r
+\r
+       source = displayofs+panadjust;\r
+\r
+//     VW_ColorBorder (15);\r
+       VW_SetLineWidth (40);\r
+       VW_SetScreen (0,0);\r
+\r
+       if (source<0x10000l-200*64)\r
+       {\r
+       //\r
+       // copy top line first\r
+       //\r
+               for (y=0;y<200;y++)\r
+                       VW_ScreenToScreen (source+y*64,y*40,40,1);\r
+       }\r
+       else\r
+       {\r
+       //\r
+       // copy bottom line first\r
+       //\r
+               for (y=199;y>=0;y--)\r
+                       VW_ScreenToScreen (source+y*64,y*40,40,1);\r
+       }\r
+\r
+       IN_Shutdown ();\r
+\r
+       VW_WaitVBL(70);\r
+       bioskey(0);\r
+       VW_WaitVBL(70);\r
+       Quit (NULL);\r
+}\r
+#endif\r
+\r
+\r
+//===========================================================================\r
+\r
+//===========================================================================\r
+\r
+#define        sc_1                    0x02\r
+#define        sc_2                    0x03\r
+#define        sc_3                    0x04\r
+#define        sc_4                    0x05\r
+#define        sc_5                    0x06\r
+#define        sc_6                    0x07\r
+#define        sc_7                    0x08\r
+#define        sc_8                    0x09\r
+#define        sc_9                    0x0a\r
+#define        sc_0                    0x0b\r
+\r
+\r
+\r
+/*\r
+================\r
+=\r
+= DebugKeys\r
+=\r
+================\r
+*/\r
+\r
+int DebugKeys (void)\r
+{\r
+       boolean esc;\r
+       int level,i;\r
+\r
+#if DEBUG_KEYS_AVAILABLE\r
+       if (Keyboard[sc_R])\r
+       {\r
+               CenterWindow (12,2);\r
+               if (autofire)\r
+                 US_PrintCentered ("Rapid-Fire OFF");\r
+               else\r
+                 US_PrintCentered ("Rapid-Fire ON");\r
+               VW_UpdateScreen();\r
+               IN_Ack();\r
+               autofire ^= 1;\r
+               return 1;\r
+       }\r
+#endif\r
+\r
+#if DEBUG_KEYS_AVAILABLE\r
+       if (Keyboard[sc_A])\r
+       {\r
+               char levelstr[50];\r
+               unsigned org_tile,org_mapon,msgnum;\r
+               boolean newmsg=true,newlevel=false;\r
+\r
+               VW_FixRefreshBuffer ();\r
+               CenterWindow (16,3);\r
+               US_Print("\n");\r
+               US_CPrint("Message Test");\r
+               VW_UpdateScreen();\r
+\r
+               org_mapon = mapon;\r
+               msgnum = (org_tile = *(mapsegs[0]+farmapylookup[player->tiley]+player->tilex))-NAMESTART;\r
+               while (1)\r
+               {\r
+       // Get outta' here\r
+       //\r
+                       if (Keyboard[sc_Escape])\r
+                       {\r
+                               while (Keyboard[sc_Escape]);\r
+                               break;\r
+                       }\r
+\r
+       // Move to previous message\r
+       //\r
+                       if (Keyboard[sc_UpArrow])\r
+                       {\r
+                               if (msgnum)\r
+                               {\r
+                                       msgnum--;\r
+                                       newmsg = true;\r
+                               }\r
+                       }\r
+\r
+       // Move to next message\r
+       //\r
+                       if (Keyboard[sc_DownArrow])\r
+                       {\r
+                               if (msgnum < 24)\r
+                               {\r
+                                       msgnum++;\r
+                                       newmsg = true;\r
+                               }\r
+                       }\r
+\r
+       // Move to previous level\r
+       //\r
+                       if (Keyboard[sc_LeftArrow])\r
+                       {\r
+                               if (mapon)\r
+                               {\r
+                                       MM_SetPurge(&grsegs[LEVEL1TEXT+mapon],3);\r
+                                       mapon--;\r
+                                       newlevel = true;\r
+                               }\r
+                       }\r
+\r
+       // Move to next level\r
+       //\r
+                       if (Keyboard[sc_RightArrow])\r
+                       {\r
+                               if (mapon < LASTMAP-1)\r
+                               {\r
+                                       MM_SetPurge(&grsegs[LEVEL1TEXT+mapon],3);\r
+                                       mapon++;\r
+                                       newlevel = true;\r
+                               }\r
+                       }\r
+\r
+       // Load new level text\r
+       //\r
+                       if (newlevel)\r
+                       {\r
+                               CA_CacheGrChunk(LEVEL1TEXT+mapon);\r
+                               ScanText();\r
+                               newmsg = true;\r
+                               newlevel=false;\r
+                       }\r
+\r
+       // Display new message text\r
+       //\r
+                       if (newmsg)\r
+                       {\r
+                               *(mapsegs[0]+farmapylookup[player->tiley]+player->tilex) = msgnum+NAMESTART;\r
+                               DrawText(true);\r
+                               strcpy(levelstr,"Level: ");\r
+                               itoa(mapon,levelstr+strlen(levelstr),10);\r
+                               strcat(levelstr,"  Msg: ");\r
+                               itoa(msgnum,levelstr+strlen(levelstr),10);\r
+                               DisplaySMsg(levelstr,NULL);\r
+                               newmsg = false;\r
+\r
+                               if (Keyboard[sc_UpArrow] || Keyboard[sc_DownArrow] || Keyboard[sc_LeftArrow] || Keyboard[sc_RightArrow])\r
+                                       VW_WaitVBL(6);\r
+                       }\r
+\r
+               }\r
+// Restore game\r
+//\r
+               MM_SetPurge(&grsegs[LEVEL1TEXT+mapon],3);\r
+               mapon = org_mapon;\r
+               CA_CacheGrChunk(LEVEL1TEXT+mapon);\r
+               ScanText();\r
+               *(mapsegs[0]+farmapylookup[player->tiley]+player->tilex) = org_tile;\r
+               DrawText(true);\r
+               status_flag = 0;\r
+       }\r
+\r
+\r
+       if (Keyboard[sc_V])\r
+       {\r
+               displayofs = bufferofs = screenloc[screenpage];\r
+               CenterWindow (16,4);\r
+               US_CPrint("\n"GAMENAME);\r
+               US_CPrint(VERSION);\r
+               US_CPrint(REVISION);\r
+               VW_UpdateScreen();\r
+               IN_Ack ();\r
+       }\r
+\r
+\r
+       if (Keyboard[sc_Q])                     // Q = Insta-Quit!\r
+               Quit("Insta-Quit!");\r
+\r
+       if (Keyboard[sc_Z])             // Z = freeze Time\r
+       {\r
+               if (FreezeTime)\r
+                 FreezeTime = 1;               // Allow refresh to dec to zero..\r
+               else\r
+                       StopTime();\r
+\r
+               IN_Ack();\r
+               return 1;\r
+       }\r
+#endif\r
+\r
+\r
+//     if (Keyboard[sc_E])\r
+//             FaceDoor((player->x>>16l)+1,(player->y>>16l));\r
+//             FaceAngle(90);\r
+\r
+#if 0\r
+       if (Keyboard[sc_B])             // B = border color\r
+       {\r
+               CenterWindow(24,3);\r
+               PrintY+=6;\r
+               US_Print(" Border color (0-15):");\r
+               VW_UpdateScreen();\r
+               esc = !US_LineInput (px,py,str,NULL,true,2,0);\r
+               if (!esc)\r
+               {\r
+                       level = atoi (str);\r
+                       if (level>=0 && level<=15)\r
+                               VW_ColorBorder (level);\r
+               }\r
+               return 1;\r
+       }\r
+#endif\r
+\r
+\r
+#if 1//DEBUG_KEYS_AVAILABLE\r
+       if (Keyboard[sc_O])\r
+       {\r
+               extern unsigned objectcount,latchmemavail;\r
+               unsigned unused,total;\r
+\r
+               CenterWindow (30,13);\r
+               US_Print ("Objects: ");\r
+               US_PrintUnsigned (objectcount);\r
+\r
+               US_Print("\n\nTics: ");\r
+               US_PrintUnsigned (tics);\r
+               US_Print("      Real Tics: ");\r
+               US_PrintUnsigned(realtics);\r
+\r
+               US_Print ("\n\n    Total Available: ");\r
+               US_PrintUnsigned (mminfo.mainmem/1024);\r
+               US_Print ("k\n        Mem In Use: ");\r
+               unused=MM_UnusedMemory()/1024;\r
+               US_PrintUnsigned (unused);\r
+               US_Print ("k\n Mem After Purge: ");\r
+               total=MM_TotalFree()/1024;\r
+               US_PrintUnsigned (total);\r
+               US_Print ("k (");\r
+               US_PrintUnsigned (total-unused);\r
+\r
+               US_Print (")\n\nLatch Mem Free: ");\r
+               US_PrintUnsigned (latchmemavail);\r
+               US_Print ("\n");\r
+               VW_UpdateScreen();\r
+               IN_Ack();\r
+       }\r
+\r
+       if (colordelay<1)\r
+       {\r
+               if (Keyboard[26])\r
+               {\r
+                       extern unsigned *groundcolor,debug_gnd;\r
+\r
+                       groundcolor = &debug_gnd;\r
+                       debug_gnd += 0x0101;\r
+                       if (debug_gnd == 0x1010)\r
+                               debug_gnd = 0;\r
+                       colordelay = 10;\r
+               }\r
+\r
+               if (Keyboard[27])\r
+               {\r
+                       extern unsigned *skycolor,debug_sky;\r
+\r
+                       skycolor = &debug_sky;\r
+                       debug_sky += 0x0101;\r
+                       if (debug_sky == 0x1010)\r
+                               debug_sky = 0;\r
+                       colordelay = 10;\r
+               }\r
+       }\r
+       else\r
+               colordelay -= realtics;\r
+#endif\r
+\r
+\r
+#if 0\r
+       if (Keyboard[sc_C])             // C = count objects\r
+       {\r
+               CountObjects();\r
+               return 1;\r
+       }\r
+\r
+\r
+       if (Keyboard[sc_D])             // D = start / end demo record\r
+       {\r
+               if (DemoMode == demo_Off)\r
+                       StartDemoRecord ();\r
+               else if (DemoMode == demo_Record)\r
+               {\r
+                       EndDemoRecord ();\r
+                       playstate = ex_completed;\r
+               }\r
+               return 1;\r
+       }\r
+#endif\r
+\r
+#if 0\r
+       if (Keyboard[sc_E])             // E = quit level\r
+       {\r
+               if (tedlevel)\r
+                       TEDDeath();\r
+               playstate = ex_warped;\r
+               gamestate.mapon++;\r
+       }\r
+#endif\r
+\r
+#if 0\r
+       if (Keyboard[sc_F])             // F = facing spot\r
+       {\r
+               CenterWindow (12,4);\r
+               US_Print ("X:");\r
+               US_PrintUnsigned (player->x);\r
+               US_Print ("Y:");\r
+               US_PrintUnsigned (player->y);\r
+               US_Print ("A:");\r
+               US_PrintUnsigned (player->angle);\r
+               VW_UpdateScreen();\r
+               IN_Ack();\r
+               return 1;\r
+       }\r
+#endif\r
+\r
+       if (Keyboard[sc_G])             // G = god mode\r
+       {\r
+               CenterWindow (12,2);\r
+               if (godmode)\r
+                 US_PrintCentered ("God mode OFF");\r
+               else\r
+                 US_PrintCentered ("God mode ON");\r
+               VW_UpdateScreen();\r
+               IN_Ack();\r
+               godmode ^= 1;\r
+               return 1;\r
+       }\r
+\r
+#if 0\r
+       if (Keyboard[sc_H])             // H = hurt self\r
+       {\r
+               TakeDamage (5);\r
+       }\r
+#endif\r
+\r
+       if (Keyboard[sc_I])                     // I = item cheat\r
+       {\r
+               extern boolean redraw_gems;\r
+\r
+               CenterWindow (12,3);\r
+               US_PrintCentered ("Free items!");\r
+               VW_UpdateScreen();\r
+               for (i=0;i<4;i++)\r
+               {\r
+                       GiveBolt ();\r
+                       GiveNuke ();\r
+                       GivePotion ();\r
+//                     if (!gamestate.keys[i])\r
+                               GiveKey (i);\r
+                       gamestate.gems[i] = GEM_DELAY_TIME;\r
+               }\r
+               gamestate.gems[4] = GEM_DELAY_TIME;\r
+               redraw_gems = true;\r
+/////////              for (i=0;i<8;i++)\r
+/////////                      GiveScroll (i,false);\r
+\r
+               IN_Ack ();\r
+               return 1;\r
+       }\r
+\r
+#if DEBUG_OVERHEAD\r
+       if (Keyboard[sc_Z])                     // O is used elsewhere...\r
+       {\r
+               ViewMap();\r
+               return 1;\r
+       }\r
+#endif\r
+\r
+#if 0\r
+       if (Keyboard[sc_P])                     // P = pause with no screen disruptioon\r
+       {\r
+               PicturePause ();\r
+               return 1;\r
+       }\r
+#endif\r
+\r
+#if 0\r
+       if (Keyboard[sc_S])     // S = slow motion\r
+       {\r
+               singlestep^=1;\r
+               CenterWindow (18,3);\r
+               if (singlestep)\r
+                       US_PrintCentered ("Slow motion ON");\r
+               else\r
+                       US_PrintCentered ("Slow motion OFF");\r
+               VW_UpdateScreen();\r
+               IN_Ack ();\r
+               return 1;\r
+       }\r
+#endif\r
+\r
+#if 0\r
+       if (Keyboard[sc_V])                     // V = extra VBLs\r
+       {\r
+               CenterWindow(30,3);\r
+               PrintY+=6;\r
+               US_Print("  Add how many extra VBLs(0-8):");\r
+               VW_UpdateScreen();\r
+               esc = !US_LineInput (px,py,str,NULL,true,2,0);\r
+               if (!esc)\r
+               {\r
+                       level = atoi (str);\r
+                       if (level>=0 && level<=8)\r
+                               extravbls = level;\r
+               }\r
+               return 1;\r
+       }\r
+#endif\r
+\r
+       if (Keyboard[sc_W])     // W = warp to level\r
+       {\r
+               CenterWindow(26,3);\r
+               PrintY+=6;\r
+               US_Print("  Warp to which level(0-16):");\r
+               VW_UpdateScreen();\r
+               esc = !US_LineInput (px,py,str,NULL,true,2,0);\r
+               if (!esc)\r
+               {\r
+                       level = atoi (str);\r
+                       if (level>=0 && level<=LASTMAP-1)\r
+                       {\r
+                               gamestate.mapon = level;\r
+                               playstate = ex_warped;\r
+                               lasttext = -1;\r
+                       }\r
+               }\r
+               return 1;\r
+       }\r
+\r
+#if 0\r
+       if (Keyboard[sc_X])                     // X = item cheat\r
+       {\r
+               CenterWindow (12,3);\r
+               US_PrintCentered ("Extra stuff!");\r
+               VW_UpdateScreen();\r
+               for (i=0;i<4;i++)\r
+               {\r
+                       GiveBolt ();\r
+                       GiveNuke ();\r
+                       GivePotion ();\r
+               }\r
+               IN_Ack ();\r
+               return 1;\r
+       }\r
+#endif\r
+\r
+////////       if (LastScan >= sc_1 && LastScan <= sc_8)       // free scrolls\r
+////////       {\r
+////////               GiveScroll (LastScan-sc_1,false);\r
+////////               IN_ClearKeysDown ();\r
+////////       }\r
+\r
+       return 0;\r
+}\r
+\r
+\r
+#if DEBUG_OVERHEAD\r
+\r
+/*\r
+=====================\r
+=\r
+= LatchDrawChar\r
+=\r
+=====================\r
+*/\r
+\r
+void LatchDrawChar (unsigned x, unsigned y, unsigned picnum)\r
+{\r
+       unsigned        source, dest;\r
+\r
+       dest = bufferofs + ylookup[y]+x;\r
+       source = latchpics[0]+picnum*8;\r
+\r
+       EGAWRITEMODE(1);\r
+       EGAMAPMASK(15);\r
+\r
+asm    mov     bx,[linewidth]\r
+asm    dec     bx\r
+\r
+asm    mov     ax,[screenseg]\r
+asm    mov     es,ax\r
+asm    mov     ds,ax\r
+\r
+asm    mov     si,[source]\r
+asm    mov     di,[dest]\r
+\r
+asm    movsb\r
+asm    add     di,bx\r
+asm    movsb\r
+asm    add     di,bx\r
+asm    movsb\r
+asm    add     di,bx\r
+asm    movsb\r
+asm    add     di,bx\r
+asm    movsb\r
+asm    add     di,bx\r
+asm    movsb\r
+asm    add     di,bx\r
+asm    movsb\r
+asm    add     di,bx\r
+asm    movsb\r
+\r
+asm    mov     ax,ss\r
+asm    mov     ds,ax                                   // restore turbo's data segment\r
+\r
+       EGAWRITEMODE(0);\r
+}\r
+\r
+#endif\r
+\r
+\r
+#if DEBUG_OVERHEAD\r
+/*\r
+=====================\r
+=\r
+= LatchDrawTile\r
+=\r
+=====================\r
+*/\r
+\r
+void LatchDrawTile (unsigned x, unsigned y, unsigned picnum)\r
+{\r
+       unsigned        source, dest;\r
+\r
+       dest = bufferofs + ylookup[y]+x;\r
+       source = tileoffsets[picnum];\r
+\r
+       EGAWRITEMODE(1);\r
+       EGAMAPMASK(15);\r
+\r
+asm    mov     bx,[linewidth]\r
+asm    sub     bx,2\r
+\r
+asm    mov     ax,[screenseg]\r
+asm    mov     es,ax\r
+asm    mov     ds,ax\r
+\r
+asm    mov     si,[source]\r
+asm    mov     di,[dest]\r
+asm    mov     dx,16\r
+\r
+lineloop:\r
+asm    movsb\r
+asm    movsb\r
+asm    add     di,bx\r
+\r
+asm    dec     dx\r
+asm    jnz     lineloop\r
+\r
+asm    mov     ax,ss\r
+asm    mov     ds,ax                                   // restore turbo's data segment\r
+\r
+       EGAWRITEMODE(0);\r
+}\r
+#endif\r
+\r
+\r
+#if DEBUG_OVERHEAD\r
+/*\r
+===================\r
+=\r
+= OverheadRefresh\r
+=\r
+===================\r
+*/\r
+\r
+void OverheadRefresh (void)\r
+{\r
+       unsigned        x,y,endx,endy,sx,sy;\r
+       unsigned        tile;\r
+\r
+\r
+       if (++screenpage == 3)\r
+               screenpage = 0;\r
+\r
+       bufferofs = screenloc[screenpage];\r
+\r
+       endx = maporgx+VIEWTILEX;\r
+       endy = maporgy+VIEWTILEY;\r
+\r
+       for (y=maporgy;y<endy;y++)\r
+               for (x=maporgx;x<endx;x++)\r
+               {\r
+                       sx = (x-maporgx)*2;\r
+                       sy = (y-maporgy)*16;\r
+\r
+                       switch (viewtype)\r
+                       {\r
+                       case mapview:\r
+                               tile = *(mapsegs[0]+farmapylookup[y]+x);\r
+                               break;\r
+\r
+                       case tilemapview:\r
+                               tile = tilemap[x][y];\r
+                               break;\r
+\r
+                       case actoratview:\r
+                               tile = (unsigned)actorat[x][y];\r
+                               break;\r
+\r
+                       case visview:\r
+                               tile = spotvis[x][y];\r
+                               break;\r
+\r
+                       case mapseg2:\r
+                               tile = *(mapsegs[2]+farmapylookup[y]+x);\r
+                               if (tile < 256)\r
+                                       tile = *(mapsegs[0]+farmapylookup[y]+x);\r
+                       break;\r
+\r
+                       }\r
+\r
+                       if (tile<NUMTILE16)\r
+                               LatchDrawTile(sx,sy,tile);\r
+                       else\r
+                       {\r
+                               LatchDrawChar(sx,sy,NUMBERCHARS+((tile&0xf000)>>12));\r
+                               LatchDrawChar(sx+1,sy,NUMBERCHARS+((tile&0x0f00)>>8));\r
+                               LatchDrawChar(sx,sy+8,NUMBERCHARS+((tile&0x00f0)>>4));\r
+                               LatchDrawChar(sx+1,sy+8,NUMBERCHARS+(tile&0x000f));\r
+                       }\r
+               }\r
+\r
+       VW_SetScreen (bufferofs,0);\r
+       displayofs = bufferofs;\r
+}\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= ViewMap\r
+=\r
+===================\r
+*/\r
+\r
+void ViewMap (void)\r
+{\r
+       boolean         button0held;\r
+\r
+       viewtype = actoratview;\r
+       button0held = false;\r
+\r
+\r
+       maporgx = player->tilex - VIEWTILEX/2;\r
+       if (maporgx<0)\r
+               maporgx = 0;\r
+       maporgy = player->tiley - VIEWTILEY/2;\r
+       if (maporgy<0)\r
+               maporgy = 0;\r
+\r
+       do\r
+       {\r
+//\r
+// let user pan around\r
+//\r
+               IN_ReadControl(0,&control);\r
+               if (control.xaxis == -1 && maporgx>0)\r
+                       maporgx--;\r
+               if (control.xaxis == 1 && maporgx<mapwidth-VIEWTILEX)\r
+                       maporgx++;\r
+               if (control.yaxis == -1 && maporgy>0)\r
+                       maporgy--;\r
+               if (control.yaxis == 1 && maporgy<mapheight-VIEWTILEY)\r
+                       maporgy++;\r
+\r
+               if (control.button0 && !button0held)\r
+               {\r
+                       button0held = true;\r
+                       viewtype++;\r
+                       if (viewtype==lastview)\r
+                               viewtype = mapview;\r
+               }\r
+               if (!control.button0)\r
+                       button0held = false;\r
+\r
+\r
+               OverheadRefresh ();\r
+\r
+       } while (!Keyboard[sc_Escape]);\r
+\r
+       IN_ClearKeysDown ();\r
+       DrawPlayScreen ();\r
+}\r
+#endif\r
+\r
diff --git a/16/cawat/C5_DRAW.C b/16/cawat/C5_DRAW.C
new file mode 100644 (file)
index 0000000..bab5ea3
--- /dev/null
@@ -0,0 +1,1932 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// C3_DRAW.C\r
+\r
+#include "DEF.H"\r
+#pragma hdrstop\r
+\r
+//#define DRAWEACH                              // draw walls one at a time for debugging\r
+\r
+unsigned        highest;\r
+unsigned        mostwalls,numwalls;\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define PI      3.141592657\r
+#define ANGLEQUAD       (ANGLES/4)\r
+\r
+unsigned        oldend;\r
+\r
+#define FINEANGLES      3600\r
+\r
+#define MINRATIO        16\r
+\r
+\r
+const   unsigned        MAXSCALEHEIGHT  = (VIEWWIDTH/2);\r
+const   unsigned        MAXVISHEIGHT    = (VIEWHEIGHT/2);\r
+const   unsigned        BASESCALE               = 32;\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+//\r
+// calculate location of screens in video memory so they have the\r
+// maximum possible distance seperating them (for scaling overflow)\r
+//\r
+\r
+unsigned screenloc[3]= {PAGE1START,PAGE2START,PAGE3START};\r
+unsigned freelatch = FREESTART;\r
+\r
+boolean         fizzlein;\r
+\r
+long    scaleshapecalll;\r
+long    scaletablecall;\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+long    bytecount,endcount;             // for profiling\r
+int             animframe;\r
+int             pixelangle[VIEWWIDTH];\r
+int             far finetangent[FINEANGLES+1];\r
+int             fineviewangle;\r
+unsigned        viewxpix,viewypix;\r
+\r
+/*\r
+============================================================================\r
+\r
+                          3 - D  DEFINITIONS\r
+\r
+============================================================================\r
+*/\r
+\r
+fixed   tileglobal      = TILEGLOBAL;\r
+fixed   focallength     = FOCALLENGTH;\r
+fixed   mindist         = MINDIST;\r
+int             viewheight      = VIEWHEIGHT;\r
+fixed scale;\r
+\r
+\r
+tilept  tile,lasttile,          // tile of wall being followed\r
+       focal,                  // focal point in tiles\r
+       left,mid,right;         // rightmost tile in view\r
+\r
+globpt edge,view;\r
+\r
+int     segstart[VIEWHEIGHT],   // addline tracks line segment and draws\r
+       segend[VIEWHEIGHT],\r
+       segcolor[VIEWHEIGHT];   // only when the color changes\r
+\r
+\r
+walltype        walls[MAXWALLS],*leftwall,*rightwall;\r
+\r
+\r
+//==========================================================================\r
+\r
+//\r
+// refresh stuff\r
+//\r
+\r
+int screenpage;\r
+\r
+long lasttimecount;\r
+\r
+//\r
+// rendering stuff\r
+//\r
+\r
+int firstangle,lastangle;\r
+\r
+fixed prestep;\r
+\r
+fixed sintable[ANGLES+ANGLES/4],*costable = sintable+(ANGLES/4);\r
+\r
+fixed   viewx,viewy;                    // the focal point\r
+int     viewangle;\r
+fixed   viewsin,viewcos;\r
+\r
+int     zbuffer[VIEWXH+1];      // holds the height of the wall at that point\r
+\r
+//==========================================================================\r
+\r
+void    DrawLine (int xl, int xh, int y,int color);\r
+void    DrawWall (walltype *wallptr);\r
+void    TraceRay (unsigned angle);\r
+fixed   FixedByFrac (fixed a, fixed b);\r
+fixed   FixedAdd (void);\r
+fixed   TransformX (fixed gx, fixed gy);\r
+int             FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max);\r
+int             BackTrace (int finish);\r
+void    ForwardTrace (void);\r
+int             TurnClockwise (void);\r
+int             TurnCounterClockwise (void);\r
+void    FollowWall (void);\r
+\r
+void    NewScene (void);\r
+void    BuildTables (void);\r
+\r
+//==========================================================================\r
+\r
+\r
+#if 0\r
+/*\r
+==================\r
+=\r
+= DrawLine\r
+=\r
+= Must be in write mode 2 with all planes enabled\r
+= The bit mask is left set to the end value, so clear it after all lines are\r
+= drawn\r
+=\r
+= draws a black dot at the left edge of the line\r
+=\r
+==================\r
+*/\r
+\r
+unsigned static char dotmask[8] = {0x80,0x40,0x20,0x10,8,4,2,1};\r
+unsigned static char leftmask[8] = {0xff,0x7f,0x3f,0x1f,0xf,7,3,1};\r
+unsigned static char rightmask[8] = {0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff};\r
+\r
+void DrawLine (int xl, int xh, int y,int color)\r
+{\r
+  unsigned dest,xlp,xlb,xhb,maskleft,maskright,maskdot,mid;\r
+\r
+  xlb=xl/8;\r
+  xhb=xh/8;\r
+\r
+  if (xh<xl)\r
+       Quit("DrawLine: xh<xl");\r
+  if (y<VIEWY)\r
+       Quit("DrawLine: y<VIEWY");\r
+  if (y>VIEWYH)\r
+       Quit("DrawLine: y>VIEWYH");\r
+\r
+       xlp = xl&7;\r
+       maskleft = leftmask[xlp];\r
+       maskright = rightmask[xh&7];\r
+\r
+  mid = xhb-xlb-1;\r
+  dest = bufferofs+ylookup[y]+xlb;\r
+\r
+       //\r
+       // set the GC index register to point to the bit mask register\r
+       //\r
+       asm     mov     al,GC_BITMASK\r
+       asm     mov     dx,GC_INDEX\r
+       asm     out     dx,al\r
+\r
+  if (xlb==xhb)\r
+  {\r
+  //\r
+  // entire line is in one byte\r
+  //\r
+\r
+       maskleft&=maskright;\r
+\r
+       asm     mov     es,[screenseg]\r
+       asm     mov     di,[dest]\r
+       asm     mov     dx,GC_INDEX+1\r
+\r
+       asm     mov     al,[BYTE PTR maskleft]\r
+       asm     out     dx,al           // mask off pixels\r
+\r
+       asm     mov     al,[BYTE PTR color]\r
+       asm     xchg    al,[es:di]      // load latches and write pixels\r
+\r
+       return;\r
+  }\r
+\r
+asm     mov     es,[screenseg]\r
+asm     mov     di,[dest]\r
+asm     mov     dx,GC_INDEX+1\r
+asm     mov     bh,[BYTE PTR color]\r
+\r
+//\r
+// draw left side\r
+//\r
+asm     mov     al,[BYTE PTR maskleft]\r
+asm     out     dx,al           // mask off pixels\r
+\r
+asm     mov     al,bh\r
+asm     xchg    al,[es:di]      // load latches and write pixels\r
+asm     inc     di\r
+\r
+//\r
+// draw middle\r
+//\r
+asm     mov     al,255\r
+asm     out     dx,al           // no masking\r
+\r
+asm     mov     al,bh\r
+asm     mov     cx,[mid]\r
+asm     rep     stosb\r
+\r
+//\r
+// draw right side\r
+//\r
+asm     mov     al,[BYTE PTR maskright]\r
+asm     out     dx,al           // mask off pixels\r
+asm     xchg    bh,[es:di]      // load latches and write pixels\r
+\r
+}\r
+\r
+//==========================================================================\r
+\r
+void DrawLineDot (int xl, int xh, int y,int color)\r
+{\r
+  unsigned dest,xlp,xlb,xhb,maskleft,maskright,maskdot,mid;\r
+\r
+  xlb=xl/8;\r
+  xhb=xh/8;\r
+\r
+  if (xh<xl)\r
+       Quit("DrawLine: xh<xl");\r
+  if (y<VIEWY)\r
+       Quit("DrawLine: y<VIEWY");\r
+  if (y>VIEWYH)\r
+       Quit("DrawLine: y>VIEWYH");\r
+\r
+       xlp = xl&7;\r
+       maskdot = dotmask[xlp];\r
+       maskleft = leftmask[xlp];\r
+       maskright = rightmask[xh&7];\r
+\r
+  mid = xhb-xlb-1;\r
+  dest = bufferofs+ylookup[y]+xlb;\r
+\r
+       //\r
+       // set the GC index register to point to the bit mask register\r
+       //\r
+       asm     mov     al,GC_BITMASK\r
+       asm     mov     dx,GC_INDEX\r
+       asm     out     dx,al\r
+\r
+  if (xlb==xhb)\r
+  {\r
+  //\r
+  // entire line is in one byte\r
+  //\r
+\r
+       maskleft&=maskright;\r
+\r
+       asm     mov     es,[screenseg]\r
+       asm     mov     di,[dest]\r
+       asm     mov     dx,GC_INDEX+1\r
+\r
+       asm     mov     al,[BYTE PTR maskleft]\r
+       asm     out     dx,al           // mask off pixels\r
+\r
+       asm     mov     al,[BYTE PTR color]\r
+       asm     xchg    al,[es:di]      // load latches and write pixels\r
+\r
+\r
+       //\r
+       // write the black dot at the start\r
+       //\r
+       asm     mov     al,[BYTE PTR maskdot]\r
+       asm     out     dx,al           // mask off pixels\r
+\r
+       asm     xor     al,al\r
+       asm     xchg    al,[es:di]      // load latches and write pixels\r
+\r
+\r
+       return;\r
+  }\r
+\r
+asm     mov     es,[screenseg]\r
+asm     mov     di,[dest]\r
+asm     mov     dx,GC_INDEX+1\r
+asm     mov     bh,[BYTE PTR color]\r
+\r
+//\r
+// draw left side\r
+//\r
+asm     mov     al,[BYTE PTR maskleft]\r
+asm     out     dx,al           // mask off pixels\r
+\r
+asm     mov     al,bh\r
+asm     xchg    al,[es:di]      // load latches and write pixels\r
+\r
+//\r
+// write the black dot at the start\r
+//\r
+asm     mov     al,[BYTE PTR maskdot]\r
+asm     out     dx,al           // mask off pixels\r
+asm     xor     al,al\r
+asm     xchg    al,[es:di]      // load latches and write pixels\r
+asm     inc     di\r
+\r
+//\r
+// draw middle\r
+//\r
+asm     mov     al,255\r
+asm     out     dx,al           // no masking\r
+\r
+asm     mov     al,bh\r
+asm     mov     cx,[mid]\r
+asm     rep     stosb\r
+\r
+//\r
+// draw right side\r
+//\r
+asm     mov     al,[BYTE PTR maskright]\r
+asm     out     dx,al           // mask off pixels\r
+asm     xchg    bh,[es:di]      // load latches and write pixels\r
+\r
+}\r
+\r
+#endif\r
+\r
+//==========================================================================\r
+\r
+\r
+long            wallscalesource;\r
+\r
+#ifdef DRAWEACH\r
+/*\r
+====================\r
+=\r
+= ScaleOneWall\r
+=\r
+====================\r
+*/\r
+\r
+void near ScaleOneWall (int xl, int xh)\r
+{\r
+       int     x,pixwidth,height;\r
+\r
+       *(((unsigned *)&wallscalesource)+1) = wallseg[xl];\r
+\r
+       for (x=xl;x<=xh;x+=pixwidth)\r
+       {\r
+               height = wallheight[x];\r
+               pixwidth = wallwidth[x];\r
+               (unsigned)wallscalesource = wallofs[x];\r
+\r
+               *(((unsigned *)&scaletablecall)+1) = (unsigned)scaledirectory[height];\r
+               (unsigned)scaletablecall = scaledirectory[height]->codeofs[0];\r
+\r
+               //\r
+               // scale a byte wide strip of wall\r
+               //\r
+               asm     mov     bx,[x]\r
+               asm     mov     di,bx\r
+               asm     shr     di,1\r
+               asm     shr     di,1\r
+               asm     shr     di,1                                            // X in bytes\r
+               asm     add     di,[bufferofs]\r
+               asm     and     bx,7\r
+               asm     shl     bx,1\r
+               asm     shl     bx,1\r
+               asm     shl     bx,1\r
+               asm     add     bx,[pixwidth]                           // bx = pixel*8+pixwidth-1\r
+               asm     dec     bx\r
+               asm     mov     al,BYTE PTR [bitmasks1+bx]\r
+               asm     mov     dx,GC_INDEX+1\r
+               asm     out     dx,al                                           // set bit mask register\r
+               asm     mov     es,[screenseg]\r
+               asm     lds     si,[wallscalesource]\r
+               asm     call [DWORD PTR ss:scaletablecall]              // scale the line of pixels\r
+\r
+               asm     mov     al,BYTE PTR [ss:bitmasks2+bx]\r
+               asm     or      al,al\r
+               asm     jz      nosecond\r
+\r
+               //\r
+               // draw a second byte for vertical strips that cross two bytes\r
+               //\r
+               asm     inc     di\r
+               asm     out     dx,al                                           // set bit mask register\r
+               asm     call [DWORD PTR ss:scaletablecall]      // scale the line of pixels\r
+       nosecond:\r
+               asm     mov     ax,ss\r
+               asm     mov     ds,ax\r
+       }\r
+}\r
+\r
+#endif\r
+\r
+char wall_anim_pos[NUMFLOORS];\r
+\r
+// EAST / WEST WALLS\r
+//\r
+int     far walllight1[NUMFLOORS] = {0,\r
+\r
+       CRYSTAL_LIGHT_1PIC,\r
+       CRYSTAL_LIGHT_2PIC,\r
+       CRYSTAL_LIGHT_3PIC,\r
+       CRYSTAL_LIGHT_4PIC,                     //4\r
+\r
+       FIRE_WALL_1PIC,\r
+       FIRE_WALL_2PIC,\r
+       FIRE_WALL_3PIC,\r
+       FIRE_WALL_4PIC,                         //8\r
+\r
+       BRN_STONE_GATEPIC,\r
+       BRN_STONE_WALL_1PIC,\r
+       KUDZU_WEAK_LIGHTPIC,\r
+       KUDZU_LIGHT_WALLPIC,\r
+       HEDGE_WALLPIC,\r
+       HEDGE_EYESPIC,                          //14\r
+\r
+       W_GEN_DOOR1PIC,                                 //15\r
+       BRN_WINDOW_LIGHTPIC,\r
+\r
+       ALTAR_LEFTPIC,\r
+       ALTAR_RIGHTPIC,\r
+       GRAY_LIGHT_WALLPIC,\r
+       GRAY_LIGHT_SIGNPIC,                     //20\r
+\r
+       MANICLE_LIGHT_WALLPIC,\r
+       MANICLE_LIGHT_BLOODYPIC,\r
+\r
+       LIGHT_CURTAIN_WINDOWPIC,\r
+       LIGHT_CURTAIN_WALLPIC,\r
+       BRN_LIGHT_SIGNPIC,                      //25\r
+\r
+       LIGHT_STONE_WALLPIC,\r
+\r
+       W_GEN_DOOR2PIC,                                 //27\r
+\r
+       TROLL_LIGHT_STONEPIC,\r
+\r
+       BRN_FLAGSTONE_LIGHT_2PIC,\r
+\r
+       W_CRYSTAL_DOORPIC,\r
+\r
+       DMG_BRN_FSTN_LTPIC,\r
+\r
+       RUST_METAL_LIGHTPIC,\r
+       GRAY_METAL_LIGHTPIC,                    //33\r
+\r
+       WEAK_STONE_LIGHTPIC,\r
+\r
+       DMG_FIN_FSTN_LTPIC,\r
+\r
+       WEAK_GRAY_RFGSTN_LIGHTPIC,\r
+       0,\r
+\r
+       WEAK_CRYSTAL_LIGHTPIC,\r
+\r
+       RED_MUD_LIGHTPIC,\r
+\r
+       STEEL_DOOR1PIC,                         //40\r
+\r
+       RED_MUD_WEAK_LIGHTPIC,\r
+\r
+       STEEL_DOOR2PIC,                         //42\r
+\r
+       HORN_DOORPIC,\r
+       TROLL_BLOODY_LT_STONEPIC,\r
+       CLOSED_DOOR_1PIC,\r
+\r
+       GRY_DOOR_LTPIC,                         //46\r
+\r
+       BRN_DOOR_LTPIC,                         //47\r
+\r
+       GRY_FGSTN_LTPIC,                           //48\r
+       DOOR_2PIC,\r
+\r
+       WATER_LIGHT_WEAK_1PIC,\r
+       WATER_LIGHT_WEAK_2PIC,\r
+       WATER_LIGHT_WEAK_3PIC,                  //52\r
+\r
+       WATER_LIGHT_1PIC,\r
+       WATER_LIGHT_2PIC,\r
+       WATER_LIGHT_3PIC,\r
+\r
+       LIGHT_BREATH_1PIC,\r
+       LIGHT_BREATH_2PIC,\r
+       LIGHT_BREATH_3PIC,                      //58\r
+\r
+       EXP_WALL_1PIC,\r
+       EXP_WALL_2PIC,\r
+       EXP_WALL_3PIC,\r
+\r
+       WATER_EXP_WALL_1PIC,\r
+       WATER_EXP_WALL_2PIC,\r
+       WATER_EXP_WALL_3PIC,                    //64\r
+\r
+       FINALWALLPIC,\r
+\r
+       LT_SKEL1PIC,\r
+       DK_SKEL1PIC,\r
+       LT_SKEL2PIC,\r
+       DK_SKEL2PIC,\r
+\r
+       0,\r
+\r
+       TAP_1PIC,\r
+       TAP_2PIC,\r
+       TAP_3PIC,\r
+       TAP_4PIC,\r
+       TAP_5PIC,\r
+\r
+       WATER_DOOR1_PIC,\r
+       WATER_DOOR2_PIC,\r
+       };\r
+\r
+// NORTH / SOUTH WALLS\r
+//\r
+int     far walldark1[NUMFLOORS] = {0,\r
+\r
+       CRYSTAL_DARK_1PIC,\r
+       CRYSTAL_DARK_2PIC,\r
+       CRYSTAL_DARK_3PIC,\r
+       CRYSTAL_DARK_4PIC,                      //4\r
+\r
+       FIRE_WALL_1PIC,\r
+       FIRE_WALL_2PIC,\r
+       FIRE_WALL_3PIC,\r
+       FIRE_WALL_4PIC,                         //8\r
+\r
+       BRN_STONE_GATEPIC,\r
+       BRN_STONE_WALL_2PIC,\r
+       KUDZU_WEAK_DARKPIC,\r
+       KUDZU_DARK_WALLPIC,\r
+       HEDGE_WALLPIC,\r
+       HEDGE_EYESPIC,                          //14\r
+\r
+       W_GEN_DOOR1PIC,                         //15\r
+       BRN_WINDOW_DARKPIC,\r
+\r
+       ALTAR_LEFTPIC,\r
+       ALTAR_RIGHTPIC,\r
+       GRAY_DARK_WALLPIC,\r
+       GRAY_DARK_SIGNPIC,                      //20\r
+\r
+       MANICLE_DARK_WALLPIC,\r
+       MANICLE_DARK_BLOODYPIC,\r
+\r
+       DARK_CURTAIN_WINDOWPIC,\r
+       DARK_CURTAIN_WALLPIC,\r
+       BRN_DARK_SIGNPIC,\r
+\r
+       DARK_STONE_WALLPIC,\r
+\r
+       W_GEN_DOOR2PIC,                         //27\r
+\r
+       TROLL_DARK_STONEPIC,\r
+\r
+       BRN_FLAGSTONE_DARK_2PIC,\r
+\r
+       W_CRYSTAL_DOORPIC,                              //30\r
+\r
+       DMG_BRN_FSTN_DKPIC,\r
+\r
+       RUST_METAL_DARKPIC,\r
+       GRAY_METAL_DARKPIC,\r
+\r
+       WEAK_STONE_DARKPIC,\r
+\r
+       DMG_FIN_FSTN_DKPIC,                     //35\r
+\r
+       WEAK_GRAY_RFGSTN_DARKPIC,\r
+       0,\r
+\r
+       WEAK_CRYSTAL_DARKPIC,\r
+\r
+       BRN_MUD_DARKPIC,\r
+\r
+       STEEL_DOOR1PIC,                         //40\r
+\r
+       BRN_MUD_WEAK_DARKPIC,\r
+\r
+       STEEL_DOOR2PIC,\r
+\r
+       HORN_DOORPIC,\r
+       TROLL_BLOODY_DK_STONEPIC,\r
+\r
+       CLOSED_DOOR_1PIC,\r
+\r
+       GRY_DOOR_DKPIC,                         //46\r
+       BRN_DOOR_DKPIC,                         //47\r
+       GRY_FGSTN_DKPIC,                                //48\r
+       DOOR_2PIC,\r
+\r
+       WATER_DARK_WEAK_1PIC,\r
+       WATER_DARK_WEAK_2PIC,\r
+       WATER_DARK_WEAK_3PIC,\r
+\r
+       WATER_DARK_1PIC,\r
+       WATER_DARK_2PIC,\r
+       WATER_DARK_3PIC,\r
+\r
+       DARK_BREATH_1PIC,\r
+       DARK_BREATH_2PIC,\r
+       DARK_BREATH_3PIC,\r
+\r
+       EXP_WALL_1PIC,\r
+       EXP_WALL_2PIC,\r
+       EXP_WALL_3PIC,\r
+\r
+       WATER_EXP_WALL_1PIC,\r
+       WATER_EXP_WALL_2PIC,\r
+       WATER_EXP_WALL_3PIC,\r
+\r
+       FINALWALLPIC,\r
+\r
+       LT_SKEL1PIC,\r
+       DK_SKEL1PIC,\r
+       LT_SKEL2PIC,\r
+       DK_SKEL2PIC,\r
+\r
+       0,\r
+\r
+       TAP_1PIC,\r
+       TAP_2PIC,\r
+       TAP_3PIC,\r
+       TAP_4PIC,\r
+       TAP_5PIC,\r
+\r
+       WATER_DOOR1_PIC,\r
+       WATER_DOOR2_PIC,\r
+       };\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= DrawVWall\r
+=\r
+= Draws a wall by vertical segments, for texture mapping!\r
+=\r
+= wallptr->side is true for east/west walls (constant x)\r
+=\r
+= fracheight and fracstep are 16.16 bit fractions\r
+=\r
+=====================\r
+*/\r
+\r
+void DrawVWall (walltype *wallptr)\r
+{\r
+       int                     x,i;\r
+       unsigned        source;\r
+       unsigned        width,sourceint;\r
+       unsigned        wallpic,wallpicseg;\r
+       unsigned        skip;\r
+       long            fracheight,fracstep,longheightchange;\r
+       unsigned        height;\r
+       int                     heightchange;\r
+       unsigned        slope,distance;\r
+       int                     traceangle,angle;\r
+       int                     mapadd;\r
+       unsigned        lastpix,lastsource,lastwidth;\r
+\r
+       if (wallptr->rightclip < wallptr->leftclip)\r
+               Quit ("DrawVWall: Right < Left");\r
+\r
+//\r
+// setup for height calculation\r
+//\r
+       wallptr->height1 >>= 1;\r
+       wallptr->height2 >>= 1;\r
+       wallptr->planecoord>>=10;                       // remove non significant bits\r
+\r
+       width = wallptr->x2 - wallptr->x1;\r
+       if (width)\r
+       {\r
+               heightchange = wallptr->height2 - wallptr->height1;\r
+               asm     mov     ax,[heightchange]\r
+               asm     mov     WORD PTR [longheightchange+2],ax\r
+               asm     mov     WORD PTR [longheightchange],0   // avoid long shift by 16\r
+               fracstep = longheightchange/width;\r
+       }\r
+\r
+       fracheight = ((long)wallptr->height1<<16)+0x8000;\r
+       skip = wallptr->leftclip - wallptr->x1;\r
+       if (skip)\r
+               fracheight += fracstep*skip;\r
+\r
+//\r
+// setup for texture mapping\r
+//\r
+// mapadd is 64*64 (to keep source positive) + the origin wall intercept\r
+// distance has 6 unit bits, and 6 frac bits\r
+// traceangle is the center view angle in FINEANGLES, moved to be in\r
+// the +-90 degree range (to thew right of origin)\r
+//\r
+       traceangle = fineviewangle;\r
+       //\r
+       // find wall picture to map from\r
+       //\r
+       if (wallptr->side)\r
+       {       // east or west wall\r
+\r
+               wallpic = walllight1[wallptr->color+wall_anim_pos[wallptr->color]];\r
+               if (wallptr->planecoord < viewxpix)\r
+               {\r
+                       distance = viewxpix-wallptr->planecoord;\r
+                       traceangle -= FINEANGLES/2;\r
+                       mapadd = (64-viewypix&63);              // the pixel spot of the origin\r
+               }\r
+               else\r
+               {\r
+                       distance = wallptr->planecoord-viewxpix;\r
+                       // traceangle is correct\r
+                       mapadd = viewypix&63;           // the pixel spot of the origin\r
+               }\r
+       }\r
+       else\r
+       {       // north or south wall\r
+\r
+               wallpic = walldark1[wallptr->color+wall_anim_pos[wallptr->color]];\r
+               if (wallptr->planecoord < viewypix)\r
+               {\r
+                       distance = viewypix-wallptr->planecoord;\r
+                       traceangle -= FINEANGLES/4;\r
+                       mapadd = viewxpix&63;           // the pixel spot of the origin\r
+               }\r
+               else\r
+               {\r
+                       distance = wallptr->planecoord-viewypix;\r
+                       traceangle -= FINEANGLES*3/4;\r
+                       mapadd = (64-viewxpix&63);              // the pixel spot of the origin\r
+               }\r
+       }\r
+\r
+       mapadd = 64*64-mapadd;                          // make sure it stays positive\r
+\r
+       wallpicseg = (unsigned)walldirectory[wallpic-FIRSTWALLPIC];\r
+       if (traceangle > FINEANGLES/2)\r
+               traceangle -= FINEANGLES;\r
+\r
+//\r
+// calculate everything\r
+//\r
+// IMPORTANT!  This loop is executed around 5000 times / second!\r
+//\r
+       lastpix = lastsource = (unsigned)-1;\r
+\r
+       for (x = wallptr->leftclip ; x <= wallptr->rightclip ; x++)\r
+       {\r
+               //\r
+               // height\r
+               //\r
+               asm     mov     ax,WORD PTR [fracheight]\r
+               asm     mov     dx,WORD PTR [fracheight+2]\r
+               asm     mov     cx,dx\r
+               asm     add     ax,WORD PTR [fracstep]\r
+               asm     adc     dx,WORD PTR [fracstep+2]\r
+               asm     mov     WORD PTR [fracheight],ax\r
+               asm     mov     WORD PTR [fracheight+2],dx\r
+               asm     mov     bx,[x]\r
+               asm     shl     bx,1\r
+               asm     cmp     cx,MAXSCALEHEIGHT\r
+               asm     jbe     storeheight\r
+               asm     mov     cx,MAXSCALEHEIGHT\r
+storeheight:\r
+               asm     mov WORD PTR [wallheight+bx],cx\r
+               asm     mov WORD PTR [zbuffer+bx],cx\r
+\r
+//              height = fracheight>>16;\r
+//              fracheight += fracstep;\r
+//              if (height > MAXSCALEHEIGHT)\r
+//                      height = MAXSCALEHEIGHT;\r
+//              wallheight[x] = zbuffer[x] = height;\r
+\r
+               //\r
+               // texture map\r
+               //\r
+               angle = pixelangle[x]+traceangle;\r
+               if (angle<0)\r
+                       angle+=FINEANGLES;\r
+\r
+               slope = finetangent[angle];\r
+\r
+//\r
+// distance is an unsigned 6.6 bit number (12 pixel bits)\r
+// slope is a signed 5.10 bit number\r
+// result is a signed 11.16 bit number\r
+//\r
+\r
+#if 0\r
+               source = distance*slope;\r
+               source >>=20;\r
+\r
+               source += mapadd;\r
+               source &= 63;                           // mask off the unused units\r
+               source = 63-source;\r
+               source <<= 6;                           // multiply by 64 for offset into pic\r
+#endif\r
+               asm     mov     ax,[distance]\r
+               asm     imul    [slope]                 // ax is the source pixel\r
+               asm     mov     al,ah\r
+               asm     shr     al,1\r
+               asm     shr     al,1                            // low 6 bits is now pixel number\r
+               asm     add     ax,[mapadd]\r
+               asm     and ax,63\r
+               asm     mov     dx,63\r
+               asm     sub     dx,ax                           // otherwise it is backwards\r
+               asm     shl     dx,1\r
+               asm     shl     dx,1\r
+               asm     shl     dx,1\r
+               asm     shl     dx,1\r
+               asm     shl     dx,1\r
+               asm     shl     dx,1                            // *64 to index into shape\r
+               asm     mov     [source],dx\r
+\r
+               if (source != lastsource)\r
+               {\r
+                       if (lastpix != (unsigned)-1)\r
+                       {\r
+                               wallofs[lastpix] = lastsource;\r
+                               wallseg[lastpix] = wallpicseg;\r
+                               wallwidth[lastpix] = lastwidth;\r
+                       }\r
+                       lastpix = x;\r
+                       lastsource = source;\r
+                       lastwidth = 1;\r
+               }\r
+               else\r
+                       lastwidth++;                    // optimized draw, same map as last one\r
+       }\r
+       wallofs[lastpix] = lastsource;\r
+       wallseg[lastpix] = wallpicseg;\r
+       wallwidth[lastpix] = lastwidth;\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= TraceRay\r
+=\r
+= Used to find the left and rightmost tile in the view area to be traced from\r
+= Follows a ray of the given angle from viewx,viewy in the global map until\r
+= it hits a solid tile\r
+= sets:\r
+=   tile.x,tile.y       : tile coordinates of contacted tile\r
+=   tilecolor   : solid tile's color\r
+=\r
+==================\r
+*/\r
+\r
+int tilecolor;\r
+\r
+void TraceRay (unsigned angle)\r
+{\r
+  long tracex,tracey,tracexstep,traceystep,searchx,searchy;\r
+  fixed fixtemp;\r
+  int otx,oty,searchsteps;\r
+\r
+  tracexstep = costable[angle];\r
+  traceystep = sintable[angle];\r
+\r
+//\r
+// advance point so it is even with the view plane before we start checking\r
+//\r
+  fixtemp = FixedByFrac(prestep,tracexstep);\r
+  tracex = viewx+fixtemp;\r
+  fixtemp = FixedByFrac(prestep,traceystep);\r
+  tracey = viewy-fixtemp;\r
+\r
+  tile.x = tracex>>TILESHIFT;   // starting point in tiles\r
+  tile.y = tracey>>TILESHIFT;\r
+\r
+\r
+  if (tracexstep<0)                     // use 2's complement, not signed magnitude\r
+       tracexstep = -(tracexstep&0x7fffffff);\r
+\r
+  if (traceystep<0)                     // use 2's complement, not signed magnitude\r
+       traceystep = -(traceystep&0x7fffffff);\r
+\r
+//\r
+// we assume viewx,viewy is not inside a solid tile, so go ahead one step\r
+//\r
+\r
+  do    // until a solid tile is hit\r
+  {\r
+    otx = tile.x;\r
+       oty = tile.y;\r
+       spotvis[otx][oty] = true;\r
+       tracex += tracexstep;\r
+    tracey -= traceystep;\r
+    tile.x = tracex>>TILESHIFT;\r
+       tile.y = tracey>>TILESHIFT;\r
+\r
+       if (tile.x!=otx && tile.y!=oty && (tilemap[otx][tile.y] || tilemap[tile.x][oty]) )\r
+    {\r
+      //\r
+         // trace crossed two solid tiles, so do a binary search along the line\r
+         // to find a spot where only one tile edge is crossed\r
+      //\r
+      searchsteps = 0;\r
+      searchx = tracexstep;\r
+      searchy = traceystep;\r
+      do\r
+      {\r
+       searchx/=2;\r
+       searchy/=2;\r
+       if (tile.x!=otx && tile.y!=oty)\r
+       {\r
+        // still too far\r
+         tracex -= searchx;\r
+         tracey += searchy;\r
+       }\r
+       else\r
+       {\r
+        // not far enough, no tiles crossed\r
+         tracex += searchx;\r
+         tracey -= searchy;\r
+       }\r
+\r
+       //\r
+       // if it is REAL close, go for the most clockwise intersection\r
+       //\r
+       if (++searchsteps == 16)\r
+       {\r
+         tracex = (long)otx<<TILESHIFT;\r
+         tracey = (long)oty<<TILESHIFT;\r
+         if (tracexstep>0)\r
+         {\r
+               if (traceystep<0)\r
+               {\r
+                 tracex += TILEGLOBAL-1;\r
+                 tracey += TILEGLOBAL;\r
+               }\r
+               else\r
+               {\r
+                 tracex += TILEGLOBAL;\r
+               }\r
+         }\r
+         else\r
+         {\r
+               if (traceystep<0)\r
+               {\r
+                 tracex --;\r
+                 tracey += TILEGLOBAL-1;\r
+               }\r
+               else\r
+               {\r
+                 tracey --;\r
+               }\r
+         }\r
+       }\r
+\r
+       tile.x = tracex>>TILESHIFT;\r
+       tile.y = tracey>>TILESHIFT;\r
+\r
+         } while (( tile.x!=otx && tile.y!=oty) || (tile.x==otx && tile.y==oty) );\r
+       }\r
+  } while (!(tilecolor = tilemap[tile.x][tile.y]) );\r
+\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+========================\r
+=\r
+= FixedByFrac\r
+=\r
+= multiply a 16/16 bit, 2's complement fixed point number by a 16 bit\r
+= fraction, passed as a signed magnitude 32 bit number\r
+=\r
+========================\r
+*/\r
+\r
+#pragma warn -rvl                       // I stick the return value in with ASMs\r
+\r
+fixed FixedByFrac (fixed a, fixed b)\r
+{\r
+  fixed value;\r
+\r
+//\r
+// setup\r
+//\r
+asm     mov     si,[WORD PTR b+2]       // sign of result = sign of fraction\r
+\r
+asm     mov     ax,[WORD PTR a]\r
+asm     mov     cx,[WORD PTR a+2]\r
+\r
+asm     or      cx,cx\r
+asm     jns     aok:                            // negative?\r
+asm     not     ax\r
+asm     not     cx\r
+asm     add     ax,1\r
+asm     adc     cx,0\r
+asm     xor     si,0x8000                       // toggle sign of result\r
+aok:\r
+\r
+//\r
+// multiply  cx:ax by bx\r
+//\r
+asm     mov     bx,[WORD PTR b]\r
+asm     mul     bx                                      // fraction*fraction\r
+asm     mov     di,dx                           // di is low word of result\r
+asm     mov     ax,cx                           //\r
+asm     mul     bx                                      // units*fraction\r
+asm add ax,di\r
+asm     adc     dx,0\r
+\r
+//\r
+// put result dx:ax in 2's complement\r
+//\r
+asm     test    si,0x8000               // is the result negative?\r
+asm     jz      ansok:\r
+asm     not     ax\r
+asm     not     dx\r
+asm     add     ax,1\r
+asm     adc     dx,0\r
+\r
+ansok:;\r
+\r
+}\r
+\r
+#pragma warn +rvl\r
+\r
+#if 0\r
+/*\r
+=========================\r
+=\r
+= FixedAdd\r
+=\r
+= add two 16 bit fixed point numbers\r
+= to subtract, invert the sign of B before invoking\r
+=\r
+=========================\r
+*/\r
+\r
+fixed FixedAdd (fixed a, fixed b)\r
+{\r
+  fixed value;\r
+\r
+asm     mov     ax,[WORD PTR a]\r
+asm     mov     dx,[WORD PTR a+2]\r
+\r
+asm     mov     bx,[WORD PTR b]\r
+asm     mov     cx,[WORD PTR b+2]\r
+\r
+asm     or      dx,dx\r
+asm     jns     aok:            // negative?\r
+asm     and     dx,0x7fff\r
+asm     not     ax              // convert a from signed magnitude to 2's compl\r
+asm     not     dx\r
+asm     add     ax,1\r
+asm     adc     dx,0\r
+aok:\r
+\r
+asm     or      cx,cx\r
+asm     jns     bok:            // negative?\r
+asm     and     cx,0x7fff\r
+asm     not     bx              // convert b from signed magnitude to 2's compl\r
+asm     not     cx\r
+asm     add     bx,1\r
+asm     adc     cx,0\r
+bok:\r
+\r
+asm     add     ax,bx           // perform the addition\r
+asm     adc     dx,cx\r
+asm     jns     done\r
+\r
+asm     and     dx,0x7fff       // value was negative\r
+asm     not     ax              // back to signed magnitude\r
+asm     not     dx\r
+asm     add     ax,1\r
+asm     adc     dx,0\r
+\r
+done:\r
+\r
+asm     mov     [WORD PTR value],ax\r
+asm     mov     [WORD PTR value+2],dx\r
+\r
+  return value;\r
+}\r
+#endif\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+========================\r
+=\r
+= TransformPoint\r
+=\r
+= Takes paramaters:\r
+=   gx,gy               : globalx/globaly of point\r
+=\r
+= globals:\r
+=   viewx,viewy         : point of view\r
+=   viewcos,viewsin     : sin/cos of viewangle\r
+=\r
+=\r
+= defines:\r
+=   CENTERX             : pixel location of center of view window\r
+=   TILEGLOBAL          : size of one\r
+=   FOCALLENGTH         : distance behind viewx/y for center of projection\r
+=   scale               : conversion from global value to screen value\r
+=\r
+= returns:\r
+=   screenx,screenheight: projected edge location and size\r
+=\r
+========================\r
+*/\r
+\r
+void TransformPoint (fixed gx, fixed gy, int *screenx, unsigned *screenheight)\r
+{\r
+  int ratio;\r
+  fixed gxt,gyt,nx,ny;\r
+\r
+//\r
+// translate point to view centered coordinates\r
+//\r
+  gx = gx-viewx;\r
+  gy = gy-viewy;\r
+\r
+//\r
+// calculate newx\r
+//\r
+  gxt = FixedByFrac(gx,viewcos);\r
+  gyt = FixedByFrac(gy,viewsin);\r
+  nx = gxt-gyt;\r
+\r
+//\r
+// calculate newy\r
+//\r
+  gxt = FixedByFrac(gx,viewsin);\r
+  gyt = FixedByFrac(gy,viewcos);\r
+  ny = gyt+gxt;\r
+\r
+//\r
+// calculate perspective ratio\r
+//\r
+  if (nx<0)\r
+       nx = 0;\r
+\r
+  ratio = nx*scale/FOCALLENGTH;\r
+\r
+  if (ratio<=MINRATIO)\r
+       ratio = MINRATIO;\r
+\r
+  *screenx = CENTERX + ny/ratio;\r
+\r
+  *screenheight = TILEGLOBAL/ratio;\r
+\r
+}\r
+\r
+\r
+//\r
+// transform actor\r
+//\r
+void TransformActor (objtype *ob)\r
+{\r
+  int ratio;\r
+  fixed gx,gy,gxt,gyt,nx,ny;\r
+\r
+//\r
+// translate point to view centered coordinates\r
+//\r
+  gx = ob->x-viewx;\r
+  gy = ob->y-viewy;\r
+\r
+//\r
+// calculate newx\r
+//\r
+  gxt = FixedByFrac(gx,viewcos);\r
+  gyt = FixedByFrac(gy,viewsin);\r
+  nx = gxt-gyt-ob->size;\r
+\r
+//\r
+// calculate newy\r
+//\r
+  gxt = FixedByFrac(gx,viewsin);\r
+  gyt = FixedByFrac(gy,viewcos);\r
+  ny = gyt+gxt;\r
+\r
+//\r
+// calculate perspective ratio\r
+//\r
+  if (nx<0)\r
+       nx = 0;\r
+\r
+  ratio = nx*scale/FOCALLENGTH;\r
+\r
+  if (ratio<=MINRATIO)\r
+       ratio = MINRATIO;\r
+\r
+  ob->viewx = CENTERX + ny/ratio;\r
+\r
+  ob->viewheight = TILEGLOBAL/ratio;\r
+}\r
+\r
+//==========================================================================\r
+\r
+fixed TransformX (fixed gx, fixed gy)\r
+{\r
+  int ratio;\r
+  fixed gxt,gyt,nx,ny;\r
+\r
+//\r
+// translate point to view centered coordinates\r
+//\r
+  gx = gx-viewx;\r
+  gy = gy-viewy;\r
+\r
+//\r
+// calculate newx\r
+//\r
+  gxt = FixedByFrac(gx,viewcos);\r
+  gyt = FixedByFrac(gy,viewsin);\r
+\r
+  return gxt-gyt;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+==================\r
+=\r
+= BuildTables\r
+=\r
+= Calculates:\r
+=\r
+= scale                 projection constant\r
+= sintable/costable     overlapping fractional tables\r
+= firstangle/lastangle  angles from focalpoint to left/right view edges\r
+= prestep               distance from focal point before checking for tiles\r
+=\r
+==================\r
+*/\r
+\r
+void BuildTables (void)\r
+{\r
+  int           i;\r
+  long          intang;\r
+  long          x;\r
+  float         angle,anglestep,radtoint;\r
+  double        tang;\r
+  fixed         value;\r
+\r
+//\r
+// calculate the angle offset from view angle of each pixel's ray\r
+//\r
+       radtoint = (float)FINEANGLES/2/PI;\r
+       for (i=0;i<VIEWWIDTH/2;i++)\r
+       {\r
+       // start 1/2 pixel over, so viewangle bisects two middle pixels\r
+               x = (TILEGLOBAL*i+TILEGLOBAL/2)/VIEWWIDTH;\r
+               tang = (float)x/(FOCALLENGTH+MINDIST);\r
+               angle = atan(tang);\r
+               intang = angle*radtoint;\r
+               pixelangle[VIEWWIDTH/2-1-i] = intang;\r
+               pixelangle[VIEWWIDTH/2+i] = -intang;\r
+       }\r
+\r
+//\r
+// calculate fine tangents\r
+// 1 sign bit, 5 units (clipped to), 10 fracs\r
+//\r
+#define MININT  (-MAXINT)\r
+\r
+       for (i=0;i<FINEANGLES/4;i++)\r
+       {\r
+               intang = tan(i/radtoint)*(1l<<10);\r
+\r
+               //\r
+               // if the tangent is not reprentable in this many bits, bound the\r
+               // units part ONLY\r
+               //\r
+               if (intang>MAXINT)\r
+                       intang = 0x8f00 | (intang & 0xff);\r
+               else if (intang<MININT)\r
+                       intang = 0xff00 | (intang & 0xff);\r
+\r
+               finetangent[i] = intang;\r
+//              finetangent[FINEANGLES/2+i] = intang;\r
+//              finetangent[FINEANGLES/2-i-1] = -intang;\r
+               finetangent[FINEANGLES-i-1] = -intang;\r
+       }\r
+\r
+//\r
+// calculate scale value so one tile at mindist allmost fills the view horizontally\r
+//\r
+  scale = GLOBAL1/VIEWWIDTH;\r
+  scale *= focallength;\r
+  scale /= (focallength+mindist);\r
+\r
+//\r
+// costable overlays sintable with a quarter phase shift\r
+// ANGLES is assumed to be divisable by four\r
+//\r
+// The low word of the value is the fraction, the high bit is the sign bit,\r
+// bits 16-30 should be 0\r
+//\r
+\r
+  angle = 0;\r
+  anglestep = PI/2/ANGLEQUAD;\r
+  for (i=0;i<=ANGLEQUAD;i++)\r
+  {\r
+       value=GLOBAL1*sin(angle);\r
+       sintable[i]=\r
+         sintable[i+ANGLES]=\r
+         sintable[ANGLES/2-i] = value;\r
+       sintable[ANGLES-i]=\r
+         sintable[ANGLES/2+i] = value | 0x80000000l;\r
+       angle += anglestep;\r
+  }\r
+\r
+//\r
+// figure trace angles for first and last pixel on screen\r
+//\r
+  angle = atan((float)VIEWWIDTH/2*scale/FOCALLENGTH);\r
+  angle *= ANGLES/(PI*2);\r
+\r
+  intang = (int)angle+1;\r
+  firstangle = intang;\r
+  lastangle = -intang;\r
+\r
+  prestep = GLOBAL1*((float)FOCALLENGTH/costable[firstangle]);\r
+\r
+//\r
+// misc stuff\r
+//\r
+  walls[0].x2 = VIEWX-1;\r
+  walls[0].height2 = 32000;\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= ClearScreen\r
+=\r
+=====================\r
+*/\r
+\r
+void ClearScreen (void)\r
+{\r
+       unsigned topcolor=*skycolor, bottomcolor=*groundcolor;\r
+       unsigned topimage=topcolor&0xf0,bottomimage=bottomcolor&0xf0;\r
+       unsigned pfoffset=0;\r
+\r
+\r
+#if USE_STRIPS\r
+       if (topimage == 0x20)           // special code for lightning\r
+               topimage = topcolor = 0;\r
+\r
+// Manually wipe screen with solid color.\r
+// If BOTH sky and ground are 'images' don't manually clear it!\r
+//\r
+       if ((!topimage) || (!bottomimage))\r
+       {\r
+#endif\r
+\r
+  //\r
+  // clear the screen\r
+  //\r
+asm     mov     dx,GC_INDEX\r
+asm     mov     ax,GC_MODE + 256*2              // read mode 0, write mode 2\r
+asm     out     dx,ax\r
+asm     mov     ax,GC_BITMASK + 255*256\r
+asm     out     dx,ax\r
+\r
+//asm     mov     dx,40-VIEWWIDTH/8                                    // dx = modulo\r
+asm     mov     bl,VIEWWIDTH/16\r
+asm     mov     bh,CENTERY+1\r
+\r
+asm     mov     ax,topcolor\r
+asm     mov     es,[screenseg]\r
+asm     mov     di,[bufferofs]\r
+asm     add     di,((SCREENWIDTH*VIEWY)+(VIEWX/8))\r
+\r
+toploop:\r
+asm     mov     cl,bl\r
+asm     rep     stosw\r
+asm     stosb\r
+//asm     add     di,dx                                        // no need to add "0" modulo\r
+asm     dec     bh\r
+asm     jnz     toploop\r
+\r
+asm     mov     bh,CENTERY+1\r
+asm     mov     ax,bottomcolor\r
+\r
+bottomloop:\r
+asm     mov     cl,bl\r
+asm     rep     stosw\r
+asm     stosb\r
+//asm     add     di,dx                                        // no need to add "0" modulo\r
+asm     dec     bh\r
+asm     jnz     bottomloop\r
+\r
+#if USE_STRIPS\r
+       }\r
+\r
+\r
+//\r
+// code to test parallax turning\r
+//\r
+\r
+       if (topimage)\r
+       {\r
+               topimage -= 16;\r
+               pfoffset = LONG_PERCENTAGE(3200,359,(359-player->angle),12);\r
+               while (pfoffset >= 640)\r
+                       pfoffset -= 640;\r
+               LatchDrawPicStrip(0,0,SKY1PIC+topimage,pfoffset+8);\r
+       }\r
+\r
+       if (bottomimage)\r
+       {\r
+////           pfoffset = LONG_PERCENTAGE(3200,359,(359-player->angle),12)+320;\r
+//             pfoffset += 320;\r
+//             while (pfoffset >= 640)\r
+//                     pfoffset -= 640;\r
+//             LatchDrawPicStrip(0,64,SKY1PIC+topimage,pfoffset+8);\r
+               bottomimage -= 16;\r
+               LatchDrawPic(0,64,GND1PIC+bottomimage);\r
+       }\r
+#endif\r
+\r
+\r
+asm     mov     dx,GC_INDEX\r
+asm     mov     ax,GC_MODE + 256*10             // read mode 1, write mode 2\r
+asm     out     dx,ax\r
+asm     mov     al,GC_BITMASK\r
+asm     out     dx,al\r
+\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= DrawWallList\r
+=\r
+= Clips and draws all the walls traced this refresh\r
+=\r
+=====================\r
+*/\r
+\r
+void DrawWallList (void)\r
+{\r
+       int i,leftx,newleft,rightclip;\r
+       walltype *wall, *check;\r
+\r
+asm     mov     ax,ds\r
+asm     mov     es,ax\r
+asm     mov     di,OFFSET wallwidth\r
+asm     xor     ax,ax\r
+asm     mov     cx,VIEWWIDTH/2\r
+asm     rep     stosw\r
+\r
+       ClearScreen ();\r
+\r
+       rightwall->x1 = VIEWXH+1;\r
+       rightwall->height1 = 32000;\r
+       (rightwall+1)->x1 = 32000;\r
+\r
+       leftx = -1;\r
+\r
+       for (wall=&walls[1];wall<rightwall && leftx<=VIEWXH ;wall++)\r
+       {\r
+         if (leftx >= wall->x2)\r
+               continue;\r
+\r
+         rightclip = wall->x2;\r
+\r
+         check = wall+1;\r
+         while (check->x1 <= rightclip && check->height1 >= wall->height2)\r
+         {\r
+               rightclip = check->x1-1;\r
+               check++;\r
+         }\r
+\r
+         if (rightclip>VIEWXH)\r
+               rightclip=VIEWXH;\r
+\r
+         if (leftx < wall->x1 - 1)\r
+               newleft = wall->x1-1;           // there was black space between walls\r
+         else\r
+               newleft = leftx;\r
+\r
+         if (rightclip > newleft)\r
+         {\r
+               wall->leftclip = newleft+1;\r
+               wall->rightclip = rightclip;\r
+               DrawVWall (wall);\r
+               leftx = rightclip;\r
+         }\r
+       }\r
+\r
+#ifndef DRAWEACH\r
+       ScaleWalls ();                                  // draw all the walls\r
+#endif\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= DrawScaleds\r
+=\r
+= Draws all objects that are visable\r
+=\r
+=====================\r
+*/\r
+\r
+objtype *depthsort[MAXACTORS];\r
+\r
+void DrawScaleds (void)\r
+{\r
+#if USE_INERT_LIST\r
+               extern inertobjtype inertobjlist[], *inert;\r
+\r
+               boolean inertlist=false;\r
+#endif\r
+       int             i,j,least,numvisable,height;\r
+       objtype         *obj,**vislist,*farthest;\r
+       memptr          shape;\r
+       byte            *tilespot,*visspot;\r
+\r
+       numvisable = 0;\r
+\r
+//\r
+// calculate base positions of all objects\r
+//\r
+       vislist = &depthsort[0];\r
+\r
+       obj = player->next;\r
+       while (obj)\r
+       {\r
+               tilespot = &tilemap[0][0]+(obj->tilex<<6)+obj->tiley;\r
+               visspot = &spotvis[0][0]+(obj->tilex<<6)+obj->tiley;\r
+               //\r
+               // could be in any of the nine surrounding tiles\r
+               //\r
+               if (*visspot\r
+               || ( *(visspot-1) && !*(tilespot-1) )\r
+               || ( *(visspot+1) && !*(tilespot+1) )\r
+               || ( *(visspot-65) && !*(tilespot-65) )\r
+               || ( *(visspot-64) && !*(tilespot-64) )\r
+               || ( *(visspot-63) && !*(tilespot-63) )\r
+               || ( *(visspot+65) && !*(tilespot+65) )\r
+               || ( *(visspot+64) && !*(tilespot+64) )\r
+               || ( *(visspot+63) && !*(tilespot+63) ) )\r
+               {\r
+#if USE_INERT_LIST\r
+                       if (!inertlist)\r
+#endif\r
+                               if ((obj->active == noalways) || (obj->active == always))\r
+                                       obj->active = always;\r
+                               else\r
+                                       obj->active = yes;\r
+                       TransformActor (obj);\r
+                       if (!obj->viewheight || obj->viewheight > VIEWWIDTH)\r
+                               goto cont;                       // too close or far away\r
+\r
+                       if (!obj->state->shapenum)\r
+                               goto cont;\r
+\r
+                       *vislist++ = obj;\r
+                       numvisable++;\r
+               }\r
+               else\r
+#if USE_INERT_LIST\r
+                       if (!inertlist)\r
+#endif\r
+                               if ((obj->active != always) && (obj->active != noalways))\r
+                                       obj->active = no;\r
+\r
+cont:;\r
+               obj = obj->next;\r
+#if USE_INERT_LIST\r
+               if ((!obj) && (!inertlist))\r
+               {\r
+                       if (inert != inertobjlist)\r
+                               obj = (objtype *)inertobjlist;\r
+                       inertlist = true;\r
+               }\r
+#endif\r
+       }\r
+\r
+       if (vislist == &depthsort[0])\r
+               return;                                         // no visable objects\r
+\r
+//\r
+// draw from back to front\r
+//\r
+       for (i = 0; i<numvisable; i++)\r
+       {\r
+               least = 32000;\r
+               for (j=0;j<numvisable;j++)\r
+               {\r
+                       height = depthsort[j]->viewheight;\r
+                       if (height < least)\r
+                       {\r
+                               least = height;\r
+                               farthest = depthsort[j];\r
+                       }\r
+               }\r
+               //\r
+               // draw farthest\r
+               //\r
+               shape = shapedirectory[farthest->state->shapenum-FIRSTSCALEPIC];\r
+               ScaleShape(farthest->viewx,shape,farthest->viewheight);\r
+               farthest->viewheight = 32000;\r
+       }\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= CalcTics\r
+=\r
+=====================\r
+*/\r
+\r
+void CalcTics (void)\r
+{\r
+       long    newtime,oldtimecount;\r
+\r
+\r
+#ifdef PROFILE\r
+       tics = 1;\r
+       return;\r
+#endif\r
+\r
+//\r
+// calculate tics since last refresh for adaptive timing\r
+//\r
+       if (lasttimecount > TimeCount)\r
+               TimeCount = lasttimecount;              // if the game was paused a LONG time\r
+\r
+#if 0\r
+       if (DemoMode)                                   // demo recording and playback needs\r
+       {                                                               // to be constant\r
+//\r
+// take DEMOTICS or more tics, and modify Timecount to reflect time taken\r
+//\r
+               oldtimecount = lasttimecount;\r
+               while (TimeCount<oldtimecount+DEMOTICS*2)\r
+               ;\r
+               lasttimecount = oldtimecount + DEMOTICS;\r
+               TimeCount = lasttimecount + DEMOTICS;\r
+               realtics = tics = DEMOTICS;\r
+       }\r
+       else\r
+#endif\r
+       {\r
+//\r
+// non demo, so report actual time\r
+//\r
+               newtime = TimeCount;\r
+               realtics = tics = newtime-lasttimecount;\r
+               lasttimecount = newtime;\r
+\r
+#ifdef FILEPROFILE\r
+                       strcpy (scratch,"\tTics:");\r
+                       itoa (tics,str,10);\r
+                       strcat (scratch,str);\r
+                       strcat (scratch,"\n");\r
+                       write (profilehandle,scratch,strlen(scratch));\r
+#endif\r
+\r
+               if (tics>MAXTICS)\r
+               {\r
+                       TimeCount -= (tics-MAXTICS);\r
+                       tics = MAXTICS;\r
+               }\r
+\r
+               if (realtics>MAXREALTICS)\r
+                       realtics = MAXREALTICS;\r
+       }\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+========================\r
+=\r
+= DrawHand\r
+=\r
+========================\r
+*/\r
+\r
+void    DrawHand (void)\r
+{\r
+       #define HAND_X_POS      ((VIEWWIDTH/16)-(10/2))         // "10" = hand width in bytes\r
+\r
+       #define picnum HAND1PICM\r
+\r
+       memptr source;\r
+       unsigned dest,width,height;\r
+\r
+//      if (gamestate.shotpower || boltsleft)\r
+//              picnum += (((unsigned)TimeCount>>3)&1);\r
+\r
+       source = grsegs[picnum];\r
+       dest = ylookup[VIEWHEIGHT-handheight]+HAND_X_POS+bufferofs;                     // 12\r
+       width = picmtable[picnum-STARTPICM].width;\r
+       height = picmtable[picnum-STARTPICM].height;\r
+\r
+       VW_MaskBlock(source,0,dest,width,handheight,width*height);\r
+       EGAMAPMASK(15);\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+========================\r
+=\r
+= ThreeDRefresh\r
+=\r
+========================\r
+*/\r
+\r
+void    ThreeDRefresh (void)\r
+{\r
+       int tracedir;\r
+\r
+restart:\r
+       aborttrace = false;\r
+\r
+//\r
+// clear out the traced array\r
+//\r
+asm     mov     ax,ds\r
+asm     mov     es,ax\r
+asm     mov     di,OFFSET spotvis\r
+asm     xor     ax,ax\r
+asm     mov     cx,[mapwidth]           // mapheight*32 words\r
+asm     shl     cx,1\r
+asm     shl     cx,1\r
+asm     shl     cx,1\r
+asm     shl     cx,1\r
+asm     shl     cx,1\r
+asm     rep stosw\r
+\r
+\r
+//\r
+// set up variables for this view\r
+//\r
+\r
+       viewangle = player->angle;\r
+       fineviewangle = viewangle*(FINEANGLES/ANGLES);\r
+       viewsin = sintable[viewangle];\r
+       viewcos = costable[viewangle];\r
+       viewx = player->x - FixedByFrac(FOCALLENGTH,viewcos);\r
+       viewy = player->y + FixedByFrac(FOCALLENGTH,viewsin);\r
+       viewx &= 0xfffffc00;            // stop on a pixel boundary\r
+       viewy &= 0xfffffc00;\r
+       viewx += 0x180;\r
+       viewy += 0x180;\r
+       viewxpix = viewx>>10;\r
+       viewypix = viewy>>10;\r
+\r
+       focal.x = viewx>>TILESHIFT;\r
+       focal.y = viewy>>TILESHIFT;\r
+\r
+//\r
+// find the rightmost visable tile in view\r
+//\r
+       tracedir = viewangle + lastangle;\r
+       if (tracedir<0)\r
+         tracedir+=ANGLES;\r
+       else if (tracedir>=ANGLES)\r
+         tracedir-=ANGLES;\r
+       TraceRay( tracedir );\r
+       right.x = tile.x;\r
+       right.y = tile.y;\r
+\r
+//\r
+// find the leftmost visable tile in view\r
+//\r
+       tracedir = viewangle + firstangle;\r
+       if (tracedir<0)\r
+         tracedir+=ANGLES;\r
+       else if (tracedir>=ANGLES)\r
+         tracedir-=ANGLES;\r
+       TraceRay( tracedir );\r
+\r
+//\r
+// follow the walls from there to the right\r
+//\r
+       rightwall = &walls[1];\r
+       FollowWalls ();\r
+\r
+       if (aborttrace)\r
+               goto restart;\r
+\r
+//\r
+// actually draw stuff\r
+//\r
+       if (++screenpage == 3)\r
+               screenpage = 0;\r
+\r
+       bufferofs = screenloc[screenpage];\r
+\r
+       EGAWRITEMODE(2);\r
+       EGAMAPMASK(15);\r
+\r
+//\r
+// draw the wall list saved be FollowWalls ()\r
+//\r
+//      animframe = (TimeCount&8)>>3;\r
+\r
+//\r
+// draw all the scaled images\r
+//\r
+       asm     mov     dx,GC_INDEX\r
+\r
+       asm     mov     ax,GC_COLORDONTCARE\r
+       asm     out     dx,ax                                           // don't look at any of the planes\r
+\r
+       asm     mov     ax,GC_MODE + 256*(10)           // read mode 1, write mode 2\r
+       asm     out     dx,ax\r
+\r
+       asm     mov     al,GC_BITMASK\r
+       asm     out     dx,al\r
+\r
+       AnimateWallList();\r
+       DrawWallList();\r
+       DrawScaleds();\r
+\r
+       EGAWRITEMODE(0);\r
+       EGABITMASK(0xff);\r
+\r
+//\r
+// draw hand\r
+//\r
+       if (handheight)\r
+               DrawHand ();\r
+\r
+//\r
+// show screen and time last cycle\r
+//\r
+       if (fizzlein)\r
+       {\r
+               fizzlein = false;\r
+               FizzleFade(bufferofs,displayofs,VIEWWIDTH,VIEWHEIGHT,true);\r
+               lasttimecount = TimeCount;\r
+               if (MousePresent) Mouse(MDelta);        // Clear accumulated mouse movement\r
+       }\r
+\r
+asm     cli\r
+asm     mov     cx,[bufferofs]\r
+asm     mov     dx,3d4h         // CRTC address register\r
+asm     mov     al,0ch          // start address high register\r
+asm     out     dx,al\r
+asm     inc     dx\r
+asm     mov     al,ch\r
+asm     out     dx,al           // set the high byte\r
+asm     dec     dx\r
+asm     mov     al,0dh          // start address low register\r
+asm     out     dx,al\r
+asm     inc     dx\r
+asm     mov     al,cl\r
+asm     out     dx,al           // set the low byte\r
+asm     sti\r
+\r
+       displayofs = bufferofs;\r
+\r
+       CalcTics ();\r
+\r
+}\r
+\r
diff --git a/16/cawat/C5_GAME.C b/16/cawat/C5_GAME.C
new file mode 100644 (file)
index 0000000..14693a7
--- /dev/null
@@ -0,0 +1,1657 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// C3_GAME.C\r
+\r
+#include <stdlib.h>\r
+\r
+#include "DEF.H"\r
+#include "gelib.h"\r
+#pragma hdrstop\r
+\r
+#ifdef PROFILE\r
+#include "TIME.H"\r
+#endif\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define NUMLUMPS        61\r
+\r
+#define SUCCUBUSLUMP   0\r
+#define FATDEMONLUMP   1\r
+#define BOLTLUMP       2\r
+#define NUKELUMP       3\r
+#define POTIONLUMP     4\r
+#define RKEYLUMP        5\r
+#define YKEYLUMP        6\r
+#define GKEYLUMP        7\r
+#define BKEYLUMP        8\r
+//#define SCROLLLUMP   9\r
+#define CHESTLUMP       10\r
+#define PLAYERLUMP      11\r
+#define WALL1LUMP       12\r
+#define WALL2LUMP       13\r
+#define BDOORLUMP       14\r
+#define GODESSLUMP     15\r
+#define MAGELUMP       16\r
+#define BATLUMP                17\r
+#define GRELLUMP       18\r
+#define TOMBSTONESLUMP 19\r
+#define ZOMBIELUMP     20\r
+#define ANTLUMP                21\r
+#define SKELETONLUMP   22\r
+#define RGEMLUMP       23\r
+#define GGEMLUMP       24\r
+#define BGEMLUMP       25\r
+#define YGEMLUMP       26\r
+#define PGEMLUMP       27\r
+//#define RKEY2LUMP    28\r
+#define DRAGONLUMP     29\r
+#define OBJ_WARPLUMP   30\r
+#define EYELUMP                31\r
+#define REDDEMONLUMP   32\r
+//#define PITLUMP      33\r
+#define FTIMELUMP      34\r
+#define WATERCHESTLUMP 35\r
+#define TREELUMP        36\r
+#define ARCH1LUMP       37\r
+#define BUNNYLUMP       38\r
+#define ANTHILLLUMP     39\r
+#define COLUMNLUMP      40\r
+#define SULPHURGASLUMP  41\r
+#define FIREPOTLUMP     42\r
+//#define WHIRLPOOLLUMP        43\r
+#define FOUNTAINLUMP    44\r
+#define FORCEFIELDLUMP  45\r
+#define ARCH2LUMP       46\r
+#define ARCH3LUMP       47\r
+#define ARCH4LUMP       48\r
+#define ARCH5LUMP       49\r
+#define ARCH6LUMP       50\r
+#define SKELHANGLUMP    51\r
+//#define SKELPILELUMP 52\r
+#define ARCH7LUMP       53\r
+#define ARCH8LUMP       54\r
+#define ARCH9LUMP       55\r
+#define ARCH10LUMP      56\r
+#define ARCH11LUMP      57\r
+#define ARCH12LUMP     58\r
+#define ARCH13LUMP     59\r
+\r
+int     lumpstart[NUMLUMPS] = {\r
+SUCCUBUS_LUMP_START,\r
+FATDEMON_LUMP_START,\r
+BOLT_LUMP_START,\r
+NUKE_LUMP_START,\r
+POTION_LUMP_START,\r
+RKEY_LUMP_START,\r
+YKEY_LUMP_START,\r
+GKEY_LUMP_START,\r
+BKEY_LUMP_START,\r
+0,\r
+//SCROLL_LUMP_START,\r
+CHEST_LUMP_START,\r
+PLAYER_LUMP_START,\r
+//WALL1_LUMP_START,\r
+//WALL2_LUMP_START,\r
+//BDOOR_LUMP_START,\r
+0,0,0,\r
+GODESS_LUMP_START,\r
+MAGE_LUMP_START,\r
+BAT_LUMP_START,\r
+GREL_LUMP_START,\r
+TOMBSTONES_LUMP_START,\r
+ZOMBIE_LUMP_START,\r
+ANT_LUMP_START,\r
+SKELDUDE_LUMP_START,\r
+RGEM_LUMP_START,\r
+GGEM_LUMP_START,\r
+BGEM_LUMP_START,\r
+YGEM_LUMP_START,\r
+PGEM_LUMP_START,\r
+0,                                     //RKEY2_LUMP_START,\r
+DRAGON_LUMP_START,\r
+OBJ_WARP_LUMP_START,\r
+EYE_LUMP_START,\r
+REDDEMON_LUMP_START,\r
+0,                                     //PIT_LUMP_START,\r
+TIME_LUMP_START,\r
+O_WATER_CHEST_LUMP_START,\r
+TREE_LUMP_START,\r
+ARCH1_LUMP_START,\r
+BUNNY_LUMP_START,\r
+ANTHILL_LUMP_START,\r
+COLUMN_LUMP_START,\r
+SULPHURGAS_LUMP_START,\r
+FIREPOT_LUMP_START,\r
+0,                                     //WHIRLPOOL_LUMP_START,\r
+FOUNTAIN_LUMP_START,\r
+FORCEFIELD_LUMP_START,\r
+ARCH2_LUMP_START,\r
+ARCH3_LUMP_START,\r
+ARCH4_LUMP_START,\r
+ARCH5_LUMP_START,\r
+ARCH6_LUMP_START,\r
+SKELHANG_LUMP_START,\r
+0,                                     //SKELPILE_LUMP_START,\r
+ARCH7_LUMP_START,\r
+ARCH8_LUMP_START,\r
+ARCH9_LUMP_START,\r
+ARCH10_LUMP_START,\r
+ARCH11_LUMP_START,\r
+ARCH12_LUMP_START,\r
+ARCH13_LUMP_START,\r
+};\r
+\r
+\r
+int     lumpend[NUMLUMPS] = {\r
+SUCCUBUS_LUMP_END,\r
+FATDEMON_LUMP_END,\r
+BOLT_LUMP_END,\r
+NUKE_LUMP_END,\r
+POTION_LUMP_END,\r
+RKEY_LUMP_END,\r
+YKEY_LUMP_END,\r
+GKEY_LUMP_END,\r
+BKEY_LUMP_END,\r
+0,\r
+//SCROLL_LUMP_END,\r
+CHEST_LUMP_END,\r
+PLAYER_LUMP_END,\r
+0,0,0,\r
+GODESS_LUMP_END,\r
+MAGE_LUMP_END,\r
+BAT_LUMP_END,\r
+GREL_LUMP_END,\r
+TOMBSTONES_LUMP_END,\r
+ZOMBIE_LUMP_END,\r
+ANT_LUMP_END,\r
+SKELDUDE_LUMP_END,\r
+RGEM_LUMP_END,\r
+GGEM_LUMP_END,\r
+BGEM_LUMP_END,\r
+YGEM_LUMP_END,\r
+PGEM_LUMP_END,\r
+0,                                     //RKEY2_LUMP_END,\r
+DRAGON_LUMP_END,\r
+OBJ_WARP_LUMP_END,\r
+EYE_LUMP_END,\r
+REDDEMON_LUMP_END,\r
+0,                                     //PIT_LUMP_END,\r
+TIME_LUMP_END,\r
+O_WATER_CHEST_LUMP_END,\r
+TREE_LUMP_END,\r
+ARCH1_LUMP_END,\r
+BUNNY_LUMP_END,\r
+ANTHILL_LUMP_END,\r
+COLUMN_LUMP_END,\r
+SULPHURGAS_LUMP_END,\r
+FIREPOT_LUMP_END,\r
+0,                                     //WHIRLPOOL_LUMP_END,\r
+FOUNTAIN_LUMP_END,\r
+FORCEFIELD_LUMP_END,\r
+ARCH2_LUMP_END,\r
+ARCH3_LUMP_END,\r
+ARCH4_LUMP_END,\r
+ARCH5_LUMP_END,\r
+ARCH6_LUMP_END,\r
+SKELHANG_LUMP_END,\r
+0,                                     //SKELPILE_LUMP_END,\r
+ARCH7_LUMP_END,\r
+ARCH8_LUMP_END,\r
+ARCH9_LUMP_END,\r
+ARCH10_LUMP_END,\r
+ARCH11_LUMP_END,\r
+ARCH12_LUMP_END,\r
+ARCH13_LUMP_END,\r
+};\r
+\r
+\r
+//extern unsigned scolor,gcolor;\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+unsigned        latchpics[NUMLATCHPICS];\r
+unsigned        tileoffsets[NUMTILE16];\r
+unsigned        textstarts[27];\r
+\r
+boolean splitscreen=false;\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+boolean lumpneeded[NUMLUMPS];\r
+\r
+\r
+//===========================================================================\r
+\r
+//==========================================================================\r
+//\r
+//\r
+//                                                      LOCAL PROTOTYPES\r
+//\r
+//\r
+//==========================================================================\r
+\r
+void CashPoints(void);\r
+\r
+\r
+\r
+/*\r
+==========================\r
+=\r
+= ScanInfoPlane\r
+=\r
+= Spawn all actors and mark down special places\r
+=\r
+==========================\r
+*/\r
+\r
+void ScanInfoPlane (void)\r
+{\r
+       unsigned char hibyte;\r
+       unsigned        x,y,i,j;\r
+       unsigned int tile;\r
+       unsigned        far     *start;\r
+\r
+       InitObjList();                  // start spawning things with a clean slate\r
+\r
+       scolor = gcolor = 0;\r
+       skycolor = &scolor;\r
+       groundcolor = &gcolor;\r
+\r
+\r
+       memset (lumpneeded,0,sizeof(lumpneeded));\r
+\r
+       start = mapsegs[2];\r
+       for (y=0;y<mapheight;y++)\r
+               for (x=0;x<mapwidth;x++)\r
+               {\r
+                       tile = *start++;\r
+                       hibyte = tile >> 8;\r
+                       tile &= 0xff;\r
+\r
+                       switch (hibyte)\r
+                       {\r
+                               char hi;\r
+\r
+                               case 0xFB:\r
+                                       wall_anim_time = tile;\r
+                                       tile = 0;\r
+                                       break;\r
+\r
+                               case 0xfa:                                                              // sky/ground color\r
+                               case 0xf9:                                                              // sky/ground 'strip'\r
+                                       x++;\r
+                                       tile = *start++;\r
+                                       hi = tile >> 8;\r
+                                       tile &= 0xff;\r
+                                       switch (hibyte)\r
+                                       {\r
+                                               case 0xfa:                      // sky / ground color\r
+                                                       scolor = ((hi)|(hi<<8));\r
+                                                       gcolor = ((tile)|(tile<<8));\r
+                                                       skycolor = &scolor;\r
+                                                       groundcolor = &gcolor;\r
+                                               break;\r
+\r
+                                               case 0xf9:                      // sky / ground 'strip'\r
+                                               break;\r
+                                       }\r
+                               break;\r
+                       }\r
+\r
+                       if ((!tile) || (hibyte))\r
+                               continue;\r
+\r
+                       switch (tile)\r
+                       {\r
+                       case 1:\r
+                       case 2:\r
+                       case 3:\r
+                       case 4:\r
+                               lumpneeded[PLAYERLUMP] = true;\r
+                               SpawnPlayer(x,y,NORTH+tile-1);\r
+                               break;\r
+\r
+                       case 5:\r
+                       case 6:\r
+                       case 7:\r
+                       case 8:\r
+                       case 9:\r
+                       case 10:\r
+                       case 11:\r
+                               lumpneeded[tile-5+BOLTLUMP] = true;\r
+                               SpawnBonus(x,y,tile-5);\r
+                               break;\r
+\r
+#if 0\r
+                       case 12:\r
+                       case 13:\r
+                       case 14:\r
+                       case 15:\r
+                       case 16:\r
+                       case 17:\r
+                       case 18:\r
+                       case 19:\r
+                               lumpneeded[SCROLLLUMP] = true;\r
+                               SpawnBonus(x,y,B_SCROLL1+tile-12);\r
+                               break;\r
+#endif\r
+\r
+                       case 20:\r
+                               lumpneeded[REDDEMONLUMP] = true;\r
+                               SpawnRedDemon (x,y);\r
+                               break;\r
+\r
+#if 0\r
+                       case 20:        // goal\r
+                               lumpneeded[GOALLUMP] = true;\r
+                               SpawnBonus(x,y,B_GOAL);\r
+                               break;\r
+#endif\r
+\r
+                       case 21:\r
+                               lumpneeded[GODESSLUMP] = true;\r
+                               SpawnGodess (x,y);\r
+                               break;\r
+\r
+                       case 22:\r
+                               lumpneeded[FATDEMONLUMP] = true;\r
+                               SpawnFatDemon (x,y);\r
+                               break;\r
+\r
+                       case 23:\r
+                               lumpneeded[SUCCUBUSLUMP] = true;\r
+                               SpawnSuccubus (x,y);\r
+                               break;\r
+\r
+                       case 24:\r
+                               lumpneeded[DRAGONLUMP] = true;\r
+                               SpawnDragon(x,y);\r
+                               break;\r
+\r
+                       case 25:\r
+                               lumpneeded[BATLUMP] = true;\r
+                               SpawnBat (x,y);\r
+                               break;\r
+\r
+                       case 26:\r
+                               lumpneeded[EYELUMP] = true;\r
+                               SpawnEye(x,y);\r
+                               break;\r
+\r
+                       case 27:\r
+                               lumpneeded[MAGELUMP] = true;\r
+                               SpawnMage (x,y);\r
+                               break;\r
+\r
+                       case 28:\r
+                               lumpneeded[RKEYLUMP] = lumpneeded[GRELLUMP] = true;\r
+                               SpawnGrelminar (x,y);\r
+                               break;\r
+\r
+                       case 30:\r
+                               lumpneeded[ANTLUMP] = true;\r
+                               SpawnAnt(x,y);\r
+                               break;\r
+\r
+                       case 31:\r
+                       case 32:\r
+                       case 33:\r
+                       case 34:\r
+                       case 35:\r
+                               lumpneeded[OBJ_WARPLUMP] = true;\r
+                               SpawnWarp (x,y,tile-30);\r
+                               break;\r
+\r
+                       case 36:\r
+                               lumpneeded[ZOMBIELUMP] = true;\r
+                               SpawnZombie(x,y);\r
+                               break;\r
+\r
+                       case 37:\r
+                               lumpneeded[SKELETONLUMP] = true;\r
+                               SpawnSkeleton(x,y);\r
+                               break;\r
+\r
+                       case 38:\r
+                               lumpneeded[SKELETONLUMP] = true;\r
+                               SpawnWallSkeleton(x,y);\r
+                               break;\r
+\r
+                       case 39:\r
+                               lumpneeded[FTIMELUMP] = true;\r
+                               SpawnFTime(x,y);\r
+                               break;\r
+\r
+                       case 40:\r
+                       case 41:\r
+                       case 42:\r
+                       case 43:\r
+                       case 44:\r
+                               lumpneeded[tile-40+RGEMLUMP] = true;\r
+                               SpawnBonus(x,y,tile-40+B_RGEM);\r
+                       break;\r
+\r
+                       case 45:\r
+                       case 46:\r
+                       case 47:\r
+                               lumpneeded[TOMBSTONESLUMP] = true;\r
+                               SpawnTombstone(x,y,tile-45);\r
+                               break;\r
+\r
+#if 0\r
+                       case 48:\r
+                               lumpneeded[PITLUMP]     = true;\r
+                               SpawnWarp(x,y,0);\r
+                               break;\r
+#endif\r
+                       case 49:        // chest\r
+                               if (gcolor == 0x0101)\r
+                                       lumpneeded[WATERCHESTLUMP] = true;\r
+                               else\r
+                                       lumpneeded[CHESTLUMP] = true;\r
+                               SpawnBonus(x,y,B_CHEST);\r
+                       break;\r
+\r
+                       case 50:\r
+                               lumpneeded[TREELUMP] = true;\r
+                               SpawnTree(x,y);\r
+                               break;\r
+\r
+                       case 51:\r
+                               lumpneeded[BUNNYLUMP] = true;\r
+                               SpawnBunny(x,y);\r
+                               break;\r
+\r
+                       case 52:\r
+                               lumpneeded[ARCH1LUMP] = true;\r
+                               SpawnArch(x,y,1);\r
+                               break;\r
+\r
+                       case 53:\r
+                               lumpneeded[ANTHILLLUMP] = true;\r
+                               SpawnWarp(x,y,0);\r
+                               break;\r
+\r
+                       case 54:\r
+                               lumpneeded[COLUMNLUMP] = true;\r
+                               SpawnMiscObjects(x,y,1);                //1=column,2=sulphur hole,3=fire pot,4=fountain\r
+                               break;\r
+\r
+                       case 55:\r
+                               lumpneeded[SULPHURGASLUMP] = true;\r
+                               SpawnMiscObjects(x,y,2);\r
+                               break;\r
+\r
+                       case 56:\r
+                               lumpneeded[FIREPOTLUMP] = true;\r
+                               SpawnMiscObjects(x,y,3);\r
+                               break;\r
+\r
+                       case 57:\r
+                               lumpneeded[ARCH13LUMP] = true;\r
+                               SpawnArch(x,y,13);\r
+                               break;\r
+\r
+                       case 58:\r
+                               lumpneeded[FOUNTAINLUMP] = true;\r
+                               SpawnMiscObjects(x,y,4);\r
+                               break;\r
+\r
+                       case 59:\r
+                               lumpneeded[FORCEFIELDLUMP] = true;\r
+                               SpawnForceField(x,y);\r
+                               break;\r
+\r
+                       case 60:\r
+                               lumpneeded[ARCH2LUMP] = true;\r
+                               SpawnArch(x,y,2);\r
+                               break;\r
+\r
+                       case 61:\r
+                               lumpneeded[ARCH3LUMP] = true;\r
+                               SpawnArch(x,y,3);\r
+                               break;\r
+\r
+                       case 62:\r
+                               lumpneeded[ARCH4LUMP] = true;\r
+                               SpawnArch(x,y,4);\r
+                               break;\r
+\r
+                       case 63:\r
+                               lumpneeded[ARCH5LUMP] = true;\r
+                               SpawnArch(x,y,5);\r
+                               break;\r
+\r
+                       case 64:\r
+                               lumpneeded[ARCH6LUMP] = true;\r
+                               SpawnArch(x,y,6);\r
+                               break;\r
+\r
+                       case 65:\r
+                               lumpneeded[SKELHANGLUMP] = true;\r
+                               lumpneeded[SKELETONLUMP] = true;\r
+                               SpawnSkeletonHanging(x,y);\r
+                               break;\r
+\r
+                       case 66:\r
+                               lumpneeded[ARCH12LUMP] = true;\r
+                               SpawnArch(x,y,12);\r
+                               break;\r
+\r
+                       case 67:\r
+                               lumpneeded[ARCH7LUMP] = true;\r
+                               SpawnArch(x,y,7);\r
+                               break;\r
+\r
+                       case 68:\r
+                               lumpneeded[ARCH8LUMP] = true;\r
+                               SpawnArch(x,y,8);\r
+                               break;\r
+\r
+                       case 69:\r
+                               lumpneeded[ARCH9LUMP] = true;\r
+                               SpawnArch(x,y,9);\r
+                               break;\r
+\r
+                       case 70:\r
+                               lumpneeded[ARCH10LUMP] = true;\r
+                               SpawnArch(x,y,10);\r
+                               break;\r
+\r
+                       case 71:\r
+                               lumpneeded[ARCH11LUMP] = true;\r
+                               SpawnArch(x,y,11);\r
+                               break;\r
+                       }\r
+               }\r
+\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+==================\r
+=\r
+= ScanText\r
+=\r
+==================\r
+*/\r
+\r
+void ScanText (void)\r
+{\r
+       int     i;\r
+       char far *text;\r
+\r
+       text = (char _seg *)grsegs[LEVEL1TEXT+mapon];\r
+\r
+       textstarts[0] = 0;\r
+\r
+       for (i=1;i<=26;i++)\r
+       {\r
+               while (*text != '\n')\r
+               {\r
+                       if (*text == '\r')\r
+                               *text = 0;\r
+                       text++;\r
+               }\r
+               text++;\r
+               textstarts[i] = FP_OFF(text);\r
+       }\r
+\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+==================\r
+=\r
+= DrawEnterScreen\r
+=\r
+==================\r
+*/\r
+#if 0\r
+static  char    *levelnames[] =\r
+                               {\r
+                                       "Programmers Test Map",\r
+                                       "The Garden of Tears",\r
+                                       "The Den of Zombies",\r
+                                       "The Mausoleum Grounds",\r
+                                       "The Main Floor of the Mausoleum",\r
+                                       "Mike's Blastable Passage",\r
+                                       "The Crypt of Nemesis the Undead",\r
+                                       "The Subterranean Vault",\r
+                                       "The Ancient Aqueduct",\r
+                                       "The Orc Mines",\r
+                                       "The Lair of the Troll",\r
+                                       "The Demon's Inferno",\r
+                                       "The Battleground of the Titans",\r
+                                       "The Coven of Mages",\r
+                                       "The Inner Sanctum",\r
+                                       "The Haunt of Nemesis",\r
+                                       "The Passage to the Surface",\r
+                                       "Big Jim's Domain",\r
+                                       "Nolan",\r
+                                       "19",\r
+                                       "20",\r
+                                       "21",\r
+                                       "22",\r
+                                       "23",\r
+                                       "24",\r
+                                       "25",\r
+                                       "26",\r
+                                       "27",\r
+                                       "28",\r
+                                       "29",\r
+                                       "30",\r
+                                       "31",\r
+                                       "32",\r
+                                       "33",\r
+                                       "34",\r
+                                       "35",\r
+                                       "36",\r
+                                       "37",\r
+                                       "38",\r
+                                       "39",\r
+                               };\r
+#endif\r
+\r
+void DrawEnterScreen (void)\r
+{\r
+       int width;\r
+\r
+       bufferofs = displayofs = screenloc[screenpage];\r
+       VW_Bar(0,0,VIEWWIDTH,VIEWHEIGHT,0);\r
+//     width = strlen(levelnames[gamestate.mapon]);\r
+       width = strlen("You enter a new area ...");\r
+       if (width < 20)\r
+               width = 20;\r
+       CenterWindow(width,3);\r
+       US_CPrint("\nYou enter a new area ...\n");\r
+//     US_CPrint(levelnames[gamestate.mapon]);\r
+}\r
+\r
+//==========================================================================\r
+\r
+boolean tileneeded[NUMFLOORS];\r
+\r
+\r
+/*\r
+==================\r
+=\r
+= CacheScaleds\r
+=\r
+==================\r
+*/\r
+\r
+void CacheScaleds (void)\r
+{\r
+       int     i,j;\r
+       unsigned        source,dest;\r
+\r
+       FreeUpMemory ();\r
+       CA_CacheGrChunk(LEVEL1TEXT+mapon);\r
+       ScanText ();\r
+\r
+//\r
+// make sure we are displaying screenpage 0\r
+//\r
+       if (screenpage)\r
+       {\r
+               source = screenloc[screenpage];\r
+               dest = screenloc[0];\r
+               VW_ScreenToScreen (source,dest,40,VIEWHEIGHT);\r
+               screenpage = 0;\r
+               VW_SetScreen (dest,0);\r
+               displayofs = dest;\r
+       }\r
+\r
+//\r
+// cache wall pictures\r
+//\r
+       for (i=1;i<NUMFLOORS;i++)\r
+               if (tileneeded[i])\r
+               {\r
+                       SetupScaleWall (walllight1[i]);\r
+                       SetupScaleWall (walldark1[i]);\r
+               }\r
+\r
+//\r
+// cache the actor pictures\r
+//\r
+       for (i=0;i<NUMLUMPS;i++)\r
+               if (lumpneeded[i])\r
+                       for (j=lumpstart[i];j<=lumpend[i];j++)\r
+                               SetupScalePic(j);\r
+\r
+       source = screenloc[0];\r
+       for (i=1;i<=2;i++)\r
+       {\r
+               dest = screenloc[i];\r
+               VW_ScreenToScreen (source,dest,40,VIEWHEIGHT);\r
+       }\r
+\r
+       screenpage = 1;\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+==================\r
+=\r
+= SetupGameLevel\r
+=\r
+==================\r
+*/\r
+\r
+void SetupGameLevel ()\r
+{\r
+       int     x,y,i,loop;\r
+       unsigned        far *map,tile,far *spotptr,spot;\r
+       unsigned                search_tile;\r
+       boolean         exploding_walls_present = false;\r
+\r
+       memset (tileneeded,0,sizeof(tileneeded));\r
+//\r
+// randomize if not a demo\r
+//\r
+#if 0\r
+       if (DemoMode)\r
+       {\r
+               US_InitRndT(false);\r
+               gamestate.difficulty = gd_Normal;\r
+       }\r
+       else\r
+#endif\r
+               US_InitRndT(true);\r
+\r
+//\r
+// load the level\r
+//\r
+       CA_CacheMap (gamestate.mapon);\r
+\r
+       mapwidth = mapheaderseg[mapon]->width;\r
+       mapheight = mapheaderseg[mapon]->height;\r
+\r
+//\r
+// make a lookup table for the maps left edge\r
+//\r
+       spot = 0;\r
+       for (y=0;y<mapheight;y++)\r
+       {\r
+         farmapylookup[y] = spot;\r
+         spot += mapwidth;\r
+       }\r
+\r
+\r
+//\r
+// copy the wall data to a data segment array\r
+//\r
+       memset (tilemap,0,sizeof(tilemap));\r
+       memset (actorat,0,sizeof(actorat));\r
+       map = mapsegs[0];\r
+       spotptr = mapsegs[2];\r
+       for (y=0;y<mapheight;y++)\r
+               for (x=0;x<mapwidth;x++)\r
+               {\r
+                       tile = *map++;\r
+\r
+                       if (((*spotptr)>>8) == EXP_WALL_CODE)\r
+                       {\r
+                               exploding_walls_present = true;\r
+                       }\r
+\r
+                       if (tile<NUMFLOORS)\r
+                       {\r
+#if 0\r
+                               if (tile == WALL_SKELETON_CODE)\r
+                               {\r
+                                       tileneeded[tile+1] = tileneeded[tile+2] = true;\r
+                                       tilemap[x][y] = tile;\r
+                               }\r
+#endif\r
+                               if ((tile == 66) || (tile == 67) || (tile == 68) || (tile == 69))\r
+                               {\r
+                                       if ((tile == 66) || (tile == 67))\r
+                                               tileneeded[tile+2] = true;\r
+                                       tileneeded[21] = tileneeded[tile] = true;\r
+                                       tilemap[x][y] = tile;\r
+                               }\r
+                               else\r
+                               if (tile != INVISIBLEWALL)\r
+                               {\r
+                                       tileneeded[tile] = true;\r
+                                       tilemap[x][y] = tile;\r
+                                       if (ANIM_FLAGS(tile))\r
+                                       {\r
+                                               search_tile = tile+(char signed)ANIM_FLAGS(tile);\r
+\r
+                                               if (!tileneeded[search_tile])\r
+                                                       while (search_tile != tile)\r
+                                                       {\r
+                                                               tileneeded[search_tile] = true;\r
+                                                               if (ANIM_FLAGS(search_tile))\r
+                                                                       search_tile += (char signed)ANIM_FLAGS(search_tile);\r
+                                                               else\r
+                                                                       TrashProg("Unending Tile Animation!");\r
+                                                       }\r
+                                       }\r
+\r
+                               }\r
+                               if (tile>0)\r
+                                       (unsigned)actorat[x][y] = tile;\r
+                       }\r
+                       spotptr++;\r
+               }\r
+\r
+\r
+       //\r
+       // Mark any gfx chunks needed\r
+       //\r
+\r
+//      CA_MarkGrChunk(NORTHICONSPR);\r
+//      CA_CacheMarks(NULL);\r
+\r
+\r
+//\r
+// decide which graphics are needed and spawn actors\r
+//\r
+       zombie_base_delay = 0;  // (1*60) + random(1*60);\r
+       ScanInfoPlane ();\r
+       _fmemset(wall_anim_pos,0,sizeof(wall_anim_pos));\r
+\r
+\r
+//\r
+// mark which exploding walls are needed ---- the check for floor color\r
+// is preformed in ScanInfoPlane.\r
+//\r
+\r
+       if (exploding_walls_present)\r
+       {\r
+                               extern unsigned gnd_colors[];\r
+\r
+                               if (gcolor == 0x0101)\r
+                                       tileneeded[WATEREXP] = tileneeded[WATEREXP+1] = tileneeded[WATEREXP+2] = true;\r
+                               else\r
+                                       tileneeded[WALLEXP] = tileneeded[WALLEXP+1] = tileneeded[WALLEXP+2] = true;\r
+\r
+       }\r
+\r
+\r
+//\r
+// have the caching manager load and purge stuff to make sure all marks\r
+// are in memory\r
+//\r
+       CA_LoadAllSounds ();\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= LatchDrawPic\r
+=\r
+=====================\r
+*/\r
+\r
+void LatchDrawPic (unsigned x, unsigned y, unsigned picnum)\r
+{\r
+       unsigned height, source, dest;\r
+       unsigned wide;\r
+\r
+       wide = pictable[picnum-STARTPICS].width;\r
+       height = pictable[picnum-STARTPICS].height;\r
+       dest = bufferofs + ylookup[y]+x;\r
+       source = latchpics[picnum-FIRSTLATCHPIC];\r
+\r
+       EGAWRITEMODE(1);\r
+       EGAMAPMASK(15);\r
+\r
+asm     mov     bx,[linewidth]\r
+asm     sub     bx,[wide]\r
+\r
+asm     mov     ax,[screenseg]\r
+asm     mov     es,ax\r
+asm     mov     ds,ax\r
+\r
+asm     mov     si,[source]\r
+asm     mov     di,[dest]\r
+asm     mov     dx,[height]                             // scan lines to draw\r
+asm     mov     ax,[wide]\r
+\r
+lineloop:\r
+asm     mov     cx,ax\r
+asm     rep     movsb\r
+asm     add     di,bx\r
+\r
+asm     dec     dx\r
+asm     jnz     lineloop\r
+\r
+asm     mov     ax,ss\r
+asm     mov     ds,ax                                   // restore turbo's data segment\r
+\r
+       EGAWRITEMODE(0);\r
+}\r
+\r
+#if USE_STRIPS\r
+\r
+//--------------------------------------------------------------------------\r
+// LatchDrawPicStrip() - srcoff is distance into source file (in PIXELS!)\r
+//--------------------------------------------------------------------------\r
+void LatchDrawPicStrip (unsigned x, unsigned y, unsigned picnum, unsigned srcoff)\r
+{\r
+       unsigned wide, height, source, dest, shift, srcmod;\r
+\r
+       shift = (srcoff & 7) >> 1;\r
+       srcoff >>= 3;\r
+       wide = pictable[picnum-STARTPICS].width;\r
+       srcmod = wide - linewidth + (shift != 3);\r
+       if (wide > linewidth)\r
+               wide = linewidth;\r
+       height = pictable[picnum-STARTPICS].height;\r
+       dest = bufferofs + ylookup[y]+x;\r
+\r
+       picnum = ((picnum - (FIRSTSTRIPPIC+1)) >> 2) + (shift);\r
+       source = latchpics[(FIRSTSTRIPPIC-FIRSTLATCHPIC+1)+picnum];\r
+\r
+       EGAWRITEMODE(1);\r
+       EGAMAPMASK(15);\r
+\r
+asm     mov     bx,[linewidth]\r
+asm     sub     bx,[wide]\r
+\r
+asm     mov     ax,[screenseg]\r
+asm     mov     es,ax\r
+asm     mov     ds,ax\r
+\r
+asm     mov     si,[source]\r
+asm      add            si,[srcoff]\r
+asm     mov     di,[dest]\r
+asm     mov     dx,[height]                             // scan lines to draw\r
+asm     mov     ax,[wide]\r
+\r
+lineloop:\r
+asm     mov     cx,ax\r
+asm     rep     movsb\r
+asm     add     di,bx\r
+asm      add     si,[srcmod]\r
+\r
+asm     dec     dx\r
+asm     jnz     lineloop\r
+\r
+asm     mov     ax,ss\r
+asm     mov     ds,ax                                   // restore turbo's data segment\r
+\r
+       EGAWRITEMODE(0);\r
+}\r
+\r
+#endif\r
+\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= Victory\r
+=\r
+=====================\r
+*/\r
+\r
+void Victory (boolean playsounds)\r
+{\r
+       struct Shape shape;\r
+\r
+       if (playsounds)\r
+       {\r
+               SD_PlaySound (GETBOLTSND);\r
+               SD_WaitSoundDone ();\r
+               SD_PlaySound (GETNUKESND);\r
+               SD_WaitSoundDone ();\r
+               SD_PlaySound (GETPOTIONSND);\r
+               SD_WaitSoundDone ();\r
+               SD_PlaySound (GETKEYSND);\r
+               SD_WaitSoundDone ();\r
+               SD_PlaySound (GETSCROLLSND);\r
+               SD_WaitSoundDone ();\r
+               SD_PlaySound (GETPOINTSSND);\r
+       }\r
+\r
+       FreeUpMemory();\r
+\r
+       if (!screenfaded)\r
+               VW_FadeOut();\r
+\r
+       NormalScreen ();\r
+\r
+       screenpage = bufferofs = 0;\r
+\r
+       CA_CacheGrChunk (FINALEPIC);\r
+       UNMARKGRCHUNK(FINALEPIC);\r
+       VW_DrawPic(0, 0, FINALEPIC);\r
+\r
+       VW_FadeIn();\r
+}\r
+\r
+//==========================================================================\r
+\r
+#if 0\r
+/*\r
+===================\r
+=\r
+= Died\r
+=\r
+===================\r
+*/\r
+\r
+void Died (void)\r
+{\r
+       unsigned page1,page2;\r
+//\r
+// fizzle fade screen to grey\r
+//\r
+       FreeUpMemory ();\r
+       SD_PlaySound (GAMEOVERSND);\r
+       bufferofs = screenloc[(screenpage+1)%3];\r
+       DisplayMsg("Though fallen, your Spirit ...",NULL);\r
+//      LatchDrawPic(0,0,DEADPIC);\r
+//      FizzleFade(bufferofs,displayofs,VIEWWIDTH,VIEWHEIGHT,false);\r
+       IN_ClearKeysDown();\r
+       while (!Keyboard[sc_Enter]);\r
+//      IN_Ack();\r
+       VW_SetScreen (bufferofs,0);\r
+       VW_ColorBorder(0);\r
+}\r
+#endif\r
+\r
+//==========================================================================\r
+\r
+/*\r
+===================\r
+=\r
+= NormalScreen\r
+=\r
+===================\r
+*/\r
+\r
+void NormalScreen (void)\r
+{\r
+        VW_SetSplitScreen (200);\r
+        bufferofs = displayofs = SCREEN1START;\r
+        VW_Bar(0,0,320,200,0);\r
+        bufferofs = SCREEN2START;\r
+        VW_Bar(0,0,320,200,0);\r
+        VW_SetScreen (displayofs,0);\r
+        splitscreen = false;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+===================\r
+=\r
+= DrawPlayScreen\r
+=\r
+===================\r
+*/\r
+\r
+void DrawPlayScreen (void)\r
+{\r
+       int     i,j,p,m;\r
+\r
+       screenpage = 0;\r
+\r
+       bufferofs = 0;\r
+       VW_Bar (0,0,320,STATUSLINES,0);\r
+       for (i=0;i<3;i++)\r
+       {\r
+               bufferofs = screenloc[i];\r
+               VW_Bar (0,0,320,VIEWHEIGHT,0);\r
+       }\r
+\r
+       splitscreen = true;\r
+       VW_SetSplitScreen(120);\r
+       VW_SetScreen(screenloc[0],0);\r
+\r
+       CA_CacheGrChunk (STATUSPIC);\r
+\r
+       bufferofs = 0;\r
+       VW_DrawPic (0,0,STATUSPIC);\r
+\r
+       grneeded[STATUSPIC] &= ~ca_levelbit;\r
+       MM_SetPurge(&grsegs[STATUSPIC],3);\r
+\r
+       RedrawStatusWindow ();\r
+       bufferofs = displayofs = screenloc[0];\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+/*\r
+===================\r
+=\r
+= LoadLatchMem\r
+=\r
+===================\r
+*/\r
+\r
+unsigned latchmemavail;\r
+\r
+void LoadLatchMem (void)\r
+{\r
+       static unsigned base_destoff=0;\r
+       static int base_numpics=0;\r
+       int     i,j,p,m,numpics;\r
+       byte    far *src, far *dest;\r
+       unsigned        destoff;\r
+\r
+       EGAWRITEMODE(0);\r
+\r
+//\r
+// draw some pics into latch memory\r
+//\r
+\r
+  if (!base_numpics)\r
+  {\r
+\r
+//\r
+// tile 8s\r
+//\r
+       latchpics[0] = freelatch;\r
+       src = (byte _seg *)grsegs[STARTTILE8];\r
+       dest = MK_FP(0xa000,freelatch);\r
+\r
+       for (i=0;i<NUMTILE8;i++)\r
+       {\r
+               for (p=0;p<4;p++)\r
+               {\r
+                       m = 1<<p;\r
+                       asm     mov     dx,SC_INDEX\r
+                       asm     mov     al,SC_MAPMASK\r
+                       asm     mov     ah,[BYTE PTR m]\r
+                       asm     out     dx,ax\r
+                       for (j=0;j<8;j++)\r
+                               *(dest+j)=*src++;\r
+               }\r
+               dest+=8;\r
+       }\r
+\r
+//\r
+// tile 16s\r
+//\r
+       src = (byte _seg *)grsegs[STARTTILE16];\r
+\r
+       for (i=0;i<NUMTILE16;i++)\r
+       {\r
+               CA_CacheGrChunk (STARTTILE16+i);\r
+               src = (byte _seg *)grsegs[STARTTILE16+i];\r
+               if (src)\r
+               {\r
+                       tileoffsets[i] = FP_OFF(dest);\r
+                       for (p=0;p<4;p++)\r
+                       {\r
+                               m = 1<<p;\r
+                               asm     mov     dx,SC_INDEX\r
+                               asm     mov     al,SC_MAPMASK\r
+                               asm     mov     ah,[BYTE PTR m]\r
+                               asm     out     dx,ax\r
+                               for (j=0;j<32;j++)\r
+                                       *(dest+j)=*src++;\r
+                       }\r
+                       dest+=32;\r
+                       MM_FreePtr (&grsegs[STARTTILE16+i]);\r
+                       UNMARKGRCHUNK(STARTTILE16+i);\r
+               }\r
+               else\r
+                       tileoffsets[i] = 0;\r
+       }\r
+\r
+\r
+//\r
+// pics\r
+//\r
+       numpics=1;\r
+       destoff = FP_OFF(dest);\r
+       for (i=FIRSTLATCHPIC+1;i<FIRSTGROUNDPIC;i++)\r
+       {\r
+               latchpics[numpics++] = destoff;\r
+               CA_CacheGrChunk (i);\r
+               j = pictable[i-STARTPICS].width * pictable[i-STARTPICS].height;\r
+               VW_MemToScreen (grsegs[i],destoff,j,1);\r
+               destoff+=j;\r
+               MM_FreePtr (&grsegs[i]);\r
+               UNMARKGRCHUNK(i);\r
+       }\r
+\r
+       base_numpics = numpics;\r
+       base_destoff = destoff;\r
+\r
+  }\r
+\r
+       numpics = base_numpics;\r
+       destoff = base_destoff;\r
+\r
+#if USE_STRIPS\r
+//\r
+// ground pics\r
+//\r
+       numpics++;\r
+       for (i=FIRSTGROUNDPIC+1;i<FIRSTSTRIPPIC;i++)\r
+       {\r
+               int shape = (*groundcolor & 0xf0) - 16;\r
+\r
+       // Is current shape needed?\r
+       //\r
+               if (shape != (i-(FIRSTGROUNDPIC+1)))\r
+               {\r
+                       numpics++;\r
+                       continue;\r
+               }\r
+\r
+               latchpics[numpics++] = destoff;\r
+               CA_CacheGrChunk (i);\r
+               j = pictable[i-STARTPICS].width * pictable[i-STARTPICS].height;\r
+               VW_MemToScreen (grsegs[i],destoff,j,1);\r
+               destoff+=j;\r
+               MM_FreePtr (&grsegs[i]);\r
+               UNMARKGRCHUNK(i);\r
+       }\r
+\r
+\r
+//\r
+// 'parallax' strips - used in place of a sky color\r
+//\r
+// Under current setup, each strip takes about 7k in latch memory.\r
+// To create 2 pixel scrolling, 4 strips are needed, that's 28k of\r
+// latch memory needed to produce this effect.\r
+//\r
+       numpics++;\r
+       for (i=FIRSTSTRIPPIC+1;i<FIRSTSCALEPIC;i++)\r
+       {\r
+               memptr work;\r
+               unsigned workdest,stripsize,planesize;\r
+               short loop,pic=i-STARTPICS;\r
+               int shape = (*skycolor & 0xf0) - 16;\r
+\r
+       // Is current shape needed?\r
+       //\r
+               if (shape != (i-(FIRSTSTRIPPIC+1)))\r
+               {\r
+                       numpics++;\r
+                       continue;\r
+               }\r
+\r
+       // CAL_ShiftSprite() works with the SRC and DST in the same\r
+       // segment. So we must allocate memory for two strips, and\r
+       // move the base strip into that segment. Then we can use the\r
+       // 2nd half of that memory for each shifted strip.\r
+       //\r
+               CA_CacheGrChunk (i);\r
+               planesize = (pictable[pic].width+1) * pictable[pic].height;\r
+               stripsize = planesize * 4;\r
+//             MM_GetPtr(&work,(stripsize*2)+0000);\r
+               MM_GetPtr(&work,65536);\r
+               movedata((unsigned)grsegs[i],0,(unsigned)work,0,stripsize);\r
+               workdest = 32768; //(stripsize+15) & 0xFFF0;\r
+\r
+       // Free base strip\r
+       //\r
+               MM_FreePtr (&grsegs[i]);\r
+               UNMARKGRCHUNK(i);\r
+\r
+       // Create three shifted strips and move 'em to latch!\r
+       //\r
+               for (loop=3; loop; loop--)\r
+               {\r
+               // Produce current shift for this strip\r
+               //\r
+                       latchpics[numpics++] = destoff;\r
+                       CAL_ShiftSprite ((unsigned)work,0,workdest,pictable[pic].width,\r
+                                                                 pictable[pic].height,loop*2,false);\r
+\r
+               // Copy this shift to latch memory\r
+               //\r
+                       VW_MemToScreen ((memptr)((unsigned)work+(workdest>>4)),destoff,planesize,1);\r
+                       destoff+=planesize;\r
+               }\r
+\r
+       // Copy unshifted strip to latch\r
+       //\r
+               latchpics[numpics++] = destoff;\r
+               planesize = pictable[pic].width * pictable[pic].height;\r
+               VW_MemToScreen (work,destoff,planesize,1);\r
+               destoff+=planesize;\r
+\r
+       // Free work buffer\r
+       //\r
+               MM_FreePtr(&work);\r
+       }\r
+#endif\r
+\r
+// Keep track of how much latch memory we have...\r
+//\r
+       latchmemavail = 65535-destoff;\r
+\r
+       EGAMAPMASK(15);\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+===================\r
+=\r
+= FizzleOut\r
+=\r
+===================\r
+*/\r
+\r
+void FizzleOut (int showlevel)\r
+{\r
+       unsigned page1,page2;\r
+//\r
+// fizzle fade screen to grey\r
+//\r
+       bufferofs = screenloc[(screenpage+1)%3];\r
+       if (showlevel)\r
+               DrawEnterScreen ();\r
+       FizzleFade(bufferofs,displayofs,VIEWWIDTH,VIEWHEIGHT,false);\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= FreeUpMemory\r
+=\r
+====================\r
+*/\r
+\r
+void FreeUpMemory (void)\r
+{\r
+       int     i;\r
+\r
+       for (i=0;i<NUMSCALEPICS;i++)\r
+               if (shapedirectory[i])\r
+                       MM_SetPurge (&(memptr)shapedirectory[i],3);\r
+\r
+       for (i=0;i<NUMSCALEWALLS;i++)\r
+               if (walldirectory[i])\r
+                       MM_SetPurge (&(memptr)walldirectory[i],3);\r
+}\r
+\r
+//==========================================================================\r
+\r
+#if 0\r
+\r
+/*\r
+==================\r
+=\r
+= DrawHighScores\r
+=\r
+==================\r
+*/\r
+\r
+void    DrawHighScores(void)\r
+{\r
+       char            buffer[16],*str;\r
+       word            i,j,\r
+                               w,h,\r
+                               x,y;\r
+       HighScore       *s;\r
+\r
+\r
+       CA_CacheGrChunk (HIGHSCORESPIC);\r
+       VWB_DrawPic (0,0,HIGHSCORESPIC);\r
+       MM_SetPurge (&grsegs[HIGHSCORESPIC],3);\r
+       UNMARKGRCHUNK(HIGHSCORESPIC);\r
+\r
+       for (i = 0,s = Scores;i < MaxScores;i++,s++)\r
+       {\r
+               PrintY = 68 + (16 * i);\r
+\r
+               //\r
+               // name\r
+               //\r
+               PrintX = 60;\r
+               US_Print(s->name);\r
+\r
+               //\r
+               // level\r
+               //\r
+               ultoa(s->completed,buffer,10);\r
+               for (str = buffer;*str;str++)\r
+                       *str = *str + (129 - '0');      // Used fixed-width numbers (129...)\r
+               USL_MeasureString(buffer,&w,&h);\r
+               PrintX = (25 * 8) - 8 - w;\r
+               US_Print(buffer);\r
+\r
+               //\r
+               // score\r
+               //\r
+               ultoa(s->score,buffer,10);\r
+               for (str = buffer;*str;str++)\r
+                       *str = *str + (129 - '0');      // Used fixed-width numbers (129...)\r
+               USL_MeasureString(buffer,&w,&h);\r
+               PrintX = (34 * 8) - 8 - w;\r
+               US_Print(buffer);\r
+       }\r
+\r
+       fontcolor = F_BLACK;\r
+}\r
+\r
+\r
+\r
+/*\r
+=======================\r
+=\r
+= CheckHighScore\r
+=\r
+=======================\r
+*/\r
+\r
+void    CheckHighScore (long score,word other)\r
+{\r
+       word            i,j;\r
+       int                     n;\r
+       HighScore       myscore;\r
+\r
+       strcpy(myscore.name,"");\r
+       myscore.score = score;\r
+       myscore.completed = other;\r
+\r
+       for (i = 0,n = -1;i < MaxScores;i++)\r
+       {\r
+               if\r
+               (\r
+                       (myscore.score > Scores[i].score)\r
+               ||      (\r
+                               (myscore.score == Scores[i].score)\r
+                       &&      (myscore.completed > Scores[i].completed)\r
+                       )\r
+               )\r
+               {\r
+                       for (j = MaxScores;--j > i;)\r
+                               Scores[j] = Scores[j - 1];\r
+                       Scores[i] = myscore;\r
+                       n = i;\r
+                       HighScoresDirty = true;\r
+                       break;\r
+               }\r
+       }\r
+\r
+       if (n != -1)\r
+       {\r
+       //\r
+       // got a high score\r
+       //\r
+               DrawHighScores ();\r
+               PrintY = 68 + (16 * n);\r
+               PrintX = 60;\r
+               US_LineInput(PrintX,PrintY,Scores[n].name,nil,true,MaxHighName,100);\r
+       }\r
+}\r
+\r
+#endif\r
+\r
+\r
+//==========================================================================\r
+\r
+/*\r
+===================\r
+=\r
+= GameLoop\r
+=\r
+===================\r
+*/\r
+\r
+void GameLoop (void)\r
+{\r
+       boolean wait = false;\r
+       int i,xl,yl,xh,yh;\r
+       char num[20];\r
+#ifdef PROFILE\r
+       clock_t start,end;\r
+#endif\r
+\r
+       DrawPlayScreen ();\r
+       IN_ClearKeysDown();\r
+\r
+restart:\r
+       if (!loadedgame)\r
+       {\r
+               gamestate.difficulty = restartgame;\r
+               restartgame = gd_Continue;\r
+               DrawEnterScreen ();\r
+               if (gamestate.mapon != 8)\r
+                       fizzlein = true;\r
+               wait = true;\r
+       }\r
+\r
+       do\r
+       {\r
+               playstate = gd_Continue;\r
+               if (!loadedgame)\r
+                       SetupGameLevel ();\r
+               else\r
+                       loadedgame = false;\r
+\r
+               FreeUpMemory();\r
+               LoadLatchMem();\r
+               CacheScaleds ();\r
+\r
+               if (EASYMODEON)\r
+                       DisplaySMsg("*** NOVICE ***", NULL);\r
+               else\r
+                       DisplaySMsg("*** WARRIOR ***", NULL);\r
+\r
+               status_delay = 250;\r
+\r
+               RedrawStatusWindow();\r
+               if (wait)\r
+               {\r
+                       VW_WaitVBL(120);\r
+                       wait = false;\r
+               }\r
+\r
+#ifdef PROFILE\r
+start = clock();\r
+while (start == clock());\r
+start++;\r
+#endif\r
+\r
+               PlayLoop ();\r
+\r
+#ifdef PROFILE\r
+end = clock();\r
+itoa(end-start,str,10);\r
+               Quit (str);\r
+#endif\r
+\r
+\r
+               switch (playstate)\r
+               {\r
+               case ex_abort:\r
+                       FreeUpMemory ();\r
+                       return;\r
+               case ex_resetgame:\r
+                       NewGame();\r
+               case ex_loadedgame:\r
+               case ex_warped:\r
+                       FreeUpMemory();\r
+                       if (playstate != ex_resetgame)\r
+                               DisplayMsg("                                      ", NULL);\r
+                       DisplaySMsg("                  ", NULL);\r
+                       goto restart;\r
+               case ex_victorious:\r
+                       screenpage = 0;\r
+                       bufferofs = 0;\r
+                       status_flag = 0;\r
+                       return;\r
+               }\r
+\r
+       } while (1);\r
+\r
+}\r
+\r
+\r
+#if 0\r
+//\r
+// make wall pictures purgable\r
+//\r
+       for (i=0;i<NUMSCALEWALLS;i++)\r
+               if (walldirectory[i])\r
+                       MM_SetPurge (&(memptr)walldirectory[i],3);\r
+\r
+\r
+//\r
+// cache wall pictures back in\r
+//\r
+       for (i=1;i<NUMFLOORS;i++)\r
+               if (tileneeded[i])\r
+               {\r
+                       SetupScaleWall (walllight1[i]);\r
+                       SetupScaleWall (walllight2[i]);\r
+                       SetupScaleWall (walldark1[i]);\r
+                       SetupScaleWall (walldark2[i]);\r
+               }\r
+#endif\r
diff --git a/16/cawat/C5_MAIN.C b/16/cawat/C5_MAIN.C
new file mode 100644 (file)
index 0000000..c7e0f82
--- /dev/null
@@ -0,0 +1,1052 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// C3_MAIN.C\r
+\r
+#define CATALOG\r
+\r
+\r
+#include <time.h>\r
+#include <stdarg.h>\r
+\r
+#include "DEF.H"\r
+#include "GELIB.H"\r
+#pragma hdrstop\r
+#include <dir.h>\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+PresenterInfo MainHelpText;\r
+\r
+GameDiff restartgame;\r
+boolean loadedgame,abortgame,ingame;\r
+\r
+\r
+memptr         scalesegs[NUMPICS];\r
+char           str[80],str2[20];\r
+unsigned       tedlevelnum;\r
+boolean                tedlevel;\r
+gametype       gamestate;\r
+exittype       playstate;\r
+char   SlowMode = 0;\r
+int starting_level;\r
+\r
+//extern unsigned scolor,gcolor;                                       //NPM\r
+\r
+short NumGames=0;\r
+unsigned Flags=0;\r
+\r
+boolean LoadShapes = true;\r
+boolean EASYMODEON = false;\r
+\r
+void DisplayIntroText(void);\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+\r
+//===========================================================================\r
+\r
+#if 0\r
+// JAB Hack begin\r
+#define        MyInterrupt     0x60\r
+void interrupt (*intaddr)();\r
+void interrupt (*oldintaddr)();\r
+       char    *JHParmStrings[] = {"no386",nil};\r
+\r
+void\r
+jabhack(void)\r
+{\r
+extern void far jabhack2(void);\r
+extern int far CheckIs386(void);\r
+\r
+       int     i;\r
+\r
+       oldintaddr = getvect(MyInterrupt);\r
+\r
+       for (i = 1;i < _argc;i++)\r
+               if (US_CheckParm(_argv[i],JHParmStrings) == 0)\r
+                       return;\r
+\r
+       if (CheckIs386())\r
+       {\r
+               jabhack2();\r
+               setvect(MyInterrupt,intaddr);\r
+       }\r
+}\r
+\r
+void\r
+jabunhack(void)\r
+{\r
+       setvect(MyInterrupt,oldintaddr);\r
+}\r
+//     JAB Hack end\r
+#endif\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= NewGame\r
+=\r
+= Set up new game to start from the beginning\r
+=\r
+=====================\r
+*/\r
+\r
+void NewGame (void)\r
+{\r
+       if (!loadedgame)\r
+       {\r
+               memset (&gamestate,0,sizeof(gamestate));\r
+               gamestate.mapon = starting_level;\r
+               gamestate.body = MAXBODY;\r
+       }\r
+\r
+       BGFLAGS = BGF_NOT_LIGHTNING;\r
+       Flags &= FL_CLEAR;\r
+\r
+       boltsleft = bolttimer = 0;\r
+\r
+//     memset (gamestate.levels,-1,sizeof(gamestate.levels));\r
+}\r
+\r
+//===========================================================================\r
+\r
+#define RLETAG 0xABCD\r
+\r
+/*\r
+==================\r
+=\r
+= SaveTheGame\r
+=\r
+==================\r
+*/\r
+\r
+boolean        SaveTheGame(int file)\r
+{\r
+       word    i,compressed,expanded;\r
+       objtype *o;\r
+       memptr  bigbuffer;\r
+\r
+       // save the sky and ground colors\r
+       if (!CA_FarWrite(file,(void far *)&skycolor,sizeof(skycolor)))\r
+               return(false);\r
+       if (!CA_FarWrite(file,(void far *)&groundcolor,sizeof(groundcolor)))\r
+               return(false);\r
+\r
+       if (!CA_FarWrite(file,(void far *)&FreezeTime,sizeof(FreezeTime)))\r
+               return(false);\r
+\r
+       if (!CA_FarWrite(file,(void far *)&gamestate,sizeof(gamestate)))\r
+               return(false);\r
+\r
+       if (!CA_FarWrite(file,(void far *)&EASYMODEON,sizeof(EASYMODEON)))\r
+               return(false);\r
+\r
+       expanded = mapwidth * mapheight * 2;\r
+       MM_GetPtr (&bigbuffer,expanded);\r
+\r
+       for (i = 0;i < 3;i+=2)  // Write planes 0 and 2\r
+       {\r
+//\r
+// leave a word at start of compressed data for compressed length\r
+//\r
+               compressed = (unsigned)CA_RLEWCompress ((unsigned huge *)mapsegs[i]\r
+                       ,expanded,((unsigned huge *)bigbuffer)+1,RLETAG);\r
+\r
+               *(unsigned huge *)bigbuffer = compressed;\r
+\r
+               if (!CA_FarWrite(file,(void far *)bigbuffer,compressed+2) )\r
+               {\r
+                       MM_FreePtr (&bigbuffer);\r
+                       return(false);\r
+               }\r
+       }\r
+\r
+       for (o = player;o;o = o->next)\r
+               if (!CA_FarWrite(file,(void far *)o,sizeof(objtype)))\r
+               {\r
+                       MM_FreePtr (&bigbuffer);\r
+                       return(false);\r
+               }\r
+\r
+       MM_FreePtr (&bigbuffer);\r
+\r
+       return(true);\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+==================\r
+=\r
+= LoadTheGame\r
+=\r
+==================\r
+*/\r
+\r
+boolean        LoadTheGame(int file)\r
+{\r
+       unsigned        i,x,y;\r
+       objtype         *obj,*prev,*next,*followed;\r
+       unsigned        compressed,expanded;\r
+       unsigned        far *map,tile;\r
+       memptr          bigbuffer;\r
+\r
+       screenpage = 0;\r
+       FreeUpMemory();\r
+\r
+       playstate = ex_loadedgame;\r
+       // load the sky and ground colors\r
+       if (!CA_FarRead(file,(void far *)&skycolor,sizeof(skycolor)))\r
+               return(false);\r
+       if (!CA_FarRead(file,(void far *)&groundcolor,sizeof(groundcolor)))\r
+               return(false);\r
+\r
+       if (!CA_FarRead(file,(void far *)&FreezeTime,sizeof(FreezeTime)))\r
+               return(false);\r
+\r
+       if (!CA_FarRead(file,(void far *)&gamestate,sizeof(gamestate)))\r
+               return(false);\r
+\r
+       if (!CA_FarRead(file,(void far *)&EASYMODEON,sizeof(EASYMODEON)))\r
+               return(false);\r
+\r
+       SetupGameLevel ();              // load in and cache the base old level\r
+\r
+       if (!FindFile(Filename,"SAVE GAME",-1))\r
+               Quit("Error: Can't find saved game file!");\r
+\r
+       expanded = mapwidth * mapheight * 2;\r
+       MM_GetPtr (&bigbuffer,expanded);\r
+\r
+       for (i = 0;i < 3;i+=2)  // Read planes 0 and 2\r
+       {\r
+               if (!CA_FarRead(file,(void far *)&compressed,sizeof(compressed)) )\r
+               {\r
+                       MM_FreePtr (&bigbuffer);\r
+                       return(false);\r
+               }\r
+\r
+               if (!CA_FarRead(file,(void far *)bigbuffer,compressed) )\r
+               {\r
+                       MM_FreePtr (&bigbuffer);\r
+                       return(false);\r
+               }\r
+\r
+               CA_RLEWexpand ((unsigned huge *)bigbuffer,\r
+                       (unsigned huge *)mapsegs[i],expanded,RLETAG);\r
+       }\r
+\r
+       MM_FreePtr (&bigbuffer);\r
+//\r
+// copy the wall data to a data segment array again, to handle doors and\r
+// bomb walls that are allready opened\r
+//\r
+       memset (tilemap,0,sizeof(tilemap));\r
+       memset (actorat,0,sizeof(actorat));\r
+       map = mapsegs[0];\r
+       for (y=0;y<mapheight;y++)\r
+               for (x=0;x<mapwidth;x++)\r
+               {\r
+                       tile = *map++;\r
+                       if (tile<NUMFLOORS)\r
+                       {\r
+                               if (tile != INVISIBLEWALL)\r
+                                       tilemap[x][y] = tile;\r
+                               if (tile>0)\r
+                                       (unsigned)actorat[x][y] = tile;\r
+                       }\r
+               }\r
+\r
+\r
+       // Read the object list back in - assumes at least one object in list\r
+\r
+       InitObjList ();\r
+       new = player;\r
+       while (true)\r
+       {\r
+               prev = new->prev;\r
+               next = new->next;\r
+               if (!CA_FarRead(file,(void far *)new,sizeof(objtype)))\r
+                       return(false);\r
+               followed = new->next;\r
+               new->prev = prev;\r
+               new->next = next;\r
+               actorat[new->tilex][new->tiley] = new;  // drop a new marker\r
+\r
+               if (followed)\r
+                       GetNewObj (false);\r
+               else\r
+                       break;\r
+       }\r
+\r
+       return(true);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==================\r
+=\r
+= ResetGame\r
+=\r
+==================\r
+*/\r
+\r
+void ResetGame(void)\r
+{\r
+       NewGame ();\r
+\r
+       ca_levelnum--;\r
+       ca_levelbit>>=1;\r
+       CA_ClearMarks();\r
+       ca_levelbit<<=1;\r
+       ca_levelnum++;\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+==========================\r
+=\r
+= ShutdownId\r
+=\r
+= Shuts down all ID_?? managers\r
+=\r
+==========================\r
+*/\r
+\r
+void ShutdownId (void)\r
+{\r
+  US_Shutdown ();\r
+#ifndef PROFILE\r
+  SD_Shutdown ();\r
+  IN_Shutdown ();\r
+#endif\r
+  VW_Shutdown ();\r
+  CA_Shutdown ();\r
+  MM_Shutdown ();\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==========================\r
+=\r
+= InitGame\r
+=\r
+= Load a few things right away\r
+=\r
+==========================\r
+*/\r
+\r
+void InitGame (void)\r
+{\r
+       unsigned        segstart,seglength;\r
+       int                     i,x,y;\r
+       unsigned        *blockstart;\r
+\r
+//     US_TextScreen();\r
+\r
+       MM_Startup ();\r
+       VW_Startup ();\r
+#ifndef PROFILE\r
+       IN_Startup ();\r
+       SD_Startup ();\r
+#endif\r
+       US_Startup ();\r
+\r
+//     US_UpdateTextScreen();\r
+\r
+       CA_Startup ();\r
+       US_Setup ();\r
+\r
+       US_SetLoadSaveHooks(LoadTheGame,SaveTheGame,ResetGame);\r
+\r
+\r
+//\r
+// load in and lock down some basic chunks\r
+//\r
+\r
+       CA_ClearMarks ();\r
+\r
+       CA_MarkGrChunk(STARTFONT);\r
+       CA_MarkGrChunk(STARTTILE8);\r
+       CA_MarkGrChunk(STARTTILE8M);\r
+       CA_MarkGrChunk(HAND1PICM);\r
+\r
+       CA_MarkGrChunk(NORTHICONSPR);\r
+       CA_CacheMarks (NULL);\r
+\r
+       MM_SetLock (&grsegs[STARTFONT],true);\r
+       MM_SetLock (&grsegs[STARTTILE8],true);\r
+       MM_SetLock (&grsegs[STARTTILE8M],true);\r
+       MM_SetLock (&grsegs[HAND1PICM],true);\r
+\r
+       fontcolor = WHITE;\r
+\r
+\r
+//\r
+// build some tables\r
+//\r
+       for (i=0;i<MAPSIZE;i++)\r
+               nearmapylookup[i] = &tilemap[0][0]+MAPSIZE*i;\r
+\r
+       for (i=0;i<PORTTILESHIGH;i++)\r
+               uwidthtable[i] = UPDATEWIDE*i;\r
+\r
+       blockstart = &blockstarts[0];\r
+       for (y=0;y<UPDATEHIGH;y++)\r
+               for (x=0;x<UPDATEWIDE;x++)\r
+                       *blockstart++ = SCREENWIDTH*16*y+x*TILEWIDTH;\r
+\r
+       BuildTables ();                 // 3-d tables\r
+\r
+       SetupScaling ();\r
+\r
+#ifndef PROFILE\r
+//     US_FinishTextScreen();\r
+#endif\r
+\r
+#if 0\r
+//\r
+// reclaim the memory from the linked in text screen\r
+//\r
+       segstart = FP_SEG(&introscn);\r
+       seglength = 4000/16;\r
+       if (FP_OFF(&introscn))\r
+       {\r
+               segstart++;\r
+               seglength--;\r
+       }\r
+       MML_UseSpace (segstart,seglength);\r
+#endif\r
+\r
+       VW_SetScreenMode (GRMODE);\r
+       ge_textmode = false;\r
+//     VW_ColorBorder (3);\r
+       VW_ClearVideo (BLACK);\r
+\r
+//\r
+// initialize variables\r
+//\r
+       updateptr = &update[0];\r
+       *(unsigned *)(updateptr + UPDATEWIDE*PORTTILESHIGH) = UPDATETERMINATE;\r
+       bufferofs = 0;\r
+       displayofs = 0;\r
+       VW_SetLineWidth(SCREENWIDTH);\r
+}\r
+\r
+//===========================================================================\r
+\r
+void clrscr (void);            // can't include CONIO.H because of name conflicts...\r
+\r
+/*\r
+==========================\r
+=\r
+= Quit\r
+=\r
+==========================\r
+*/\r
+\r
+void Quit (char *error, ...)\r
+{\r
+       short exit_code=0;\r
+       unsigned        finscreen;\r
+\r
+       va_list ap;\r
+\r
+       va_start(ap,error);\r
+\r
+#ifndef CATALOG\r
+       if (!error)\r
+       {\r
+               CA_SetAllPurge ();\r
+               CA_CacheGrChunk (PIRACY);\r
+               finscreen = (unsigned)grsegs[PIRACY];\r
+       }\r
+#endif\r
+\r
+       ShutdownId ();\r
+\r
+       if (error && *error)\r
+       {\r
+               vprintf(error,ap);\r
+               exit_code = 1;\r
+       }\r
+#ifndef CATALOG\r
+       else\r
+       if (!NoWait)\r
+       {\r
+               movedata (finscreen,0,0xb800,0,4000);\r
+               bioskey (0);\r
+       }\r
+#endif\r
+\r
+       va_end(ap);\r
+\r
+#ifndef CATALOG\r
+       if (!error)\r
+       {\r
+               _argc = 2;\r
+               _argv[1] = "LAST.SHL";\r
+               _argv[2] = "ENDSCN.SCN";\r
+               _argv[3] = NULL;\r
+               if (execv("LOADSCN.EXE", _argv) == -1)\r
+               {\r
+                       clrscr();\r
+                       puts("Couldn't find executable LOADSCN.EXE.\n");\r
+                       exit(1);\r
+               }\r
+       }\r
+#endif\r
+\r
+       exit(exit_code);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==================\r
+=\r
+= TEDDeath\r
+=\r
+==================\r
+*/\r
+\r
+void   TEDDeath(void)\r
+{\r
+       ShutdownId();\r
+       execlp("TED5.EXE","TED5.EXE","/LAUNCH",NULL);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= DisplayDepartment\r
+=\r
+====================\r
+*/\r
+void DisplayDepartment(char *text)\r
+{\r
+       short temp;\r
+\r
+//     bufferofs = 0;\r
+       PrintY = 1;\r
+       WindowX = 0;\r
+       WindowW = 320;\r
+\r
+       VW_Bar(WindowX,PrintY+1,WindowW,7,7);\r
+       temp = fontcolor;\r
+       fontcolor = 2;\r
+       US_CPrintLine (text);\r
+       fontcolor = temp;\r
+}\r
+\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= DemoLoop\r
+=\r
+=====================\r
+*/\r
+\r
+void   DemoLoop (void)\r
+{\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+// main game cycle\r
+/////////////////////////////////////////////////////////////////////////////\r
+\r
+       displayofs = bufferofs = 0;\r
+       VW_Bar (0,0,320,200,0);\r
+       VW_SetScreen(0,0);\r
+\r
+//\r
+// Read in all the graphic images needed for the title sequence\r
+//\r
+               VW_WaitVBL(1);\r
+               IN_ReadControl(0,&control);\r
+\r
+//     set EASYMODE\r
+//\r
+       if (stricmp(_argv[2], "1") == 0)\r
+               EASYMODEON = true;\r
+       else\r
+               EASYMODEON = false;\r
+\r
+// restore game\r
+//\r
+       if (stricmp(_argv[3], "1") == 0)\r
+       {\r
+               VW_FadeOut();\r
+               bufferofs = displayofs = 0;\r
+               VW_Bar(0,0,320,200,0);\r
+               if (GE_LoadGame())\r
+               {\r
+                       loadedgame = true;\r
+                       playstate = ex_loadedgame;\r
+                       Keyboard[sc_Enter] = true;\r
+                       VW_Bar(0,0,320,200,0);\r
+                       ColoredPalette();\r
+               }\r
+               VW_Bar(0,0,320,200,0);\r
+               VW_FadeIn();\r
+       }\r
+\r
+       // Play a game\r
+       //\r
+               restartgame = gd_Normal;\r
+               NewGame();\r
+               GameLoop();\r
+}\r
+\r
+\r
+//-------------------------------------------------------------------------\r
+// DisplayIntroText()\r
+//-------------------------------------------------------------------------\r
+void DisplayIntroText()\r
+{\r
+       PresenterInfo pi;\r
+\r
+#ifdef TEXT_PRESENTER\r
+       char *toptext = "You stand before the gate leading into the Towne "\r
+                                                "of Morbidity.... "\r
+                                                "^XX";\r
+\r
+       char *bottomtext = "Enter now boldly to defeat the evil Nemesis "\r
+                                                        "deep inside the catacombs."\r
+                                                        "\r
+                                                        "^XX";\r
+#endif\r
+\r
+       char oldfontcolor=fontcolor;\r
+\r
+       fontcolor = 14;\r
+\r
+\r
+#ifdef TEXT_PRESENTER\r
+       pi.xl = 0;\r
+       pi.yl = 0;\r
+       pi.xh = 319;\r
+       pi.yh = 1;\r
+       pi.bgcolor = 0;\r
+       pi.script[0] = (char far *)toptext;\r
+       Presenter(&pi);\r
+\r
+       pi.yl = 160;\r
+       pi.yh = 161;\r
+       pi.script[0] = (char far *)bottomtext;\r
+       Presenter(&pi);\r
+\r
+#else\r
+       PrintY = 1;\r
+       PrintX = 0;\r
+       WindowX = 0;\r
+       WindowW = 320;\r
+       US_Print ("         You stand before the gate leading into\n");\r
+       US_Print ("                 the Towne of Morbidity...\n");\r
+\r
+       PrintY = 180;\r
+       US_Print ("    Enter now boldly to defeat the evil Nemesis\n");\r
+       US_Print ("              deep inside the catacombs.\n");\r
+\r
+#endif\r
+\r
+       fontcolor = oldfontcolor;\r
+}\r
+\r
+#if 0\r
+boolean ChooseGameLevel()\r
+{\r
+       char choices[] = {sc_Escape,sc_E,sc_N,sc_H,0},ch;\r
+\r
+       CenterWindow(20,10);\r
+\r
+       US_Print("\n   Choose difficulty level:\n");\r
+       US_Print("       (E)asy\n");\r
+       US_Print("       (N)ormal\n");\r
+       US_Print("       (H)ard\n");\r
+       US_Print("\n      (ESC)ape aborts\n");\r
+\r
+//     VW_UpdateScreen();\r
+       if ((ch=GetKeyChoice(choices)) == sc_Escape)\r
+       {\r
+               while (Keyboard[sc_Escape]);\r
+               LastScan = 0;\r
+               return(false);\r
+       }\r
+\r
+       if (ch == sc_E)\r
+               restartgame = gd_Easy;\r
+       else\r
+       if (ch == sc_N)\r
+               restartgame = gd_Normal;\r
+       else\r
+               restartgame = gd_Hard;\r
+\r
+       return(true);\r
+}\r
+#endif\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==========================\r
+=\r
+= SetupScalePic\r
+=\r
+==========================\r
+*/\r
+\r
+void SetupScalePic (unsigned picnum)\r
+{\r
+       unsigned        scnum;\r
+\r
+       if (picnum == 1)\r
+               return;\r
+\r
+       scnum = picnum-FIRSTSCALEPIC;\r
+\r
+       if (shapedirectory[scnum])\r
+       {\r
+               MM_SetPurge (&(memptr)shapedirectory[scnum],0);\r
+               return;                                 // allready in memory\r
+       }\r
+\r
+       CA_CacheGrChunk (picnum);\r
+       DeplanePic (picnum);\r
+       shapesize[scnum] = BuildCompShape (&shapedirectory[scnum]);\r
+       grneeded[picnum]&= ~ca_levelbit;\r
+       MM_FreePtr (&grsegs[picnum]);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==========================\r
+=\r
+= SetupScaleWall\r
+=\r
+==========================\r
+*/\r
+\r
+void SetupScaleWall (unsigned picnum)\r
+{\r
+       int             x,y;\r
+       unsigned        scnum;\r
+       byte    far *dest;\r
+\r
+       if (picnum == 1)\r
+               return;\r
+\r
+       scnum = picnum-FIRSTWALLPIC;\r
+\r
+       if (walldirectory[scnum])\r
+       {\r
+               MM_SetPurge (&walldirectory[scnum],0);\r
+               return;                                 // allready in memory\r
+       }\r
+\r
+       CA_CacheGrChunk (picnum);\r
+       DeplanePic (picnum);\r
+       MM_GetPtr(&walldirectory[scnum],64*64);\r
+       dest = (byte far *)walldirectory[scnum];\r
+       for (x=0;x<64;x++)\r
+               for (y=0;y<64;y++)\r
+                       *dest++ = spotvis[y][x];\r
+       grneeded[picnum]&= ~ca_levelbit;\r
+       MM_FreePtr (&grsegs[picnum]);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==========================\r
+=\r
+= SetupScaling\r
+=\r
+==========================\r
+*/\r
+\r
+void SetupScaling (void)\r
+{\r
+       int             i,x,y;\r
+       byte    far *dest;\r
+\r
+//\r
+// build the compiled scalers\r
+//\r
+       for (i=1;i<=VIEWWIDTH/2;i++)\r
+               BuildCompScale (i*2,&scaledirectory[i]);\r
+}\r
+\r
+//===========================================================================\r
+\r
+int    showscorebox;\r
+\r
+void RF_FixOfs (void)\r
+{\r
+}\r
+\r
+void HelpScreens (void)\r
+{\r
+}\r
+\r
+\r
+#if 0\r
+/*\r
+==================\r
+=\r
+= CheckMemory\r
+=\r
+==================\r
+*/\r
+\r
+#define MINMEMORY      400000l\r
+\r
+void   CheckMemory(void)\r
+{\r
+       unsigned        finscreen;\r
+\r
+       if (Flags & FL_NOMEMCHECK)\r
+               return;\r
+\r
+       if (mminfo.nearheap+mminfo.farheap+mminfo.EMSmem+mminfo.XMSmem\r
+               >= MINMEMORY)\r
+               return;\r
+\r
+       CA_CacheGrChunk (OUTOFMEM);\r
+       finscreen = (unsigned)grsegs[OUTOFMEM];\r
+       ShutdownId ();\r
+       movedata (finscreen,7,0xb800,0,4000);\r
+       gotoxy (1,24);\r
+       exit(1);\r
+}\r
+#endif\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+==========================\r
+=\r
+= main\r
+=\r
+==========================\r
+*/\r
+\r
+char                   *MainParmStrings[] = {"q","l","ver","nomemcheck","helptest",nil};\r
+\r
+void main (void)\r
+{\r
+       short i;\r
+\r
+       starting_level = 0;\r
+\r
+       for (i = 1;i < _argc;i++)\r
+       {\r
+               switch (US_CheckParm(_argv[i],MainParmStrings))\r
+               {\r
+                       case 0:\r
+                               Flags |= FL_QUICK;\r
+                       break;\r
+\r
+                       case 1:\r
+                               starting_level = atoi(_argv[i]+1);\r
+                               if ((starting_level < 0) || (starting_level > LASTMAP-1))\r
+                                       starting_level = 0;\r
+                       break;\r
+\r
+                       case 2:\r
+                               printf("%s\n", GAMENAME);\r
+                               printf("Copyright 1992-93 Softdisk Publishing\n");\r
+                               printf("%s %s\n",VERSION,REVISION);\r
+                               printf("\n");\r
+                               exit(0);\r
+                       break;\r
+\r
+                       case 3:\r
+                               Flags |= FL_NOMEMCHECK;\r
+                       break;\r
+\r
+                       case 4:\r
+                               Flags |= (FL_HELPTEST|FL_QUICK);\r
+                       break;\r
+               }\r
+       }\r
+\r
+       if (stricmp(_argv[1], "^(a@&r`"))\r
+               Quit("You must type CATARM to run CATACOMB ARMAGEDDON 3-D\n");\r
+\r
+       MainHelpText.xl = 0;\r
+       MainHelpText.yl = 0;\r
+       MainHelpText.xh = 639;\r
+       MainHelpText.yh = 199;\r
+       MainHelpText.bgcolor = 7;\r
+       MainHelpText.ltcolor = 15;\r
+       MainHelpText.dkcolor = 8;\r
+\r
+//     jabhack();\r
+\r
+       randomize();\r
+\r
+       InitGame ();\r
+//     CheckMemory ();\r
+       LoadLatchMem ();\r
+\r
+//     if (!LoadTextFile("MAINHELP."EXT,&MainHelpText))\r
+//             Quit("Can't load MAINHELP."EXT);\r
+\r
+#ifdef PROFILE\r
+       NewGame ();\r
+       GameLoop ();\r
+#endif\r
+\r
+       DemoLoop();\r
+       Quit(NULL);\r
+}\r
+\r
+//-------------------------------------------------------------------------\r
+// Display640()\r
+//-------------------------------------------------------------------------\r
+void Display640()\r
+{\r
+// Can you believe it takes all this just to change to 640 mode!!???!\r
+//\r
+       VW_ScreenToScreen(0,FREESTART-STATUSLEN,40,80);\r
+       VW_SetLineWidth(80);\r
+       MoveScreen(0,0);\r
+       VW_Bar (0,0,640,200,0);\r
+       VW_SetScreenMode(EGA640GR);\r
+       VW_SetLineWidth(80);\r
+       BlackPalette();\r
+}\r
+\r
+//-------------------------------------------------------------------------\r
+// Display320()\r
+//-------------------------------------------------------------------------\r
+void Display320()\r
+{\r
+// Can you believe it takes all this just to change to 320 mode!!???!\r
+//\r
+       VW_ColorBorder(0);\r
+       VW_FadeOut();\r
+       VW_SetLineWidth(40);\r
+       MoveScreen(0,0);\r
+       VW_Bar (0,0,320,200,0);\r
+       VW_SetScreenMode(EGA320GR);\r
+       BlackPalette();\r
+       VW_ScreenToScreen(FREESTART-STATUSLEN,0,40,80);\r
+}\r
+\r
+void PrintHelp(void)\r
+{\r
+       char oldfontcolor = fontcolor;\r
+       PrintY = 1;\r
+       WindowX = 135;\r
+       WindowW = 640;\r
+\r
+       VW_FadeOut();\r
+       bufferofs = displayofs = screenloc[0];\r
+       VW_Bar(0,0,320,200,0);\r
+\r
+       Display640();\r
+\r
+       VW_Bar(0, 0, 640, 200, 7);\r
+\r
+       fontcolor = (7 ^ 1);\r
+       US_Print ("\n\n                    SUMMARY OF GAME CONTROLS\n\n");\r
+\r
+       fontcolor = (7 ^ 4);\r
+       US_Print ("         ACTION\n\n");\r
+\r
+       US_Print ("Arrow keys, joystick, or mouse\n");\r
+       US_Print ("TAB or V while turning\n");\r
+       US_Print ("ALT or Button 2 while turning\n");\r
+       US_Print ("CTRL or Button 1\n");\r
+       US_Print ("Z\n");\r
+       US_Print ("X or Enter\n");\r
+       US_Print ("F1\n");\r
+       US_Print ("F2\n");\r
+       US_Print ("F3\n");\r
+       US_Print ("F4\n");\r
+       US_Print ("F5\n");\r
+       US_Print ("ESC\n\n");\r
+#ifndef CATALOG\r
+       fontcolor = (7 ^ 0);\r
+       US_Print ("          (See complete Instructions for more info)\n");\r
+#endif\r
+\r
+       fontcolor = (7 ^ 8);\r
+       PrintX = 400;\r
+       PrintY = 37;\r
+       WindowX = 400;\r
+       US_Print ("   REACTION\n\n");\r
+       US_Print ("Move and turn\n");\r
+       US_Print ("Turn quickly (Quick Turn)\n");\r
+       US_Print ("Move sideways\n");\r
+       US_Print ("Shoot a Missile\n");\r
+       US_Print ("Shoot a Zapper\n");\r
+       US_Print ("Shoot an Xterminator\n");\r
+       US_Print ("Help (this screen)\n");\r
+       US_Print ("Sound control\n");\r
+       US_Print ("Save game position\n");\r
+       US_Print ("Restore a saved game\n");\r
+       US_Print ("Joystick control\n");\r
+       US_Print ("System options\n\n\n");\r
+\r
+       VW_UpdateScreen();\r
+       VW_FadeIn();\r
+       VW_ColorBorder(8 | 56);\r
+       IN_Ack();\r
+       Display320();\r
+       fontcolor = oldfontcolor;\r
+}
\ No newline at end of file
diff --git a/16/cawat/C5_PLAY.C b/16/cawat/C5_PLAY.C
new file mode 100644 (file)
index 0000000..96f7775
--- /dev/null
@@ -0,0 +1,1425 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// C3_PLAY.C\r
+\r
+#include "DEF.H"\r
+#include "gelib.h"\r
+#pragma hdrstop\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define POINTTICS      6\r
+#define PAUSE 300\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+byte bcolor;\r
+short skytimer=-1,skytimer_reset;\r
+short groundtimer=-1,groundtimer_reset;\r
+\r
+unsigned scolor,gcolor;\r
+unsigned *skycolor,*groundcolor,debug_sky,debug_gnd;\r
+\r
+unsigned nocolorchange=0xFFFF;\r
+byte BGFLAGS,                          // global that holds all current flags\r
+         bgflag;                               // used by BG changer, this flag is set when done\r
+\r
+\r
+unsigned sky_daytonight[]={0x0909,0x0101,0x0808,0x0000,0xFFFF};\r
+//unsigned gnd_daytonight[]={0x0202,0xFFFF};\r
+\r
+unsigned sky_lightning[]={0x0101,0x0909,0x0f0f,0x0808,0x0000,0xFFFF};\r
+\r
+unsigned sky_colors[NUMLEVELS]={0x0000,0x0000,0x0000,0x0000,0x0808,\r
+                                                                                 0x0404,0x0000,0x0000,0x0000,0x0000,\r
+                                                                                 0x0000,0x0000,0x0000,0x0000,0x0606,\r
+                                                                                 0x0000,0x0000,0x0000,0x0000,0x0000,\r
+                                                                                 0x0000};\r
+unsigned gnd_colors[NUMLEVELS]={0x0202,0x0202,0x0606,0x0202,0x0707,\r
+                                                                                 0x0505,0x0808,0x0606,0x0101,0x0808,\r
+                                                                                 0x0606,0x0404,0x0808,0x0c0c,0x0e0e,\r
+                                                                                 0x0808,0x0808,0x0c0c,0x0000,0x0707,\r
+                                                                                 0x0808};\r
+\r
+\r
+ControlInfo    control;\r
+boolean                running=false; //,slowturn;\r
+\r
+int                    bordertime;\r
+objtype objlist[MAXACTORS],*new,*obj,*player,*lastobj,*objfreelist;\r
+\r
+#if USE_INERT_LIST\r
+inertobjtype inertobjlist[MAXINERTOBJ],*inert;\r
+#endif\r
+\r
+unsigned       farmapylookup[MAPSIZE];\r
+byte           *nearmapylookup[MAPSIZE];\r
+\r
+boolean                singlestep,godmode;\r
+int                    extravbls;\r
+status_flags    status_flag;\r
+int             status_delay;\r
+\r
+//\r
+// replacing refresh manager\r
+//\r
+unsigned       mapwidth,mapheight,tics,realtics;\r
+boolean                compatability;\r
+byte           *updateptr;\r
+unsigned       mapwidthtable[64];\r
+unsigned       uwidthtable[UPDATEHIGH];\r
+unsigned       blockstarts[UPDATEWIDE*UPDATEHIGH];\r
+#define        UPDATESCREENSIZE        (UPDATEWIDE*PORTTILESHIGH+2)\r
+#define        UPDATESPARESIZE         (UPDATEWIDE*2+4)\r
+#define UPDATESIZE                     (UPDATESCREENSIZE+2*UPDATESPARESIZE)\r
+byte           update[UPDATESIZE];\r
+\r
+int            mousexmove,mouseymove;\r
+int            pointcount,pointsleft;\r
+\r
+short BeepTime = 0;\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+void CalcBounds (objtype *ob);\r
+void DrawPlayScreen (void);\r
+void PreFullDisplay(void);\r
+void PostFullDisplay(boolean draw_view);\r
+\r
+\r
+//\r
+// near data map array (wall values only, get text number from far data)\r
+//\r
+byte           tilemap[MAPSIZE][MAPSIZE];\r
+byte           spotvis[MAPSIZE][MAPSIZE];\r
+objtype                *actorat[MAPSIZE][MAPSIZE];\r
+\r
+objtype dummyobj;\r
+\r
+int bordertime;\r
+int    objectcount;\r
+\r
+void StopMusic(void);\r
+void StartMusic(void);\r
+\r
+void CalibrateJoystick(short joynum);\r
+\r
+//==========================================================================\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     CenterWindow() - Generates a window of a given width & height in the\r
+//             middle of the screen\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+\r
+#define MAXX   320\r
+#define MAXY   120\r
+\r
+void   CenterWindow(word w,word h)\r
+{\r
+       US_DrawWindow(((MAXX / 8) - w) / 2,((MAXY / 8) - h) / 2,w,h);\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= CheckKeys\r
+=\r
+=====================\r
+*/\r
+\r
+void CheckKeys (void)\r
+{\r
+       extern boolean autofire;\r
+\r
+       if (screenfaded)                        // don't do anything with a faded screen\r
+               return;\r
+\r
+#if 0\r
+//\r
+// pause key wierdness can't be checked as a scan code\r
+//\r
+       if (Paused)\r
+       {\r
+               CenterWindow (8,3);\r
+               US_PrintCentered ("PAUSED");\r
+               VW_UpdateScreen ();\r
+//             SD_MusicOff();\r
+               IN_Ack();\r
+//             SD_MusicOn();\r
+               Paused = false;\r
+               if (MousePresent) Mouse(MDelta);        // Clear accumulated mouse movement\r
+       }\r
+       else\r
+       if (Keyboard[sc_Enter])                 // P = pause with no screen disruptioon\r
+       {\r
+//             SD_MusicOff();\r
+               DisplaySMsg("PAUSED",NULL);\r
+               IN_Ack();\r
+//             SD_MusicOn();\r
+               if (MousePresent) Mouse(MDelta);        // Clear accumulated mouse movement\r
+       }\r
+       else\r
+       if (Keyboard[sc_S])\r
+       {\r
+               char *Text[] = {{"Slow Mode ON"},{"Slow Mode OFF"}};\r
+\r
+               SlowMode ^= 1;\r
+               extravbls = SlowMode << 3;\r
+               CenterWindow (8,3);\r
+               US_PrintCentered (Text[SlowMode]);\r
+               VW_UpdateScreen ();\r
+//             SD_MusicOff();\r
+               IN_Ack();\r
+//             SD_MusicOn();\r
+               if (MousePresent) Mouse(MDelta);        // Clear accumulated mouse movement\r
+       }\r
+#endif\r
+\r
+\r
+// F2 - SOUND OPTIONS\r
+//\r
+       if (Keyboard[sc_F2])\r
+       {\r
+               int height=7;\r
+               boolean ChoiceMade = false;\r
+\r
+               if (AdLibPresent)\r
+                       height++;\r
+\r
+               VW_FixRefreshBuffer();\r
+               CenterWindow(22,height);\r
+               US_Print( "\n        1 )  NO SOUND \n");\r
+               US_Print(   "        2 )  PC  AUDIO \n");\r
+\r
+               if (AdLibPresent)\r
+                       US_Print("        3 ) ADLIB AUDIO\n");\r
+\r
+               US_Print( "\n       ESC)    EXIT    ");\r
+               VW_UpdateScreen();\r
+\r
+               // Switch audio device ON/OFF & load sounds if there\r
+               // was a change in the device.\r
+\r
+               do {\r
+\r
+                       if (Keyboard[1])                                                                // ESC - Exit\r
+                               ChoiceMade = true;\r
+                       else\r
+                       if (Keyboard[2])                                                                // 1 - No Sound\r
+                       {\r
+                               SD_SetSoundMode(sdm_Off);\r
+                               ChoiceMade = true;\r
+                       }\r
+                       else\r
+                       if (Keyboard[3])                                                        // 2 - PC Audio\r
+                       {\r
+                               SD_SetSoundMode(sdm_PC);\r
+//                             if (oldsoundmode != sdm_PC)\r
+                                       CA_LoadAllSounds();\r
+                               ChoiceMade = true;\r
+                       }\r
+                       else\r
+                       if ((Keyboard[4]) &&    AdLibPresent)           // 3 - AdLib Audio\r
+                       {\r
+                               SD_SetSoundMode(sdm_AdLib);\r
+//                             if (oldsoundmode != sdm_AdLib)\r
+                                       CA_LoadAllSounds();\r
+                               ChoiceMade = true;\r
+                       }\r
+\r
+               } while (!ChoiceMade);\r
+               tics = realtics = 1;\r
+               IN_ClearKeysDown();\r
+       }\r
+\r
+// F5 - CALIBRATE JOYSTICK\r
+//\r
+       if (Keyboard[sc_F5])\r
+       {\r
+               CalibrateJoystick(0);\r
+               tics = realtics = 1;\r
+               IN_ClearKeysDown();\r
+       }\r
+\r
+deadloop:;\r
+// ESCAPE - quits game\r
+//\r
+       if ((Keyboard[sc_Escape]) || (Flags & FL_DEAD))\r
+       {\r
+               char ch;\r
+\r
+               DisplaySMsg("Options", NULL);\r
+               status_flag = S_NONE;\r
+\r
+\r
+               if (Flags & FL_DEAD)\r
+               {\r
+                       char choices[] = {sc_Escape,sc_R,sc_N,sc_Q,0};\r
+                       ch = DisplayMsg("Restore          New          Quit",choices);\r
+               }\r
+               else\r
+               {\r
+                       char choices[] = {sc_Escape,sc_S,sc_R,sc_N,sc_Q,0};\r
+                       ch = DisplayMsg("Save       Restore       New       Quit",choices);\r
+               }\r
+               DrawText(true);\r
+\r
+               switch (ch)\r
+               {\r
+                       case sc_S:\r
+                               if (!(Flags & FL_DEAD))\r
+                                       Keyboard[sc_F3] = true;\r
+                       break;\r
+\r
+                       case sc_R:\r
+                               Keyboard[sc_F4] = true;\r
+                       break;\r
+\r
+                       case sc_N:\r
+                               DisplaySMsg("Starting anew", NULL);\r
+                               VW_WaitVBL(60);\r
+                               playstate = ex_resetgame;\r
+                               Flags &= ~FL_DEAD;\r
+                       break;\r
+\r
+                       case sc_Q:\r
+                               DisplaySMsg("FARE THEE WELL!", NULL);\r
+                               VW_WaitVBL(120);\r
+                               if (!Flags & FL_QUICK)\r
+                                       VW_FadeOut();\r
+                               NormalScreen();\r
+                               FreeUpMemory();\r
+                               Quit(NULL);\r
+                       break;\r
+               }\r
+               tics = realtics = 1;\r
+       }\r
+\r
+// F1 - DISPLAY HELP\r
+//\r
+       if (Keyboard[sc_F1])\r
+       {\r
+               PrintHelp();\r
+\r
+#ifdef TEXT_PRESENTER\r
+\r
+               extern PresenterInfo MainHelpText;\r
+\r
+               VW_FadeOut();\r
+\r
+               FreeUpMemory();\r
+               if (!LoadPresenterScript("HELP.TXT",&MainHelpText))\r
+               {\r
+                       VW_FadeIn();\r
+                       CenterWindow(30,5);\r
+                       US_CPrint("\nError loading HELP file.\n");\r
+                       US_CPrint("Press any key.");\r
+                       IN_Ack();\r
+                       VW_FadeOut();\r
+               }\r
+               else\r
+               {\r
+                       VW_SetSplitScreen(200);\r
+                       bufferofs = displayofs = screenloc[0];\r
+                       VW_Bar(0,0,320,200,0);\r
+\r
+                       Display640();\r
+                       Presenter(&MainHelpText);\r
+                       Display320();\r
+               }\r
+               FreePresenterScript(&MainHelpText);\r
+#endif\r
+               VW_SetSplitScreen(120);\r
+               VW_SetScreen(screenloc[0],0);\r
+               screenpage = 0;\r
+               CacheScaleds();\r
+\r
+               bufferofs = 0;\r
+               RedrawStatusWindow();\r
+               ThreeDRefresh();\r
+               VW_FadeIn();\r
+               Keyboard[sc_F1] = false;\r
+               tics = realtics = 1;\r
+               IN_ClearKeysDown();\r
+       }\r
+\r
+// F3 - SAVE GAME\r
+//\r
+       if ((Keyboard[sc_F3]) && (!(Flags & FL_DEAD)))\r
+       {\r
+               PreFullDisplay();\r
+               GE_SaveGame();\r
+               PostFullDisplay(true);\r
+               tics = realtics = 1;\r
+               IN_ClearKeysDown();\r
+       }\r
+\r
+// F4 - LOAD GAME\r
+//\r
+       if (Keyboard[sc_F4])\r
+       {\r
+               PreFullDisplay();\r
+               if (GE_LoadGame())\r
+               {\r
+                       loadedgame = true;\r
+                       playstate = ex_loadedgame;\r
+                       Flags &= ~FL_DEAD;\r
+                       lasttext = -1;\r
+                       PostFullDisplay(false);\r
+               }\r
+               else\r
+               if (playstate == ex_victorious)\r
+               {\r
+                       PostFullDisplay(false);\r
+                       Victory(false);\r
+                       IN_Ack();\r
+//                     gamestate.mapon++;\r
+               }\r
+               else\r
+                       PostFullDisplay(true);\r
+               Keyboard[sc_F5] = false;\r
+               tics = realtics = 1;\r
+               IN_ClearKeysDown();\r
+       }\r
+\r
+       if (Flags & FL_DEAD)\r
+               goto deadloop;\r
+\r
+//\r
+// F10-? debug keys\r
+//\r
+       if (Keyboard[sc_BackSpace])\r
+       {\r
+               DebugKeys();\r
+               if (MousePresent) Mouse(MDelta);        // Clear accumulated mouse movement\r
+               lasttimecount = TimeCount;\r
+       }\r
+}\r
+\r
+//-------------------------------------------------------------------------\r
+// PreFullDisplay()\r
+//-------------------------------------------------------------------------\r
+void PreFullDisplay()\r
+{\r
+       VW_FadeOut();\r
+       VW_SetSplitScreen(200);\r
+       bufferofs = displayofs = screenloc[0];\r
+       VW_Bar(0,0,320,200,0);\r
+}\r
+\r
+//-------------------------------------------------------------------------\r
+// PostFullDisplay()\r
+//-------------------------------------------------------------------------\r
+void PostFullDisplay(boolean draw_view)\r
+{\r
+       VW_SetSplitScreen(120);\r
+       bufferofs = 0;\r
+       RedrawStatusWindow();\r
+       if (draw_view)\r
+       {\r
+               ThreeDRefresh();\r
+               VW_FadeIn();\r
+       }\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+#############################################################################\r
+\r
+                                 The objlist data structure\r
+\r
+#############################################################################\r
+\r
+objlist containt structures for every actor currently playing.  The structure\r
+is accessed as a linked list starting at *player, ending when ob->next ==\r
+NULL.  GetNewObj inserts a new object at the end of the list, meaning that\r
+if an actor spawn another actor, the new one WILL get to think and react the\r
+same frame.  RemoveObj unlinks the given object and returns it to the free\r
+list, but does not damage the objects ->next pointer, so if the current object\r
+removes itself, a linked list following loop can still safely get to the\r
+next element.\r
+\r
+<backwardly linked free list>\r
+\r
+#############################################################################\r
+*/\r
+\r
+\r
+/*\r
+=========================\r
+=\r
+= InitObjList\r
+=\r
+= Call to clear out the entire object list, returning them all to the free\r
+= list.  Allocates a special spot for the player.\r
+=\r
+=========================\r
+*/\r
+\r
+void InitObjList (void)\r
+{\r
+       int     i;\r
+\r
+       for (i=0;i<MAXACTORS;i++)\r
+       {\r
+               objlist[i].prev = &objlist[i+1];\r
+               objlist[i].next = NULL;\r
+       }\r
+\r
+       objlist[MAXACTORS-1].prev = NULL;\r
+\r
+       objfreelist = &objlist[0];\r
+       lastobj = NULL;\r
+\r
+       objectcount = 0;\r
+\r
+//\r
+// give the player and score the first free spots\r
+//\r
+       GetNewObj (false);\r
+       player = new;\r
+\r
+#if USE_INERT_LIST\r
+       inert = inertobjlist;\r
+#endif\r
+\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=========================\r
+=\r
+= GetNewObj\r
+=\r
+= Sets the global variable new to point to a free spot in objlist.\r
+= The free spot is inserted at the end of the liked list\r
+=\r
+= When the object list is full, the caller can either have it bomb out ot\r
+= return a dummy object pointer that will never get used\r
+=\r
+=========================\r
+*/\r
+\r
+void GetNewObj (boolean usedummy)\r
+{\r
+       if (!objfreelist)\r
+       {\r
+               if (usedummy)\r
+               {\r
+                       new = &dummyobj;\r
+                       return;\r
+               }\r
+               Quit ("GetNewObj: No free spots in objlist!");\r
+       }\r
+\r
+       new = objfreelist;\r
+       objfreelist = new->prev;\r
+       memset (new,0,sizeof(*new));\r
+\r
+       if (lastobj)\r
+               lastobj->next = new;\r
+       new->prev = lastobj;    // new->next is allready NULL from memset\r
+\r
+       new->active = false;\r
+       lastobj = new;\r
+\r
+       objectcount++;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=========================\r
+=\r
+= RemoveObj\r
+=\r
+= Add the given object back into the free list, and unlink it from it's\r
+= neighbors\r
+=\r
+=========================\r
+*/\r
+\r
+void RemoveObj (objtype *gone)\r
+{\r
+       objtype **spotat;\r
+\r
+       if (gone == player)\r
+               Quit ("RemoveObj: Tried to remove the player!");\r
+\r
+//\r
+// fix the next object's back link\r
+//\r
+       if (gone == lastobj)\r
+               lastobj = (objtype *)gone->prev;\r
+       else\r
+               gone->next->prev = gone->prev;\r
+\r
+//\r
+// fix the previous object's forward link\r
+//\r
+       gone->prev->next = gone->next;\r
+\r
+//\r
+// add it back in to the free list\r
+//\r
+       gone->prev = objfreelist;\r
+       objfreelist = gone;\r
+\r
+       objectcount--;\r
+}\r
+\r
+#if USE_INERT_LIST\r
+\r
+//--------------------------------------------------------------------------\r
+// MoveObjToInert()\r
+//--------------------------------------------------------------------------\r
+void MoveObjToInert(objtype *obj)\r
+{\r
+\r
+       if (inert == &inertobjlist[MAXINERTOBJ])\r
+               return;\r
+\r
+// Transfer info needed by inert objtype\r
+//\r
+       inert->x = obj->x;\r
+       inert->y = obj->y;\r
+       inert->size = obj->size;\r
+       inert->viewx = obj->viewx;\r
+       inert->tilex = obj->tilex;\r
+       inert->tiley = obj->tiley;\r
+       inert->state = obj->state;\r
+       inert->ticcount = obj->ticcount;\r
+\r
+// Setup links between inert objects\r
+//\r
+       if (inert != inertobjlist)\r
+               (inert-1)->next = inert;\r
+       inert->next = NULL;\r
+       inert++;\r
+\r
+// Free 'real' object from list.\r
+//\r
+       RemoveObj(obj);\r
+}\r
+\r
+#endif\r
+\r
+//==========================================================================\r
+\r
+/*\r
+===================\r
+=\r
+= PollControls\r
+=\r
+===================\r
+*/\r
+\r
+void PollControls (void)\r
+{\r
+       unsigned buttons;\r
+\r
+       IN_ReadControl(0,&control);\r
+\r
+       if (MousePresent)\r
+       {\r
+               Mouse(MButtons);\r
+               buttons = _BX;\r
+               Mouse(MDelta);\r
+               mousexmove = _CX;\r
+               mouseymove = _DX;\r
+\r
+               if (buttons&1)\r
+                       control.button0 = 1;\r
+               if (buttons&2)\r
+                       control.button1 = 1;\r
+\r
+       }\r
+\r
+       if (Keyboard[sc_V] || Keyboard[sc_Tab])\r
+               running = true;\r
+       else\r
+               running = false;\r
+}\r
+\r
+//==========================================================================\r
+\r
+#if 0\r
+/*\r
+=================\r
+=\r
+= StopMusic\r
+=\r
+=================\r
+*/\r
+\r
+void StopMusic(void)\r
+{\r
+       int     i;\r
+\r
+       SD_MusicOff();\r
+       for (i = 0;i < LASTMUSIC;i++)\r
+               if (audiosegs[STARTMUSIC + i])\r
+               {\r
+                       MM_SetPurge(&((memptr)audiosegs[STARTMUSIC + i]),3);\r
+                       MM_SetLock(&((memptr)audiosegs[STARTMUSIC + i]),false);\r
+               }\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= StartMusic\r
+=\r
+=================\r
+*/\r
+\r
+// JAB - Cache & start the appropriate music for this level\r
+void StartMusic(void)\r
+{\r
+       musicnames      chunk;\r
+\r
+       SD_MusicOff();\r
+       chunk = TOOHOT_MUS;\r
+//     if ((chunk == -1) || (MusicMode != smm_AdLib))\r
+//DEBUG control panel          return;\r
+\r
+       MM_BombOnError (false);\r
+       CA_CacheAudioChunk(STARTMUSIC + chunk);\r
+       MM_BombOnError (true);\r
+       if (mmerror)\r
+               mmerror = false;\r
+       else\r
+       {\r
+               MM_SetLock(&((memptr)audiosegs[STARTMUSIC + chunk]),true);\r
+               SD_StartMusic((MusicGroup far *)audiosegs[STARTMUSIC + chunk]);\r
+       }\r
+}\r
+#endif\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= PlayLoop\r
+=\r
+===================\r
+*/\r
+\r
+void PlayLoop (void)\r
+{\r
+       char shot_color[3] = {4,9,14};\r
+\r
+       int allgems[5]={GEM_DELAY_TIME,         // used for Q & D comparison\r
+                                                GEM_DELAY_TIME,                // for having all gems...\r
+                                                GEM_DELAY_TIME,                // the "allgems" declaration MUST\r
+                                                GEM_DELAY_TIME,                // match the "gems" declaration in\r
+                                                GEM_DELAY_TIME         // the gametype structure!\r
+                                               };\r
+\r
+//     int originx=0;\r
+//     int i=100;\r
+       signed long dx,dy,radius,psin,pcos,newx,newy;\r
+       int             give;\r
+       short objnum;\r
+       signed long ox,oy,xl,xh,yl,yh,px,py,norm_dx,norm_dy;\r
+       short o_radius;\r
+\r
+       void (*think)();\r
+\r
+       ingame = true;\r
+       playstate = TimeCount = 0;\r
+       gamestate.shotpower = handheight = 0;\r
+       pointcount = pointsleft = 0;\r
+\r
+       status_flag = S_NONE;\r
+\r
+#if 0\r
+       // setup sky/ground colors and effects (based on level)\r
+       //\r
+       switch (gamestate.mapon)\r
+       {\r
+               case 255:\r
+                       if (!(BGFLAGS & BGF_NIGHT))\r
+                       {\r
+                               InitBgChange(3*60,sky_daytonight,-1,NULL,BGF_NIGHT);\r
+                               groundcolor = &gnd_colors[0];\r
+                       }\r
+                       else\r
+                       {\r
+                               skycolor = &sky_colors[0];\r
+                               groundcolor = &gnd_colors[0];\r
+                       }\r
+               break;\r
+\r
+               default:\r
+                       skycolor = &sky_colors[gamestate.mapon];\r
+                       groundcolor = &gnd_colors[gamestate.mapon];\r
+                       skytimer = groundtimer = -1;\r
+               break;\r
+       }\r
+#endif\r
+\r
+       BGFLAGS |= BGF_NOT_LIGHTNING;\r
+       skytimer = groundtimer = -1;\r
+\r
+       debug_gnd = *groundcolor;\r
+       debug_sky = *skycolor;\r
+       RedrawStatusWindow();\r
+       ThreeDRefresh();\r
+       if (screenfaded)\r
+               VW_FadeIn();\r
+\r
+#ifndef PROFILE\r
+       fizzlein = true;                                // fizzle fade in the first refresh\r
+#endif\r
+       TimeCount = lasttimecount = lastnuke = 0;\r
+\r
+       PollControls ();                                // center mouse\r
+//     StartMusic ();\r
+       do\r
+       {\r
+#ifndef PROFILE\r
+               PollControls();\r
+#else\r
+               control.xaxis = 1;\r
+               if (++TimeCount == 300)\r
+                       return;\r
+#endif\r
+               DisplayStatus(&status_flag);\r
+\r
+               objnum=0;\r
+               for (obj = player;obj;obj = obj->next)\r
+               {\r
+                       if ((obj->active >= yes) && (!(FreezeTime && (obj!=player))))\r
+                       {\r
+                               if (obj->ticcount)\r
+                               {\r
+                                       obj->ticcount-=realtics;\r
+                                       while ( obj->ticcount <= 0)\r
+                                       {\r
+                                               think = obj->state->think;\r
+                                               if (think)\r
+                                               {\r
+                                                       statetype *oldstate=obj->state;\r
+\r
+                                                       think (obj);\r
+                                                       if (!obj->state)\r
+                                                       {\r
+                                                               RemoveObj (obj);\r
+                                                               goto nextactor;\r
+                                                       }\r
+                                                       if (obj->state != oldstate)\r
+                                                               break;\r
+                                               }\r
+\r
+                                               obj->state = obj->state->next;\r
+                                               if (!obj->state)\r
+                                               {\r
+                                                       RemoveObj (obj);\r
+                                                       goto nextactor;\r
+                                               }\r
+                                               if (!obj->state->tictime)\r
+                                               {\r
+                                                       obj->ticcount = 0;\r
+                                                       goto nextactor;\r
+                                               }\r
+                                               if (obj->state->tictime>0)\r
+                                                       obj->ticcount += obj->state->tictime;\r
+                                       }\r
+                               }\r
+\r
+                               think = obj->state->think;\r
+                               if (think)\r
+                               {\r
+                                       think (obj);\r
+                                       if (!obj->state)\r
+                                               RemoveObj (obj);\r
+                               }\r
+nextactor:;\r
+                       }\r
+\r
+                       // keep a list of objects around the player for radar updates\r
+                       //\r
+                               if (obj == player)\r
+                               {\r
+                                       px = player->x;\r
+                                       py = player->y;\r
+                                       psin = sintable[player->angle];\r
+                                       pcos = costable[player->angle];\r
+                                       xl = px-((long)RADAR_WIDTH<<TILESHIFT)/2;\r
+                                       xh = px+((long)RADAR_WIDTH<<TILESHIFT)/2-1;\r
+                                       yl = py-((long)RADAR_HEIGHT<<TILESHIFT)/2;\r
+                                       yh = py+((long)RADAR_HEIGHT<<TILESHIFT)/2;\r
+                               }\r
+\r
+                               if (objnum > MAX_RADAR_BLIPS-2)\r
+                                       objnum = MAX_RADAR_BLIPS-2;\r
+\r
+                               ox = obj->x;\r
+                               oy = obj->y;\r
+\r
+\r
+                               if ((ox >= xl) && (ox <= xh) && (oy >= yl) && (oy <= yh))\r
+                               {\r
+                                       norm_dx = (dx = px-ox)>>TILESHIFT;\r
+                                       norm_dy = (dy = oy-py)>>TILESHIFT;\r
+\r
+                                       o_radius = IntSqrt((norm_dx * norm_dx) + (norm_dy * norm_dy));\r
+\r
+                                       if (o_radius < RADAR_RADIUS)\r
+                                       {\r
+                                               newx = FixedByFrac(dy,pcos)-FixedByFrac(dx,psin);\r
+                                               newy = FixedByFrac(dy,psin)+FixedByFrac(dx,pcos);\r
+\r
+                                               RadarXY[objnum][0]=newx>>TILESHIFT;\r
+                                               RadarXY[objnum][1]=newy>>TILESHIFT;\r
+\r
+                                               // Define color to use for this object...\r
+                                               //\r
+\r
+                                               switch (obj->obclass)\r
+                                               {\r
+                       // NO GEM NEEDED\r
+                       //\r
+                                       // THE WIZARD! (YOU)\r
+                                       //\r
+                                                       case playerobj:\r
+                                                               RadarXY[objnum++][2]=15;\r
+                                                       break;\r
+\r
+                                       // WIZARD'S SHOTS\r
+                                       //\r
+                                                       case pshotobj:\r
+                                                       case bigpshotobj:\r
+                                                               RadarXY[objnum++][2]=shot_color[screenpage];\r
+                                                       break;\r
+\r
+                                       // BATS                                                         (DK GRAY)\r
+                                       //\r
+                                                       case batobj:\r
+                                                               if (obj->active == always)\r
+                                                                       RadarXY[objnum++][2]=8;\r
+                                                       break;\r
+\r
+                                       // RABBITS                                                      (LT GRAY)\r
+                                       //\r
+                                                       case bunnyobj:\r
+                                                               if (obj->active == always)\r
+                                                                       RadarXY[objnum++][2]=7;\r
+                                                       break;\r
+\r
+                       // RED GEM\r
+                       //\r
+                                       // EYE, RED DEMON                                               (DK RED)\r
+                                       //\r
+                                                       case eyeobj:\r
+                                                       case reddemonobj:\r
+                                                               if (gamestate.gems[B_RGEM-B_RGEM])\r
+                                                                       if (obj->active == always)\r
+                                                                               RadarXY[objnum++][2]=4;\r
+                                                       break;\r
+\r
+                                       // RED MAGE                                                     (LT RED)\r
+                                       //\r
+                                                       case mageobj:\r
+                                                               if (gamestate.gems[B_RGEM-B_RGEM])\r
+                                                                       if (obj->active == always)\r
+                                                                               RadarXY[objnum++][2]=12;\r
+                                                       break;\r
+\r
+                       // BLUE GEM\r
+                       //\r
+                                       // SUCCUBUS                                                     (LT BLUE)\r
+                                       //\r
+                                                       case succubusobj:\r
+                                                               if (gamestate.gems[B_BGEM-B_RGEM])\r
+                                                                       if (obj->active == always)\r
+                                                                               RadarXY[objnum++][2]=9;\r
+                                                       break;\r
+\r
+                                       // WATER DRAGON                                                 (DK BLUE)\r
+                                       //\r
+                                                       case wetobj:\r
+                                                               if (gamestate.gems[B_GGEM-B_RGEM])\r
+                                                                       if (obj->active == always)\r
+                                                                               RadarXY[objnum++][2]=1;\r
+                                                       break;\r
+\r
+\r
+\r
+                       // GREEN GEM\r
+                       //\r
+                                       // GREEN TROLL                                                  (LT GREEN)\r
+                                       //\r
+                                                       case fatdemonobj:\r
+                                                               if (gamestate.gems[B_GGEM-B_RGEM])\r
+                                                                       if (obj->active == always)\r
+                                                                               RadarXY[objnum++][2]=10;\r
+                                                       break;\r
+\r
+                                       // GODESS                                                       (DK GREEN)\r
+                                       //\r
+                                                       case godessobj:\r
+                                                               if (gamestate.gems[B_GGEM-B_RGEM])\r
+                                                                       if (obj->active == always)\r
+                                                                               RadarXY[objnum++][2]=2;\r
+                                                       break;\r
+\r
+                       // YELLOW GEM\r
+                       //\r
+                                       // ANT                                                          (BROWN)\r
+                                       //\r
+                                                       case antobj:\r
+                                                       case treeobj:\r
+                                                               if (gamestate.gems[B_YGEM-B_RGEM])\r
+                                                                       if (obj->active == always)\r
+                                                                               RadarXY[objnum++][2]=6;\r
+                                                       break;\r
+\r
+                                       // SKELETON                                                     (YELLOW)\r
+                                       //\r
+                                                       case skeletonobj:\r
+                                                               if (gamestate.gems[B_YGEM-B_RGEM])\r
+                                                                       if (obj->active == always)\r
+                                                                               RadarXY[objnum++][2]=14;\r
+                                                       break;\r
+\r
+                       // PURPLE GEM\r
+                       //\r
+                                       // ZOMBIE\r
+                                       //\r
+                                                       case zombieobj:\r
+                                                               if (gamestate.gems[B_PGEM-B_RGEM])\r
+                                                                       if (obj->active == always)\r
+                                                                               RadarXY[objnum++][2]=13;\r
+                                                       break;\r
+\r
+                       // ALL GEMS NEEDED\r
+                       //\r
+                                       // NEMESIS\r
+                                       //\r
+                                                       case grelmobj:\r
+                                                               if (!memcmp(gamestate.gems,allgems,sizeof(gamestate.gems)))\r
+                                                                       if (obj->active == always)\r
+                                                                               RadarXY[objnum++][2]=15;\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                               }\r
+               }\r
+               RadarXY[objnum][2]=-1;          // Signals end of RadarXY list...\r
+\r
+#if USE_INERT_LIST\r
+               if (inert != inertobjlist)\r
+                       for (obj=(objtype *)inertobjlist;obj;obj=obj->next)\r
+                               if (obj->ticcount)\r
+                               {\r
+                                       obj->ticcount-=realtics;\r
+                                       while ( obj->ticcount <= 0)\r
+                                       {\r
+                                               obj->state = obj->state->next;\r
+                                               if (!obj->state)\r
+                                                       Quit("Removable object in INERT list.");\r
+\r
+                                               if (!obj->state->tictime)\r
+                                               {\r
+                                                       obj->ticcount = 0;\r
+                                                       goto nextactor;\r
+                                               }\r
+\r
+                                               if (obj->state->tictime>0)\r
+                                                       obj->ticcount += obj->state->tictime;\r
+                                       }\r
+                               }\r
+#endif\r
+\r
+               if (bordertime)\r
+               {\r
+                       bordertime -= realtics;\r
+                       if (bordertime<=0)\r
+                       {\r
+                               bordertime = 0;\r
+                               VW_ColorBorder(0);\r
+                       }\r
+               }\r
+\r
+#if 1\r
+// random lightning?\r
+//\r
+       if (BGFLAGS & (BGF_NOT_LIGHTNING))\r
+       {\r
+               if ((scolor & 0xe0) && (!(random(20-realtics))))\r
+               {\r
+                       BGFLAGS &= ~BGF_NOT_LIGHTNING;\r
+                       InitBgChange(1,sky_lightning,-1,NULL,BGF_NOT_LIGHTNING);\r
+               }\r
+       }\r
+\r
+// handle sky/ground color changes\r
+//\r
+               if (skytimer != -1)\r
+               {\r
+                       skytimer -= realtics;\r
+                       if (skytimer < 0)\r
+                       {\r
+                               skycolor++;\r
+                               if (*skycolor == 0xffff)\r
+                               {\r
+                                       skytimer = -1;\r
+//                                     skycolor--;\r
+                                       skycolor = &scolor;\r
+                                       if (groundtimer == -1)\r
+                                               BGFLAGS |= bgflag;\r
+                               }\r
+                               else\r
+                                       skytimer = skytimer_reset;\r
+                       }\r
+               }\r
+\r
+               if (groundtimer != -1)\r
+               {\r
+                       groundtimer -= realtics;\r
+                       if (groundtimer < 0)\r
+                       {\r
+                               groundcolor++;\r
+                               if (*groundcolor == 0xffff)\r
+                               {\r
+                                       groundtimer = -1;\r
+//                                     groundcolor--;\r
+                                       groundcolor = &gcolor;\r
+                                       if (skytimer == -1)\r
+                                               BGFLAGS |= bgflag;\r
+                               }\r
+                               else\r
+                                       groundtimer = groundtimer_reset;\r
+                       }\r
+               }\r
+#endif\r
+\r
+\r
+//\r
+//             Handle FreezeTime counter..\r
+//\r
+               if (FreezeTime)\r
+               {\r
+                       if (FreezeTime<20*30)\r
+                               if ((BeepTime+=realtics)>=60)\r
+                               {\r
+                                       BeepTime -= 60;\r
+                                       SD_PlaySound(TICKSND);\r
+                               }\r
+\r
+                       if ((FreezeTime-=realtics)<=0)\r
+                       {\r
+                               FreezeTime=0;\r
+                               SD_PlaySound(TIMERETURNSND);\r
+                               DisplaySMsg(NULL,NULL);\r
+                               status_flag = S_NONE;\r
+                       }\r
+               }\r
+\r
+\r
+// refresh all\r
+//\r
+               ThreeDRefresh ();\r
+\r
+               if (Flags & FL_DEAD)\r
+               {\r
+                       SD_PlaySound (GAMEOVERSND);\r
+                       DisplaySMsg("DEAD",NULL);\r
+                       DrawHealth();\r
+                       if (gamestate.potions)\r
+                       {\r
+                                bufferofs = displayofs = screenloc[screenpage];\r
+                                CenterWindow(35,3);\r
+                                US_CPrint("\nYou should use your Cure Potions wisely\n");\r
+                                IN_Ack();\r
+                       }\r
+               }\r
+\r
+// check for win\r
+//\r
+               if (playstate == ex_victorious)\r
+               {\r
+                       Victory(true);\r
+//                     Flags |= FL_DEAD;\r
+                       IN_Ack();\r
+//                     gamestate.mapon++;\r
+               }\r
+\r
+               CheckKeys();\r
+\r
+       }while (!playstate);\r
+//     StopMusic ();\r
+\r
+       ingame = false;\r
+       if (bordertime)\r
+       {\r
+               bordertime = 0;\r
+               VW_ColorBorder(0);\r
+       }\r
+\r
+       if (abortgame)\r
+               abortgame = false;\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// IntSqrt() - by Master Programmer, George Leritte!\r
+//--------------------------------------------------------------------------\r
+int IntSqrt(long va)\r
+{\r
+asm     mov     AX, word ptr va\r
+asm     mov     DX, word ptr va+2\r
+asm     mov     bx,dx           // {bx = integer square root of dx:ax}\r
+asm     or      bx,ax           // {if dx:ax=0 then return}\r
+asm     jz      isq01\r
+asm     mov     bx,dx\r
+asm     shl     bx,1\r
+asm     or      bl,ah\r
+asm     or      bl,al\r
+asm     dec     bx\r
+asm     add     bx,dx           // { initial guess}\r
+asm     jg      isq10\r
+asm     inc     bx              // { don't return zero}\r
+asm     jg      isq10\r
+asm     mov     bx,7fffh\r
+isq01:;\r
+                 goto    exitrout;\r
+\r
+isq10:;\r
+asm     push    ax\r
+asm     push    dx\r
+asm     div     bx\r
+asm     sub     ax,bx\r
+asm     cmp     ax,1\r
+asm     jbe     isq90\r
+asm     cmp     ax,-1\r
+asm     jae     isq90\r
+asm     sar     ax,1\r
+asm     add     bx,ax\r
+asm     pop     dx\r
+asm     pop     ax\r
+asm     jmp     isq10\r
+isq90:;\r
+asm     pop     dx\r
+asm     pop     ax\r
+exitrout:;\r
+asm     mov     ax,bx\r
+}\r
+\r
+//-------------------------------------------------------------------------\r
+// InitBgChange()\r
+//-------------------------------------------------------------------------\r
+void InitBgChange(short stimer, unsigned *scolors, short gtimer, unsigned *gcolors, byte flag)\r
+{\r
+       skytimer_reset = skytimer = stimer;\r
+       if (scolors)\r
+               skycolor = scolors;\r
+\r
+       groundtimer_reset = groundtimer = gtimer;\r
+       if (gcolors)\r
+               groundcolor = gcolors;\r
+\r
+       bgflag = flag;\r
+}\r
+\r
+////////////////////////////////////////////////////////\r
+//\r
+// DisplayStatus\r
+//\r
+//  Stat_Flag -  contains the type of status displayed\r
+//  -- also uses status_delay (global variable) will not\r
+//     change display until this variable is zero.\r
+//  -- heirarchy is determined by the series of if statements,\r
+//        to change it, rearrange th if statements.\r
+//\r
+////////////////////////////////////////////////////////\r
+\r
+#define MESSAGEDELAY  25\r
+void DisplayStatus (status_flags *stat_flag)\r
+{\r
+       status_flags temp_status;\r
+\r
+\r
+       if (*stat_flag == S_TIMESTOP)\r
+         return;\r
+\r
+       if (status_delay > 0)\r
+       {\r
+               status_delay -= realtics;\r
+               return;\r
+       }\r
+       else\r
+               status_delay = 0;\r
+\r
+       // check for a change in status from previous call\r
+\r
+       temp_status = S_VIEWING;                             //precaution\r
+\r
+       if (Keyboard[sc_Control] || control.button0)\r
+               temp_status = S_MISSLE;\r
+\r
+       if (Keyboard[sc_Z] && !Keyboard[sc_F10])\r
+               temp_status = S_ZAPPER;\r
+\r
+       if ((Keyboard[sc_X] && !Keyboard[sc_F10]) || Keyboard[sc_Enter])\r
+               temp_status = S_XTER;\r
+\r
+       if (control.x)\r
+               temp_status = S_TURN;\r
+\r
+       if ((Keyboard[sc_V] || Keyboard[sc_Tab]) && control.x)\r
+               temp_status = S_QTURN;\r
+\r
+       if (Keyboard[sc_Alt] && control.x)\r
+               temp_status = S_SIDESTEP;\r
+\r
+       if (control.y < 0)\r
+               temp_status = S_ADVANCE;\r
+\r
+       if (control.y > 0)\r
+               temp_status = S_RETREAT;\r
+\r
+       if (Keyboard[sc_F5])\r
+               temp_status = S_JOYSTICK;\r
+\r
+       if (Keyboard[sc_F4])\r
+               temp_status = S_RESTORING;\r
+\r
+       if (Keyboard[sc_F3])\r
+               temp_status = S_SAVING;\r
+\r
+       if (Keyboard[sc_F2])\r
+               temp_status = S_SND;\r
+\r
+       if (Keyboard[sc_F1])\r
+               temp_status = S_HELP;\r
+\r
+       if (temp_status != *stat_flag)\r
+       {\r
+               *stat_flag = temp_status;\r
+\r
+\r
+               switch (*stat_flag)\r
+               {\r
+                       case S_MISSLE:\r
+                               DisplaySMsg("Magick Missile", NULL);\r
+                               status_delay = MESSAGEDELAY;\r
+                       break;\r
+\r
+                       case S_ZAPPER:\r
+                               if (gamestate.bolts)\r
+                               {\r
+                                       DisplaySMsg("Zapper", NULL);\r
+                                       status_delay = MESSAGEDELAY+10;\r
+                               }\r
+                       break;\r
+\r
+                       case S_XTER:\r
+                               if (gamestate.nukes)\r
+                               {\r
+                                       DisplaySMsg("Xterminator", NULL);\r
+                                       status_delay = MESSAGEDELAY+5;\r
+                               }\r
+                       break;\r
+\r
+                       case S_TURN:\r
+                               DisplaySMsg("Turning", NULL);\r
+                               status_delay = MESSAGEDELAY;\r
+                       break;\r
+\r
+                       case S_QTURN:\r
+                               DisplaySMsg("Quick Turning", NULL);\r
+                               status_delay = MESSAGEDELAY;\r
+                       break;\r
+\r
+                       case S_SIDESTEP:\r
+                               DisplaySMsg("Sidestepping", NULL);\r
+                               status_delay = MESSAGEDELAY;\r
+                       break;\r
+\r
+                       case S_ADVANCE:\r
+                               DisplaySMsg("Advancing", NULL);\r
+                               status_delay = MESSAGEDELAY;\r
+                       break;\r
+\r
+                       case S_RETREAT:\r
+                               DisplaySMsg("Retreating", NULL);\r
+                               status_delay = MESSAGEDELAY;\r
+                       break;\r
+\r
+                       case S_JOYSTICK:\r
+                               DisplaySMsg("Adjusting Joystick", NULL);\r
+                       break;\r
+\r
+                       case S_RESTORING:\r
+                               DisplaySMsg("Restoring", NULL);\r
+                       break;\r
+\r
+                       case S_SAVING:\r
+                               DisplaySMsg("Saving", NULL);\r
+                       break;\r
+\r
+                       case S_SND:\r
+                               DisplaySMsg("Select Sound", NULL);\r
+                       break;\r
+\r
+                       case S_HELP:\r
+                               DisplaySMsg("Getting Help", NULL);\r
+                       break;\r
+\r
+                       case S_VIEWING:\r
+                               DisplaySMsg("Viewing", NULL);\r
+                       break;\r
+               }\r
+               bufferofs = displayofs = screenloc[screenpage];\r
+\r
+       }\r
+}\r
diff --git a/16/cawat/C5_SCALE.C b/16/cawat/C5_SCALE.C
new file mode 100644 (file)
index 0000000..fee4dbe
--- /dev/null
@@ -0,0 +1,697 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// C3_SCALE.C\r
+\r
+#include "DEF.H"\r
+#pragma hdrstop\r
+\r
+//const        unsigned        viewheight = 144;\r
+const  unsigned        screenbwide = 40;\r
+const  byte            BACKGROUNDPIX   =   5;\r
+\r
+unsigned               shapesize[NUMSCALEPICS];\r
+t_compscale _seg *scaledirectory[NUMSCALEPICS];\r
+t_compshape _seg *shapedirectory[NUMSCALEPICS];\r
+memptr                 walldirectory[NUMSCALEWALLS];\r
+\r
+/*\r
+===========================\r
+=\r
+= DeplanePic\r
+=\r
+= Takes a raw bit map of width bytes by height and creates a scaleable shape\r
+=\r
+= Returns the length of the shape in bytes\r
+=\r
+= Fills in spotvis (a convenient 64*64 array) with the color values\r
+=\r
+===========================\r
+*/\r
+\r
+void DeplanePic (int picnum)\r
+{\r
+       byte            far *plane0,far *plane1,far *plane2,far *plane3;\r
+       byte            by0,by1,by2,by3;\r
+       unsigned        x,y,b,color,shift,width,height;\r
+       byte            *dest;\r
+\r
+//\r
+// convert ega pixels to byte color values in a temp buffer\r
+//\r
+       width = pictable[picnum-STARTPICS].width;\r
+       height = pictable[picnum-STARTPICS].height;\r
+\r
+       if (width>8 || height!=64)\r
+               Quit ("DePlanePic: Bad size shape");\r
+\r
+       memset (spotvis,BACKGROUNDPIX,sizeof(spotvis));\r
+\r
+       plane0 = (byte _seg *)grsegs[picnum];\r
+       plane1 = plane0 + width*height;\r
+       plane2 = plane1 + width*height;\r
+       plane3 = plane2 + width*height;\r
+\r
+       for (y=0;y<height;y++)\r
+       {\r
+               dest = &spotvis[y][0];\r
+               for (x=0;x<width;x++)\r
+               {\r
+                       by0 = *plane0++;\r
+                       by1 = *plane1++;\r
+                       by2 = *plane2++;\r
+                       by3 = *plane3++;\r
+\r
+                       for (b=0;b<8;b++)\r
+                       {\r
+                               shift=8-b;\r
+\r
+                               color = 0;\r
+                               asm     mov     cl,[BYTE PTR shift]\r
+                               asm     mov     al,[BYTE PTR by3]\r
+                               asm     rcr     al,cl;\r
+                               asm     rcl     [BYTE PTR color],1;\r
+\r
+                               asm     mov     cl,[BYTE PTR shift]\r
+                               asm     mov     al,[BYTE PTR by2]\r
+                               asm     rcr     al,cl;\r
+                               asm     rcl     [BYTE PTR color],1;\r
+\r
+                               asm     mov     cl,[BYTE PTR shift]\r
+                               asm     mov     al,[BYTE PTR by1]\r
+                               asm     rcr     al,cl;\r
+                               asm     rcl     [BYTE PTR color],1;\r
+\r
+                               asm     mov     cl,[BYTE PTR shift]\r
+                               asm     mov     al,[BYTE PTR by0]\r
+                               asm     rcr     al,cl;\r
+                               asm     rcl     [BYTE PTR color],1;\r
+\r
+                               *dest++ = color;\r
+                       }       // B\r
+               }               // X\r
+       }                       // Y\r
+}\r
+\r
+\r
+\r
+\r
+/*\r
+========================\r
+=\r
+= BuildCompScale\r
+=\r
+= Builds a compiled scaler object that will scale a 64 tall object to\r
+= the given height (centered vertically on the screen)\r
+=\r
+= height should be even\r
+=\r
+= Call with\r
+= ---------\r
+= DS:SI                Source for scale\r
+= ES:DI                Dest for scale\r
+=\r
+= Calling the compiled scaler only destroys AL\r
+=\r
+========================\r
+*/\r
+\r
+unsigned BuildCompScale (int height, memptr *finalspot)\r
+{\r
+       t_compscale     _seg *work;\r
+       byte            far *code;\r
+\r
+       int                     i;\r
+       long            fix,step;\r
+       unsigned        src,totalscaled,totalsize;\r
+       int                     startpix,endpix,toppix;\r
+\r
+\r
+       MM_GetPtr (&(memptr)work,20000);\r
+\r
+       step = ((long)height<<16) / 64;\r
+       code = &work->code[0];\r
+       toppix = (viewheight-height)/2;\r
+       fix = 0;\r
+\r
+       for (src=0;src<=64;src++)\r
+       {\r
+               startpix = fix>>16;\r
+               fix += step;\r
+               endpix = fix>>16;\r
+\r
+               work->start[src] = startpix;\r
+               if (endpix>startpix)\r
+                       work->width[src] = endpix-startpix;\r
+               else\r
+                       work->width[src] = 0;\r
+\r
+//\r
+// mark the start of the code\r
+//\r
+               work->codeofs[src] = FP_OFF(code);\r
+\r
+//\r
+// compile some code if the source pixel generates any screen pixels\r
+//\r
+               startpix+=toppix;\r
+               endpix+=toppix;\r
+\r
+               if (startpix == endpix || endpix < 0 || startpix >= VIEWHEIGHT || src == 64)\r
+                       continue;\r
+\r
+       //\r
+       // mov al,[si+src]\r
+       //\r
+               *code++ = 0x8a;\r
+               *code++ = 0x44;\r
+               *code++ = src;\r
+\r
+               for (;startpix<endpix;startpix++)\r
+               {\r
+                       if (startpix >= VIEWHEIGHT)\r
+                               break;                                          // off the bottom of the view area\r
+                       if (startpix < 0)\r
+                               continue;                                       // not into the view area\r
+\r
+               //\r
+               // and [es:di+heightofs],al\r
+               //\r
+                       *code++ = 0x26;\r
+                       *code++ = 0x20;\r
+                       *code++ = 0x85;\r
+                       *((unsigned far *)code)++ = startpix*screenbwide;\r
+               }\r
+\r
+       }\r
+\r
+//\r
+// retf\r
+//\r
+       *code++ = 0xcb;\r
+\r
+       totalsize = FP_OFF(code);\r
+       MM_GetPtr (finalspot,totalsize);\r
+       _fmemcpy ((byte _seg *)(*finalspot),(byte _seg *)work,totalsize);\r
+       MM_FreePtr (&(memptr)work);\r
+\r
+       return totalsize;\r
+}\r
+\r
+\r
+\r
+\r
+/*\r
+========================\r
+=\r
+= BuildCompShape\r
+=\r
+= typedef struct\r
+= {\r
+=      unsigned        width;\r
+=      unsigned        codeofs[64];\r
+= }    t_compshape;\r
+=\r
+= Width is the number of compiled line draws in the shape.  The shape\r
+= drawing code will assume that the midpoint of the shape is in the\r
+= middle of the width.\r
+=\r
+= The non background pixel data will start at codeofs[width], so codeofs\r
+= greater than width will be invalid.\r
+=\r
+= Each code offset will draw one vertical line of the shape, consisting\r
+= of 0 or more segments of scaled pixels.\r
+=\r
+= The scaled shapes use ss:0-4 as a scratch variable for the far call to\r
+= the compiled scaler, so zero it back out after the shape is scaled, or\r
+= a "null pointer assignment" will result upon termination.\r
+=\r
+= Setup for a call to a compiled shape\r
+= -----------------------------------\r
+= ax   toast\r
+= bx   toast\r
+= cx   toast\r
+= dx   segment of compiled shape\r
+= si   toast\r
+= di   byte at top of view area to draw the line in\r
+= bp   0\r
+= ss:2 and ds  the segment of the compiled scaler to use\r
+= es   screenseg\r
+=\r
+= Upon return, ds IS NOT SET to the data segment.  Do:\r
+=      mov     ax,ss\r
+=      mov     ds,ax\r
+=\r
+=\r
+= GC_BITMASK   set to the pixels to be drawn in the row of bytes under DI\r
+= GC_MODE              read mode 1, write mode 2\r
+= GC_COLORDONTCARE  set to 0, so all reads from video memory return 0xff\r
+=\r
+=\r
+= Code generated for each segment\r
+= -------------------------------\r
+=      mov     bx,[(segend+1)*2]\r
+=      mov     cx,[bx]\r
+=      mov     [BYTE PTR bx],0xc8              // far return\r
+=      mov     ax,[segstart*2]\r
+=      mov     [ss:0],ax                               // entry point into the compiled scaler\r
+=      mov     ds,dx                   // (mov ds,cs) the data is after the compiled code\r
+=      mov     si,ofs data\r
+=      call    [bp]                            // scale some pixels\r
+=      mov     ds,[bp+2]\r
+=      mov     [bx],cx                                 // un patch return\r
+=\r
+= Code generated after all segments on a line\r
+= -------------------------------------------\r
+=      retf\r
+=\r
+========================\r
+*/\r
+\r
+unsigned BuildCompShape (t_compshape _seg **finalspot)\r
+{\r
+       t_compshape     _seg *work;\r
+       byte            far *code;\r
+       int                     firstline,lastline,x,y;\r
+       unsigned        firstpix,lastpix,width;\r
+       unsigned        totalsize,pixelofs;\r
+       unsigned        buff;\r
+\r
+\r
+//     MM_GetPtr (&(memptr)work,20000);\r
+       EGAWRITEMODE(0);\r
+       EGAREADMAP(0);          // use ega screen memory for temp buffer\r
+       EGAMAPMASK(1);\r
+       buff = screenloc[1];\r
+       work = (t_compshape _seg *)(0xa000+(buff+15)/16);\r
+\r
+//\r
+// find the width of the shape\r
+//\r
+       firstline = -1;\r
+       x=0;\r
+       do\r
+       {\r
+               for (y=0;y<64;y++)\r
+                       if (spotvis[y][x] != BACKGROUNDPIX)\r
+                       {\r
+                               firstline = x;\r
+                               break;\r
+                       }\r
+               if (++x == 64)\r
+                       Quit ("BuildCompShape: No shape data!");\r
+       } while (firstline == -1);\r
+\r
+       lastline = -1;\r
+       x=63;\r
+       do\r
+       {\r
+               for (y=0;y<64;y++)\r
+                       if (spotvis[y][x] != BACKGROUNDPIX)\r
+                       {\r
+                               lastline = x;\r
+                               break;\r
+                       }\r
+               x--;\r
+       } while (lastline == -1);\r
+\r
+       width = lastline-firstline+1;\r
+\r
+       work->width = width;\r
+       code = (byte far *)&work->codeofs[width];\r
+\r
+//\r
+// copy all non background pixels to the work space\r
+//\r
+       pixelofs = FP_OFF(code);\r
+\r
+       for (x=firstline;x<=lastline;x++)\r
+               for (y=0;y<64;y++)\r
+                       if (spotvis[y][x] != BACKGROUNDPIX)\r
+                               *code++ = spotvis[y][x];\r
+\r
+//\r
+// start compiling the vertical lines\r
+//\r
+       for (x=firstline;x<=lastline;x++)\r
+       {\r
+               work->codeofs[x-firstline] = FP_OFF(code);\r
+\r
+               y=0;\r
+               do\r
+               {\r
+               //\r
+               // scan past black background pixels\r
+               //\r
+                       while (spotvis[y][x] == BACKGROUNDPIX && y<64)\r
+                               y++;\r
+\r
+                       if (y>63)               // no more segments\r
+                               break;\r
+\r
+                       firstpix = y+1;         // +1 because width is before codeofs\r
+\r
+               //\r
+               // scan past scalable pixels\r
+               //\r
+                       while (spotvis[y][x] != BACKGROUNDPIX && y<64)\r
+                               y++;\r
+\r
+                       if (y>63)\r
+                               lastpix = 65;\r
+                       else\r
+                               lastpix = y+1;  // actually one pixel past the last displayed\r
+\r
+               //\r
+               // compile the scale call\r
+               //\r
+                       *code++ = 0x8b;         // mov bx,[lastpix*2]\r
+                       *code++ = 0x1e;\r
+                       *((unsigned far *)code)++ = lastpix*2;\r
+\r
+                       *code++ = 0x8b;         // mov cx,[bx]\r
+                       *code++ = 0x0f;\r
+\r
+                       *code++ = 0xc6;         // move [BYTE bx],0xcb\r
+                       *code++ = 0x07;\r
+                       *code++ = 0xcb;\r
+\r
+                       *code++ = 0xa1;         // mov ax,[firstpix*2]  /*************\r
+                       *((unsigned far *)code)++ = firstpix*2;\r
+\r
+                       *code++ = 0x36;         // mov [ss:0],ax\r
+                       *code++ = 0xa3;\r
+                       *code++ = 0x00;\r
+                       *code++ = 0x00;\r
+\r
+                       *code++ = 0x8e;         // mov ds,dx    (mov ds,cs)\r
+                       *code++ = 0xda;\r
+\r
+                       *code++ = 0xbe;         // mov si,OFFSET pixelofs-firstpixel\r
+                       *((unsigned far *)code)++ = pixelofs-firstpix;\r
+\r
+                       *code++ = 0xff;         // call [DWORD bp]\r
+                       *code++ = 0x5e;\r
+                       *code++ = 0x00;\r
+\r
+                       *code++ = 0x8e;         // mov ds,[bp+2]\r
+                       *code++ = 0x5e;\r
+                       *code++ = 0x02;\r
+\r
+                       *code++ = 0x89;         // mov [bx],cx\r
+                       *code++ = 0x0f;\r
+\r
+                       pixelofs += (lastpix-firstpix);\r
+               } while (y<63);\r
+\r
+       //\r
+       // retf\r
+       //\r
+               *code++ = 0xcb;\r
+       }\r
+\r
+\r
+//\r
+// copy the final shape to a properly sized buffer\r
+//\r
+       totalsize = FP_OFF(code);\r
+\r
+       if (totalsize >= (PAGELEN*2))\r
+               Quit("BuildCompShape(): Shape is too complex!");\r
+\r
+       MM_GetPtr ((memptr *)finalspot,totalsize);\r
+       _fmemcpy ((byte _seg *)(*finalspot),(byte _seg *)work,totalsize);\r
+//     MM_FreePtr (&(memptr)work);\r
+\r
+       return totalsize;\r
+}\r
+\r
+\r
+\r
+/*\r
+=======================\r
+=\r
+= ScaleShape\r
+=\r
+= Draws a compiled shape at [scale] pixels high\r
+=\r
+= Setup for call\r
+= --------------\r
+= GC_MODE                      read mode 1, write mode 2\r
+= GC_COLORDONTCARE  set to 0, so all reads from video memory return 0xff\r
+= GC_INDEX                     pointing at GC_BITMASK\r
+=\r
+=======================\r
+*/\r
+\r
+static long            longtemp;\r
+\r
+void ScaleShape (int xcenter, t_compshape _seg *compshape, unsigned scale)\r
+{\r
+       #define MAX_OBJ_SCALE (MAXSCALE)\r
+\r
+\r
+       t_compscale _seg *comptable;\r
+       unsigned        width,scalewidth;\r
+       int                     x,pixel,lastpixel,pixwidth,min;\r
+       unsigned        far *codehandle, far *widthptr;\r
+       unsigned        badcodeptr;\r
+       int                     rightclip;\r
+\r
+       if (!compshape)\r
+               Quit ("ScaleShape: NULL compshape ptr!");\r
+\r
+       scale = (scale+1)/2;\r
+       if (!scale)\r
+               return;                                                         // too far away\r
+       if (scale>MAX_OBJ_SCALE)\r
+               scale = MAX_OBJ_SCALE;\r
+       comptable = scaledirectory[scale];\r
+\r
+       width = compshape->width;\r
+       scalewidth = comptable->start[width];\r
+\r
+       pixel = xcenter - scalewidth/2;\r
+       lastpixel = pixel+scalewidth-1;\r
+       if (pixel >= VIEWWIDTH || lastpixel < 0)\r
+               return;                                                         // totally off screen\r
+\r
+//\r
+// scan backwards from the right edge until pixels are visable\r
+// rightclip is the first NON VISABLE pixel\r
+//\r
+       if (lastpixel>=VIEWWIDTH-1)\r
+               rightclip = VIEWWIDTH-1;\r
+       else\r
+               rightclip = lastpixel;\r
+\r
+       if (zbuffer[rightclip]>scale)\r
+       {\r
+               if (pixel>0)\r
+                       min = pixel;\r
+               else\r
+                       min = 0;\r
+               do\r
+               {\r
+                       if (--rightclip < min)\r
+                               return;                                                 // totally covered or before 0\r
+                       if (zbuffer[rightclip]<=scale)\r
+                               break;\r
+               } while (1);\r
+       }\r
+       rightclip++;\r
+\r
+//\r
+// scan from the left until it is on screen, leaving\r
+// [pixel],[pixwidth],[codehandle],and [widthptr] set correctly\r
+//\r
+       *(((unsigned *)&longtemp)+1) = (unsigned)compshape;     // seg of shape\r
+       codehandle = &compshape->codeofs[0];\r
+       badcodeptr = compshape->codeofs[width];\r
+       widthptr = &comptable->width[0];\r
+       asm     mov     ax,[comptable]\r
+       asm     mov     WORD PTR [2],ax                         // ds:0-4 is used as a far call pointer\r
+                                                                               // by the compiled shapes\r
+       pixwidth = *widthptr;                           // scaled width of this pixel\r
+       while (!pixwidth)\r
+       {\r
+               pixwidth = *++widthptr;                 // find the first visable pixel\r
+               codehandle++;\r
+       }\r
+\r
+       if (pixel<0)\r
+       {\r
+               do\r
+               {\r
+                       if (pixel+pixwidth>0)\r
+                       {\r
+                               pixwidth += pixel;\r
+                               pixel = 0;\r
+                               break;\r
+                       }\r
+                       do\r
+                       {\r
+                               pixwidth = *++widthptr;\r
+                               codehandle++;\r
+                       } while (!pixwidth);\r
+                       pixel+=pixwidth;\r
+               } while (1);\r
+       }\r
+\r
+//\r
+// scan until it is visable, leaving\r
+// [pixel],[pixwidth],[codehandle],and [widthptr] set correctly\r
+//\r
+       do\r
+       {\r
+               if (zbuffer[pixel] <= scale)\r
+                       break;                                                  // start drawing here\r
+               pixel++;\r
+               if (!--pixwidth)\r
+               {\r
+                       do\r
+                       {\r
+                               pixwidth = *++widthptr;\r
+                               codehandle++;\r
+                       } while (!pixwidth);\r
+               }\r
+       } while (1);\r
+\r
+       if (pixel+pixwidth>rightclip)\r
+               pixwidth = rightclip-pixel;\r
+//\r
+// draw lines\r
+//\r
+       do              // while (1)\r
+       {\r
+       //\r
+       // scale a vertical segment [pixwidth] pixels wide at [pixel]\r
+       //\r
+               (unsigned)longtemp = *codehandle;       // offset of compiled code\r
+               if ((unsigned)longtemp == badcodeptr)\r
+                       Quit ("ScaleShape: codehandle past end!");\r
+\r
+               asm     mov     bx,[pixel]\r
+               asm     mov     di,bx\r
+               asm     shr     di,1\r
+               asm     shr     di,1\r
+               asm     shr     di,1                                            // X in bytes\r
+               asm     add     di,[bufferofs]\r
+               asm     and     bx,7\r
+               asm     shl     bx,1\r
+               asm     shl     bx,1\r
+               asm     shl     bx,1\r
+               asm     add     bx,[pixwidth]                           // bx = pixel*8+pixwidth-1\r
+               asm     dec     bx\r
+               asm     push    bx\r
+               asm     mov     al,BYTE PTR [bitmasks1+bx]\r
+               asm     mov     dx,GC_INDEX+1\r
+               asm     out     dx,al                                           // set bit mask register\r
+\r
+               asm     mov     es,[screenseg]\r
+               asm     push    si\r
+               asm     push    di\r
+               asm     push    bp\r
+               asm     xor     bp,bp\r
+               asm     mov     dx,[WORD PTR longtemp+2]\r
+               asm     mov     ds,[2]\r
+               asm     call ss:[DWORD PTR longtemp]            // scale the line of pixels\r
+               asm     mov     ax,ss\r
+               asm     mov     ds,ax\r
+               asm     pop             bp\r
+               asm     pop             di\r
+               asm     pop             si\r
+\r
+               asm     pop     bx\r
+               asm     mov     al,BYTE PTR [bitmasks2+bx]\r
+               asm     or      al,al\r
+               asm     jz      nosecond\r
+\r
+       //\r
+       // draw a second byte for vertical strips that cross two bytes\r
+       //\r
+               asm     inc     di\r
+               asm     mov     dx,GC_INDEX+1\r
+               asm     out     dx,al                                           // set bit mask register\r
+               asm     push    si\r
+               asm     push    di\r
+               asm     push    bp\r
+               asm     xor     bp,bp\r
+               asm     mov     dx,[WORD PTR longtemp+2]\r
+               asm     mov     ds,[2]\r
+               asm     call ss:[DWORD PTR longtemp]            // scale the line of pixels\r
+               asm     mov     ax,ss\r
+               asm     mov     ds,ax\r
+               asm     pop             bp\r
+               asm     pop             di\r
+               asm     pop             si\r
+\r
+\r
+       //\r
+       // advance to the next drawn line\r
+       //\r
+nosecond:;\r
+               if ( (pixel+=pixwidth) == rightclip )\r
+               {\r
+                       asm     mov     WORD PTR [0],0\r
+                       asm     mov     WORD PTR [2],0\r
+                       return;                                                 // all done!\r
+               }\r
+\r
+               do\r
+               {\r
+                       pixwidth = *++widthptr;\r
+                       codehandle++;\r
+               } while (!pixwidth);\r
+\r
+               if (pixel+pixwidth > rightclip)\r
+                       pixwidth = rightclip-pixel;\r
+\r
+       } while (1);\r
+\r
+}\r
+\r
+//\r
+// bit mask tables for drawing scaled strips up to eight pixels wide\r
+//\r
+\r
+byte   bitmasks1[8][8] = {\r
+{0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff},\r
+{0x40,0x60,0x70,0x78,0x7c,0x7e,0x7f,0x7f},\r
+{0x20,0x30,0x38,0x3c,0x3e,0x3f,0x3f,0x3f},\r
+{0x10,0x18,0x1c,0x1e,0x1f,0x1f,0x1f,0x1f},\r
+{0x8,0xc,0xe,0xf,0xf,0xf,0xf,0xf},\r
+{0x4,0x6,0x7,0x7,0x7,0x7,0x7,0x7},\r
+{0x2,0x3,0x3,0x3,0x3,0x3,0x3,0x3},\r
+{0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1} };\r
+\r
+byte   bitmasks2[8][8] = {\r
+{0,0,0,0,0,0,0,0},\r
+{0,0,0,0,0,0,0,0x80},\r
+{0,0,0,0,0,0,0x80,0xc0},\r
+{0,0,0,0,0,0x80,0xc0,0xe0},\r
+{0,0,0,0,0x80,0xc0,0xe0,0xf0},\r
+{0,0,0,0x80,0xc0,0xe0,0xf0,0xf8},\r
+{0,0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc},\r
+{0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe} };\r
+\r
+\r
+\r
+\r
+\r
+\r
diff --git a/16/cawat/C5_SCA_A.ASM b/16/cawat/C5_SCA_A.ASM
new file mode 100644 (file)
index 0000000..44c8eb1
--- /dev/null
@@ -0,0 +1,153 @@
+; Catacomb Armageddon Source Code\r
+; Copyright (C) 1993-2014 Flat Rock Software\r
+;\r
+; This program is free software; you can redistribute it and/or modify\r
+; it under the terms of the GNU General Public License as published by\r
+; the Free Software Foundation; either version 2 of the License, or\r
+; (at your option) any later version.\r
+;\r
+; This program is distributed in the hope that it will be useful,\r
+; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+; GNU General Public License for more details.\r
+;\r
+; You should have received a copy of the GNU General Public License along\r
+; with this program; if not, write to the Free Software Foundation, Inc.,\r
+; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+IDEAL\r
+MODEL  MEDIUM,C\r
+\r
+include "ID_ASM.EQU"\r
+\r
+;===========================================================================\r
+;\r
+;                    SCALING GRAPHICS\r
+;\r
+;===========================================================================\r
+\r
+\r
+\r
+MACRO  MAKELAB NUM\r
+\r
+lab&NUM:\r
+\r
+ENDM\r
+\r
+MACRO  MAKEREF NUM\r
+\r
+dw OFFSET lab&NUM\r
+\r
+ENDM\r
+\r
+\r
+;=========================================================================\r
+\r
+MAXSCALES equ 256\r
+\r
+       DATASEG\r
+\r
+EXTRN  screenseg:WORD\r
+EXTRN  linewidth:WORD\r
+\r
+LABEL endtable WORD\r
+labcount = 0\r
+REPT MAXSCALES\r
+MAKEREF %labcount\r
+labcount = labcount + 1\r
+ENDM\r
+\r
+\r
+       CODESEG\r
+\r
+;==================================================\r
+;\r
+; void scaleline (int scale, unsigned picseg, unsigned maskseg,\r
+;                 unsigned screen, unsigned width)\r
+;\r
+;==================================================\r
+\r
+PROC   ScaleLine pixels:word, scaleptr:dword, picptr:dword, screen:word\r
+USES   si,di\r
+PUBLIC ScaleLine\r
+\r
+;\r
+; modify doline procedure for proper width\r
+;\r
+       mov     bx,[pixels]\r
+       cmp     bx,MAXSCALES\r
+       jbe     @@scaleok\r
+       mov     bx,MAXSCALES\r
+@@scaleok:\r
+       shl     bx,1\r
+       mov     bx,[endtable+bx]\r
+       push    [cs:bx]                 ;save the code that will be modified over\r
+       mov     [WORD cs:bx],0d18eh     ;mov ss,cx\r
+       push    [cs:bx+2]               ;save the code that will be modified over\r
+       mov     [WORD cs:bx+2],90c3h    ;ret / nop\r
+       push    bx\r
+\r
+       mov     dx,[linewidth]\r
+\r
+       mov     di,[WORD screen]\r
+       mov     es,[screenseg]\r
+\r
+       mov     si,[WORD scaleptr]\r
+       mov     ds,[WORD scaleptr+2]\r
+\r
+       mov     bx,[WORD picptr]\r
+       mov     ax,[WORD picptr+2]      ;will be moved into ss after call\r
+\r
+       mov     bp,bx\r
+\r
+       cli\r
+       call    doline\r
+       sti\r
+;\r
+; restore doline to regular state\r
+;\r
+       pop     bx              ;address of modified code\r
+       pop     [cs:bx+2]\r
+       pop     [cs:bx]\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax\r
+       ret\r
+\r
+;================\r
+;\r
+; doline\r
+;\r
+; Big unwound scaling routine\r
+;\r
+; ds:si = scale table\r
+; ss:bx = pic data\r
+; es:di = screen location\r
+;\r
+;================\r
+\r
+doline:\r
+\r
+       mov     cx,ss\r
+       mov     ss,ax           ;can't call a routine with ss used...\r
+\r
+labcount = 0\r
+\r
+REPT MAXSCALES\r
+\r
+MAKELAB %labcount\r
+labcount = labcount + 1\r
+\r
+       lodsb                   ; get scaled pixel number\r
+       xlat    [ss:bx]         ; look it up in the picture\r
+       xchg    [es:di],al      ; load latches and write pixel to screen\r
+       add     di,dx           ; down to next line\r
+\r
+ENDM\r
+\r
+       mov     ss,cx\r
+       ret\r
+\r
+ENDP\r
+\r
+END
\ No newline at end of file
diff --git a/16/cawat/C5_STATE.C b/16/cawat/C5_STATE.C
new file mode 100644 (file)
index 0000000..ac10ae8
--- /dev/null
@@ -0,0 +1,682 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// C3_STATE.C\r
+\r
+#include "DEF.H"\r
+#pragma hdrstop\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+dirtype opposite[9] =\r
+       {south,west,north,east,southwest,northwest,northeast,southeast,nodir};\r
+\r
+\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= Internal_SpawnNewObj\r
+=\r
+===================\r
+*/\r
+void Internal_SpawnNewObj (unsigned x, unsigned y, statetype *state, unsigned size, boolean UseDummy, boolean PutInActorat)\r
+{\r
+       extern objtype dummyobj;\r
+\r
+       GetNewObj(UseDummy);\r
+       new->size = size;\r
+       new->state = state;\r
+       new->ticcount = random (state->tictime)+1;\r
+\r
+       new->tilex = x;\r
+       new->tiley = y;\r
+       new->x = ((long)x<<TILESHIFT)+TILEGLOBAL/2;\r
+       new->y = ((long)y<<TILESHIFT)+TILEGLOBAL/2;\r
+       CalcBounds(new);\r
+       new->dir = nodir;\r
+       new->active = noalways;\r
+\r
+       if (new != &dummyobj && PutInActorat)\r
+               actorat[new->tilex][new->tiley] = new;\r
+}\r
+\r
+void Internal_SpawnNewObjFrac (long x, long y, statetype *state, unsigned size,boolean UseDummy)\r
+{\r
+       GetNewObj(UseDummy);\r
+       new->size = size;\r
+       new->state = state;\r
+       new->ticcount = random (state->tictime)+1;\r
+       new->active = noalways;\r
+\r
+       new->x = x;\r
+       new->y = y;\r
+       new->tilex = x>>TILESHIFT;\r
+       new->tiley = y>>TILESHIFT;\r
+       CalcBounds(new);\r
+       new->distance = 100;\r
+       new->dir = nodir;\r
+}\r
+\r
+\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= CheckHandAttack\r
+=\r
+= If the object can move next to the player, it will return true\r
+=\r
+===================\r
+*/\r
+\r
+boolean CheckHandAttack (objtype *ob)\r
+{\r
+       long deltax,deltay,size;\r
+\r
+       size = (long)ob->size + player->size + ob->speed*tics + SIZE_TEST;\r
+       deltax = ob->x - player->x;\r
+       deltay = ob->y - player->y;\r
+\r
+       if (deltax > size || deltax < -size || deltay > size || deltay < -size)\r
+               return false;\r
+\r
+       return true;\r
+}\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= T_DoDamage\r
+=\r
+= Attacks the player if still nearby, then immediately changes to next state\r
+=\r
+===================\r
+*/\r
+\r
+void T_DoDamage (objtype *ob)\r
+{\r
+       int     points;\r
+\r
+\r
+       if (CheckHandAttack(ob) && (!(ob->flags & of_damagedone)))\r
+       {\r
+               points = 0;\r
+\r
+               switch (ob->obclass)\r
+               {\r
+               case zombieobj:\r
+               case fatdemonobj:\r
+                       points = 8;\r
+                       break;\r
+               case reddemonobj:\r
+               case godessobj:\r
+                       points = 15;\r
+                       break;\r
+               case antobj:\r
+                       points = 2;\r
+                       break;\r
+               case skeletonobj:\r
+                       points = 6;\r
+                       break;\r
+\r
+               case wetobj:\r
+                       points = 7;\r
+                       break;\r
+               case treeobj:\r
+                       points = 7;\r
+                       break;\r
+               case bunnyobj:\r
+                       points = 4;\r
+                       break;\r
+               }\r
+               TakeDamage (points);\r
+               ob->flags |= of_damagedone;\r
+       }\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+/*\r
+==================================\r
+=\r
+= Walk\r
+=\r
+==================================\r
+*/\r
+\r
+boolean Walk (objtype *ob)\r
+{\r
+       switch (ob->dir)\r
+       {\r
+       case north:\r
+               if (actorat[ob->tilex][ob->tiley-1])\r
+                       return false;\r
+               ob->tiley--;\r
+               ob->distance = TILEGLOBAL;\r
+               return true;\r
+\r
+       case northeast:\r
+               if (actorat[ob->tilex+1][ob->tiley-1])\r
+                       return false;\r
+               ob->tilex++;\r
+               ob->tiley--;\r
+               ob->distance = TILEGLOBAL;\r
+               return true;\r
+\r
+       case east:\r
+               if (actorat[ob->tilex+1][ob->tiley])\r
+                       return false;\r
+               ob->tilex++;\r
+               ob->distance = TILEGLOBAL;\r
+               return true;\r
+\r
+       case southeast:\r
+               if (actorat[ob->tilex+1][ob->tiley+1])\r
+                       return false;\r
+               ob->tilex++;\r
+               ob->tiley++;\r
+               ob->distance = TILEGLOBAL;\r
+               return true;\r
+\r
+       case south:\r
+               if (actorat[ob->tilex][ob->tiley+1])\r
+                       return false;\r
+               ob->tiley++;\r
+               ob->distance = TILEGLOBAL;\r
+               return true;\r
+\r
+       case southwest:\r
+               if (actorat[ob->tilex-1][ob->tiley+1])\r
+                       return false;\r
+               ob->tilex--;\r
+               ob->tiley++;\r
+               ob->distance = TILEGLOBAL;\r
+               return true;\r
+\r
+       case west:\r
+               if (actorat[ob->tilex-1][ob->tiley])\r
+                       return false;\r
+               ob->tilex--;\r
+               ob->distance = TILEGLOBAL;\r
+               return true;\r
+\r
+       case northwest:\r
+               if (actorat[ob->tilex-1][ob->tiley-1])\r
+                       return false;\r
+               ob->tilex--;\r
+               ob->tiley--;\r
+               ob->distance = TILEGLOBAL;\r
+               return true;\r
+\r
+       case nodir:\r
+               return false;\r
+       }\r
+\r
+       Quit ("Walk: Bad dir");\r
+       return false;\r
+}\r
+\r
+\r
+\r
+/*\r
+==================================\r
+=\r
+= ChaseThink\r
+= have the current monster go after the player,\r
+= either diagonally or straight on\r
+=\r
+==================================\r
+*/\r
+\r
+void ChaseThink (objtype *obj, boolean diagonal)\r
+{\r
+       int deltax,deltay,i;\r
+       dirtype d[3];\r
+       dirtype tdir, olddir, turnaround;\r
+\r
+\r
+       olddir=obj->dir;\r
+       turnaround=opposite[olddir];\r
+\r
+       deltax=player->tilex - obj->tilex;\r
+       deltay=player->tiley - obj->tiley;\r
+\r
+       d[1]=nodir;\r
+       d[2]=nodir;\r
+\r
+       if (deltax>0)\r
+               d[1]= east;\r
+       if (deltax<0)\r
+               d[1]= west;\r
+       if (deltay>0)\r
+               d[2]=south;\r
+       if (deltay<0)\r
+               d[2]=north;\r
+\r
+       if (abs(deltay)>abs(deltax))\r
+       {\r
+               tdir=d[1];\r
+               d[1]=d[2];\r
+               d[2]=tdir;\r
+       }\r
+\r
+       if (d[1]==turnaround)\r
+               d[1]=nodir;\r
+       if (d[2]==turnaround)\r
+               d[2]=nodir;\r
+\r
+\r
+       if (diagonal)\r
+       {                           /*ramdiagonals try the best dir first*/\r
+               if (d[1]!=nodir)\r
+               {\r
+                       obj->dir=d[1];\r
+                       if (Walk(obj))\r
+                               return;     /*either moved forward or attacked*/\r
+               }\r
+\r
+               if (d[2]!=nodir)\r
+               {\r
+                       obj->dir=d[2];\r
+                       if (Walk(obj))\r
+                               return;\r
+               }\r
+       }\r
+       else\r
+       {                  /*ramstraights try the second best dir first*/\r
+\r
+               if (d[2]!=nodir)\r
+               {\r
+                       obj->dir=d[2];\r
+                       if (Walk(obj))\r
+                               return;\r
+               }\r
+\r
+               if (d[1]!=nodir)\r
+               {\r
+                       obj->dir=d[1];\r
+                       if (Walk(obj))\r
+                               return;\r
+               }\r
+       }\r
+\r
+/* there is no direct path to the player, so pick another direction */\r
+\r
+       obj->dir=olddir;\r
+       if (Walk(obj))\r
+               return;\r
+\r
+       if (US_RndT()>128)      /*randomly determine direction of search*/\r
+       {\r
+               for (tdir=north;tdir<=west;tdir++)\r
+               {\r
+                       if (tdir!=turnaround)\r
+                       {\r
+                               obj->dir=tdir;\r
+                               if (Walk(obj))\r
+                                       return;\r
+                       }\r
+               }\r
+       }\r
+       else\r
+       {\r
+               for (tdir=west;tdir>=north;tdir--)\r
+               {\r
+                       if (tdir!=turnaround)\r
+                       {\r
+                         obj->dir=tdir;\r
+                         if (Walk(obj))\r
+                               return;\r
+                       }\r
+               }\r
+       }\r
+\r
+       obj->dir=turnaround;\r
+       Walk(obj);              /*last chance, don't worry about returned value*/\r
+}\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= MoveObj\r
+=\r
+=================\r
+*/\r
+\r
+void MoveObj (objtype *ob, long move)\r
+{\r
+       ob->distance -=move;\r
+\r
+       switch (ob->dir)\r
+       {\r
+       case north:\r
+               ob->y -= move;\r
+               return;\r
+       case northeast:\r
+               ob->x += move;\r
+               ob->y -= move;\r
+               return;\r
+       case east:\r
+               ob->x += move;\r
+               return;\r
+       case southeast:\r
+               ob->x += move;\r
+               ob->y += move;\r
+               return;\r
+       case south:\r
+               ob->y += move;\r
+               return;\r
+       case southwest:\r
+               ob->x -= move;\r
+               ob->y += move;\r
+               return;\r
+       case west:\r
+               ob->x -= move;\r
+               return;\r
+       case northwest:\r
+               ob->x -= move;\r
+               ob->y -= move;\r
+               return;\r
+\r
+       case nodir:\r
+               return;\r
+       }\r
+}\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= Chase\r
+=\r
+= returns true if hand attack range\r
+=\r
+=================\r
+*/\r
+\r
+boolean Chase (objtype *ob, boolean diagonal)\r
+{\r
+       long move;\r
+       long deltax,deltay,size;\r
+\r
+       ob->flags &= ~of_damagedone;\r
+\r
+       move = ob->speed*tics;\r
+       size = (long)ob->size + player->size + move + SIZE_TEST;\r
+\r
+       while (move)\r
+       {\r
+               deltax = ob->x - player->x;\r
+               deltay = ob->y - player->y;\r
+\r
+               if (deltax <= size && deltax >= -size\r
+               && deltay <= size && deltay >= -size)\r
+               {\r
+                       CalcBounds (ob);\r
+                       return true;\r
+               }\r
+\r
+               if (move < ob->distance)\r
+               {\r
+                       MoveObj (ob,move);\r
+                       break;\r
+               }\r
+               actorat[ob->tilex][ob->tiley] = 0;      // pick up marker from goal\r
+               if (ob->dir == nodir)\r
+                       ob->dir = north;\r
+\r
+               ob->x = ((long)ob->tilex<<TILESHIFT)+TILEGLOBAL/2;\r
+               ob->y = ((long)ob->tiley<<TILESHIFT)+TILEGLOBAL/2;\r
+               move -= ob->distance;\r
+\r
+               ChaseThink (ob,diagonal);\r
+               if (!ob->distance)\r
+                       break;                  // no possible move\r
+               actorat[ob->tilex][ob->tiley] = ob;     // set down a new goal marker\r
+       }\r
+       CalcBounds (ob);\r
+       return false;\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= ShootActor\r
+=\r
+===================\r
+*/\r
+\r
+void ShootActor (objtype *ob, unsigned damage)\r
+{\r
+       ob->hitpoints -= damage;\r
+\r
+       if (ob->hitpoints<=0)\r
+       {\r
+               switch (ob->obclass)\r
+               {\r
+               case reddemonobj:\r
+                       ob->state = &s_red_demondie1;\r
+                       break;\r
+               case succubusobj:\r
+                       ob->state = &s_succubus_death1;\r
+                       break;\r
+               case fatdemonobj:\r
+                       ob->state = &s_fatdemon_blowup1;\r
+                       break;\r
+               case godessobj:\r
+                       ob->state = &s_godessdie1;\r
+                       break;\r
+               case mageobj:\r
+                       ob->state = &s_magedie1;\r
+                       break;\r
+               case batobj:\r
+                       ob->state = &s_batdie1;\r
+#if USE_INERT_LIST\r
+                       ob->obclass = solidobj;         // don't add this obj to inert list\r
+#endif\r
+                       break;\r
+               case grelmobj:\r
+                       ob->state = &s_greldie1;\r
+                       break;\r
+\r
+               case zombieobj:\r
+                       ob->state = &s_zombie_death1;\r
+               break;\r
+\r
+               case skeletonobj:\r
+                       ob->state = &s_skel_die1;\r
+               break;\r
+\r
+               case antobj:\r
+                       ob->state = &s_ant_die1;\r
+               break;\r
+\r
+               case wetobj:\r
+                       ob->state = &s_wet_die1;\r
+#if USE_INERT_LIST\r
+                       ob->obclass = solidobj;         // don't add this obj to inert list\r
+#endif\r
+               break;\r
+\r
+               case eyeobj:\r
+                       ob->state = &s_eye_die1;\r
+               break;\r
+\r
+               case sshotobj:\r
+               case eshotobj:\r
+               case mshotobj:\r
+                       ob->state = &s_bonus_die;\r
+#if USE_INERT_LIST\r
+                       ob->obclass = solidobj;         // don't add these objs to inert list\r
+#endif\r
+               break;\r
+\r
+               case treeobj:\r
+                       ob->state = &s_tree_death1;\r
+                       ob->obclass = solidobj;\r
+                       ob->temp1 = 3;\r
+                       ob->flags &= ~of_damagedone;\r
+                       CalcBounds(ob);\r
+               break;\r
+\r
+               case bunnyobj:\r
+                       ob->state = &s_bunny_death1;\r
+               break;\r
+\r
+               case bonusobj:\r
+               case freezeobj:\r
+                       switch (ob->temp1)\r
+                       {\r
+                               case B_POTION:\r
+                               case B_CHEST:\r
+                               case B_NUKE:\r
+                               case B_BOLT:\r
+                                       ob->state = &s_pshot_exp1;\r
+                                       ob->obclass = expobj;\r
+                                       ob->ticcount = ob->state->tictime;\r
+                                       SpawnBigExplosion(ob->x,ob->y,12,(16l<<16L));\r
+                                       bordertime = FLASHTICS<<2;\r
+                                       bcolor = 14;\r
+                                       VW_ColorBorder(14 | 56);\r
+                                       DisplaySMsg("Item destroyed", NULL);\r
+                                       status_flag  = S_NONE;\r
+                                       status_delay = 80;\r
+                               break;\r
+                       }\r
+#if USE_INERT_LIST\r
+                       ob->obclass = solidobj;         // don't add this obj to inert list\r
+#endif\r
+               break;\r
+\r
+               }\r
+\r
+               if (ob->obclass != solidobj && ob->obclass != realsolidobj)\r
+               {\r
+                       ob->obclass = inertobj;\r
+                       ob->flags &= ~of_shootable;\r
+                       actorat[ob->tilex][ob->tiley] = NULL;\r
+#if USE_INERT_LIST\r
+                       MoveObjToInert(ob);\r
+#endif\r
+               }\r
+               else\r
+               {\r
+                       if (ob->flags & of_forcefield)\r
+                       {\r
+                               ob->state = &s_force_field_die;\r
+                               ob->flags &= ~of_shootable;\r
+                       }\r
+               }\r
+       }\r
+       else\r
+       {\r
+               switch (ob->obclass)\r
+               {\r
+               case reddemonobj:\r
+                       if (!(random(8)))\r
+                               ob->state = &s_red_demonouch;\r
+                       else\r
+                               return;\r
+                       break;\r
+               case succubusobj:\r
+                       ob->state = &s_succubus_ouch;\r
+                       break;\r
+               case fatdemonobj:\r
+                       ob->state = &s_fatdemon_ouch;\r
+                       break;\r
+               case godessobj:\r
+                       ob->state = &s_godessouch;\r
+                       break;\r
+               case mageobj:\r
+                       ob->state = &s_mageouch;\r
+                       break;\r
+\r
+               case grelmobj:\r
+                       ob->state = &s_grelouch;\r
+                       break;\r
+\r
+               case zombieobj:\r
+                       ob->state = &s_zombie_ouch;\r
+               break;\r
+\r
+               case antobj:\r
+                       ob->state = &s_ant_ouch;\r
+                       break;\r
+\r
+               case skeletonobj:\r
+                       ob->state = &s_skel_ouch;\r
+                       break;\r
+\r
+               case wetobj:\r
+                       ob->state = &s_wet_ouch;\r
+                       break;\r
+\r
+               case eyeobj:\r
+                       ob->state = &s_eye_ouch;\r
+               break;\r
+\r
+               case treeobj:\r
+                       ob->state = &s_tree_ouch;\r
+               break;\r
+\r
+               case bunnyobj:\r
+                       ob->state = &s_bunny_ouch;\r
+               break;\r
+               }\r
+       }\r
+       ob->ticcount = ob->state->tictime;\r
+}\r
+\r
+\r
diff --git a/16/cawat/C5_TRACE.C b/16/cawat/C5_TRACE.C
new file mode 100644 (file)
index 0000000..d9ed20e
--- /dev/null
@@ -0,0 +1,872 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// C3_TRACE.C\r
+\r
+#include "DEF.H"\r
+#pragma hdrstop\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+//\r
+// TESTWALLVISABLE will set the global variable wallvisable to 1 or 0\r
+// depending on if tile.x,tile.y,wallon is visable from focal point\r
+//\r
+#define TESTWALLVISABLE {                                              \\r
+       if (tile.y<focal.y)                         \\r
+               voffset = 0;                            \\r
+       else if (tile.y==focal.y)                   \\r
+               voffset = 3;                            \\r
+       else                                        \\r
+               voffset = 6;                            \\r
+       if (tile.x==focal.x)                        \\r
+               voffset ++;                             \\r
+       else if (tile.x>focal.x)                    \\r
+               voffset += 2;                           \\r
+       wallvisable = visable[voffset][wallon]; }\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+boolean        aborttrace;\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+unsigned       wallvisable,voffset;\r
+\r
+\r
+fixed edgex,edgey;\r
+\r
+int wallon;\r
+int basecolor;\r
+\r
+walltype *oldwall;\r
+\r
+//\r
+// offsets from upper left corner of a tile to the left and right edges of\r
+// a given wall (NORTH-WEST)\r
+//\r
+fixed point1x[4] = {GLOBAL1,GLOBAL1,0      ,0       };\r
+fixed point1y[4] = {0      ,GLOBAL1,GLOBAL1,0       };\r
+\r
+fixed point2x[4] = {0      ,GLOBAL1,GLOBAL1,0       };\r
+fixed point2y[4] = {0     ,0      ,GLOBAL1 ,GLOBAL1};\r
+\r
+\r
+//\r
+// offset from tile.x,tile.y of the tile that shares wallon side\r
+// (side is not visable if it is shared)\r
+//\r
+int sharex[4] = { 0, 1, 0,-1};\r
+int sharey[4] = {-1, 0, 1, 0};\r
+\r
+//\r
+// amount to move tile.x,tile.y to follow wallon to another tile\r
+//\r
+int followx[4] = {-1, 0, 1, 0};\r
+int followy[4] = { 0,-1, 0, 1};\r
+\r
+//\r
+// cornerwall gives the wall on the same tile to start following when the\r
+// wall ends at an empty tile (go around an edge on same tile)\r
+// turnwall gives the wall on tile.x+sharex,tile.y+sharey to start following\r
+// when the wall hits another tile (right angle corner)\r
+//\r
+int cornerwall[4] = {WEST,NORTH,EAST,SOUTH};\r
+int turnwall[4] = {EAST,SOUTH,WEST,NORTH};\r
+\r
+//\r
+// wall visabilities in reletive locations\r
+// -,- 0,- +,-\r
+// -,0 0,0 +,0\r
+// -,+ 0,+ +,+\r
+//\r
+int visable[9][4] =\r
+{\r
+ {0,1,1,0}, {0,0,1,0}, {0,0,1,1},\r
+ {0,1,0,0}, {0,0,0,0}, {0,0,0,1},\r
+ {1,1,0,0}, {1,0,0,0}, {1,0,0,1}\r
+};\r
+\r
+int startwall[9] =  {2,2,3, 1,0,3, 1,0,0};\r
+int backupwall[9] = {3,3,0, 2,0,0, 2,1,1};\r
+\r
+\r
+int    walllength;\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                        FUNCTIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+/*\r
+========================\r
+=\r
+= FollowTrace\r
+=\r
+========================\r
+*/\r
+\r
+int FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max)\r
+{\r
+       int tx,ty,otx,oty;\r
+       long absdx,absdy,xstep,ystep;\r
+\r
+       tx = tracex>>TILESHIFT;\r
+       ty = tracey>>TILESHIFT;\r
+\r
+       spotvis[tx][ty] = true;\r
+\r
+       absdx=LABS(deltax);\r
+       absdy=LABS(deltay);\r
+\r
+       if (absdx>absdy)\r
+       {\r
+               ystep = (deltay<<8)/(absdx>>8);\r
+\r
+               if (!ystep)\r
+                       ystep = deltay>0 ? 1 : -1;\r
+\r
+               oty = (tracey+ystep)>>TILESHIFT;\r
+               if (deltax>0)\r
+               {\r
+//###############\r
+//\r
+// step x by +1\r
+//\r
+//###############\r
+                       do\r
+                       {\r
+                               tx++;\r
+                               spotvis[tx][ty] = true;\r
+                               tracey+=ystep;\r
+                               ty = tracey>>TILESHIFT;\r
+\r
+                               if (ty!=oty)\r
+                               {\r
+                                       if (tilemap[tx-1][ty])\r
+                                       {\r
+                                               tile.x = tx-1;\r
+                                               tile.y = ty;\r
+                                               return 1;\r
+                                       }\r
+                                       oty = ty;\r
+                               }\r
+                               if (tilemap[tx][ty])\r
+                               {\r
+                                       tile.x = tx;\r
+                                       tile.y = ty;\r
+                                       return 1;\r
+                               }\r
+                       } while (--max);\r
+                       return 0;\r
+               }\r
+               else\r
+               {\r
+//###############\r
+//\r
+// step x by -1\r
+//\r
+//###############\r
+                       do\r
+                       {\r
+                               tx--;\r
+                               spotvis[tx][ty] = true;\r
+                               tracey+=ystep;\r
+                               ty = tracey>>TILESHIFT;\r
+\r
+                               if (ty!=oty)\r
+                               {\r
+                                       if (tilemap[tx][oty])\r
+                                       {\r
+                                               tile.x = tx;\r
+                                               tile.y = oty;\r
+                                               return 1;\r
+                                       }\r
+                                       oty = ty;\r
+                               }\r
+                               if (tilemap[tx][ty])\r
+                               {\r
+                                       tile.x = tx;\r
+                                       tile.y = ty;\r
+                                       return 1;\r
+                               }\r
+                       } while (--max);\r
+                       return 0;\r
+\r
+               }\r
+       }\r
+       else\r
+       {\r
+               xstep = (deltax<<8)/(absdy>>8);\r
+               if (!xstep)\r
+                       xstep = deltax>0 ? 1 : -1;\r
+\r
+\r
+               otx = (tracex+xstep)>>TILESHIFT;\r
+               if (deltay>0)\r
+               {\r
+//###############\r
+//\r
+// step y by +1\r
+//\r
+//###############\r
+                       do\r
+                       {\r
+                               ty++;\r
+                               spotvis[tx][ty] = true;\r
+                               tracex+=xstep;\r
+                               tx = tracex>>TILESHIFT;\r
+\r
+                               if (tx!=otx)\r
+                               {\r
+                                       if (tilemap[tx][ty-1])\r
+                                       {\r
+                                               tile.x = tx;\r
+                                               tile.y = ty-1;\r
+                                               return 1;\r
+                                       }\r
+                                       otx = tx;\r
+                               }\r
+                               if (tilemap[tx][ty])\r
+                               {\r
+                                       tile.x = tx;\r
+                                       tile.y = ty;\r
+                                       return 1;\r
+                               }\r
+                       } while (--max);\r
+                       return 0;\r
+               }\r
+               else\r
+               {\r
+//###############\r
+//\r
+// step y by -1\r
+//\r
+//###############\r
+                       do\r
+                       {\r
+                               ty--;\r
+                               spotvis[tx][ty] = true;\r
+                               tracex+=xstep;\r
+                               tx = tracex>>TILESHIFT;\r
+\r
+                               if (tx!=otx)\r
+                               {\r
+                                       if (tilemap[otx][ty])\r
+                                       {\r
+                                               tile.x = otx;\r
+                                               tile.y = ty;\r
+                                               return 1;\r
+                                       }\r
+                                       otx = tx;\r
+                               }\r
+                               if (tilemap[tx][ty])\r
+                               {\r
+                                       tile.x = tx;\r
+                                       tile.y = ty;\r
+                                       return 1;\r
+                               }\r
+                       } while (--max);\r
+                       return 0;\r
+               }\r
+\r
+       }\r
+\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= BackTrace\r
+=\r
+= Traces backwards from edgex,edgey to viewx,viewy to see if a closer\r
+= tile obscures the given point.  If it does, it finishes the wall and\r
+= starts a new one.\r
+= Returns true if a tile is hit.\r
+= Call with a 1 to have it automatically finish the current wall\r
+=\r
+=================\r
+*/\r
+\r
+int BackTrace (int finish)\r
+{\r
+  fixed tracex,tracey;\r
+  long deltax,deltay,absdx,absdy;\r
+  int steps,otx,oty,testx,testheight,offset,wall;\r
+\r
+  deltax = viewx-edgex;\r
+  deltay = viewy-edgey;\r
+\r
+  absdx = LABS(deltax);\r
+  absdy = LABS(deltay);\r
+\r
+  if (absdx>absdy)\r
+    steps = ABS(focal.x-(edgex>>TILESHIFT))-1;\r
+  else\r
+    steps = ABS(focal.y-(edgey>>TILESHIFT))-1;\r
+\r
+  if (steps<=0)\r
+    return 0;\r
+\r
+  otx = tile.x;\r
+  oty = tile.y;\r
+  if (!FollowTrace(edgex,edgey,deltax,deltay,steps))\r
+    return 0;\r
+\r
+//\r
+// if the start wall is behind the focal point, the trace went too far back\r
+//\r
+  if (ABS(tile.x-focal.x)<2 && ABS(tile.y-focal.y)<2)  // too close\r
+  {\r
+    if (tile.x == focal.x && tile.y == focal.y)\r
+    {\r
+      tile.x = otx;\r
+      tile.y = oty;\r
+      return 0;\r
+    }\r
+\r
+    if (tile.x<focal.x)\r
+    {\r
+      if (tile.y<focal.y)\r
+       wall = SOUTH;\r
+      else\r
+       wall = EAST;\r
+    }\r
+    else if (tile.x==focal.x)\r
+    {\r
+         if (tile.y<focal.y)\r
+       wall = SOUTH;\r
+      else\r
+       wall = NORTH;\r
+    }\r
+    else\r
+       {\r
+      if (tile.y<=focal.y)\r
+       wall = WEST;\r
+      else\r
+       wall = NORTH;\r
+    }\r
+\r
+    //\r
+    // rotate the X value to see if it is behind the view plane\r
+    //\r
+    if (TransformX (((long)tile.x<<16)+point1x[wall],\r
+                   ((long)tile.y<<16)+point1y[wall]) < FOCALLENGTH)\r
+    {\r
+      tile.x = otx;\r
+      tile.y = oty;\r
+      return 0;\r
+    }\r
+  }\r
+\r
+//\r
+// if the old wall is still behind a closer wall, ignore the back trace\r
+// and continue on (dealing with limited precision...)\r
+//\r
+  if (finish && !FinishWall ())        // the wall is still behind a forward wall\r
+  {\r
+    tile.x = otx;\r
+    tile.y = oty;\r
+    rightwall->x1 = oldwall->x2;               // common edge with last wall\r
+    rightwall->height1 = oldwall->height2;\r
+    return 0;\r
+  }\r
+\r
+\r
+//\r
+// back up along the intersecting face to find the rightmost wall\r
+//\r
+\r
+  if (tile.y<focal.y)\r
+    offset = 0;\r
+  else if (tile.y==focal.y)\r
+    offset = 3;\r
+  else\r
+    offset = 6;\r
+  if (tile.x==focal.x)\r
+    offset ++;\r
+  else if (tile.x>focal.x)\r
+    offset += 2;\r
+\r
+  wallon = backupwall[offset];\r
+\r
+  while (tilemap[tile.x][tile.y])\r
+  {\r
+    tile.x += followx[wallon];\r
+    tile.y += followy[wallon];\r
+  };\r
+\r
+  tile.x -= followx[wallon];\r
+  tile.y -= followy[wallon];\r
+\r
+  wallon = cornerwall[wallon]; // turn to first visable face\r
+\r
+  edgex = ((long)tile.x<<16);\r
+  edgey = ((long)tile.y<<16);\r
+\r
+  TransformPoint (edgex+point1x[wallon],edgey+point1y[wallon],\r
+    &rightwall->x1,&rightwall->height1);\r
+\r
+  basecolor = tilemap[tile.x][tile.y];\r
+\r
+  return 1;\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= ForwardTrace\r
+=\r
+= Traces forwards from edgex,edgey along the line from viewx,viewy until\r
+= a solid tile is hit.  Sets tile.x,tile.y\r
+=\r
+=================\r
+*/\r
+\r
+void ForwardTrace (void)\r
+{\r
+  int offset;\r
+  fixed tracex,tracey;\r
+  long deltax,deltay;\r
+\r
+  deltax = edgex-viewx;\r
+  deltay = edgey-viewy;\r
+\r
+  FollowTrace(edgex,edgey,deltax,deltay,0);\r
+\r
+  if (tile.y<focal.y)\r
+    offset = 0;\r
+  else if (tile.y==focal.y)\r
+    offset = 3;\r
+  else\r
+    offset = 6;\r
+  if (tile.x==focal.x)\r
+    offset ++;\r
+  else if (tile.x>focal.x)\r
+    offset += 2;\r
+\r
+  wallon = startwall[offset];\r
+\r
+//\r
+// start the new wall\r
+//\r
+  edgex = ((long)tile.x<<16);\r
+  edgey = ((long)tile.y<<16);\r
+\r
+//\r
+// if entire first wall is invisable, corner\r
+//\r
+  TransformPoint (edgex+point2x[wallon],edgey+point2y[wallon],\r
+    &rightwall->x2,&rightwall->height2);\r
+\r
+  if (tilemap [tile.x+sharex[wallon]] [tile.y+sharey[wallon]]\r
+  || rightwall->x2 < (rightwall-1)->x2 )\r
+    wallon = cornerwall [wallon];\r
+\r
+//\r
+// transform first point\r
+//\r
+\r
+  TransformPoint (edgex+point1x[wallon],edgey+point1y[wallon],\r
+    &rightwall->x1,&rightwall->height1);\r
+\r
+  basecolor = tilemap[tile.x][tile.y];\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= FinishWall\r
+=\r
+= Transforms edgex,edgey as the next point of the current wall\r
+= and sticks it in the wall list\r
+=\r
+=================\r
+*/\r
+\r
+int FinishWall (void)\r
+{\r
+  char num[20];\r
+\r
+  oldwall = rightwall;\r
+\r
+       rightwall->color  = basecolor;\r
+\r
+  TransformPoint (edgex,edgey,&rightwall->x2,&rightwall->height2);\r
+\r
+  if (rightwall->x2 <= (rightwall-1)->x2+2\r
+  && rightwall->height2 < (rightwall-1)->height2 )\r
+       return 0;\r
+\r
+  rightwall->walllength = walllength;\r
+\r
+  switch (wallon)\r
+  {\r
+  case north:\r
+  case south:\r
+         rightwall->side = 0;\r
+         rightwall->planecoord = edgey;\r
+         break;\r
+\r
+  case west:\r
+  case east:\r
+         rightwall->side = 1;\r
+         rightwall->planecoord = edgex;\r
+         break;\r
+  }\r
+\r
+  walllength = 1;\r
+\r
+  rightwall++;\r
+\r
+  return 1;\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= InsideCorner\r
+=\r
+=================\r
+*/\r
+\r
+void InsideCorner (void)\r
+{\r
+  int offset;\r
+\r
+  //\r
+  // the wall turned -90 degrees, so draw what we have, move to the new tile,\r
+  // change wallon, change color, and continue following.\r
+  //\r
+  FinishWall ();\r
+\r
+  tile.x += sharex[wallon];\r
+  tile.y += sharey[wallon];\r
+\r
+  wallon = turnwall[wallon];\r
+\r
+  //\r
+  // if the new wall is visable, continue following it.  Otherwise\r
+  // follow it backwards until it turns\r
+  //\r
+  TESTWALLVISABLE;\r
+\r
+  if (wallvisable)\r
+  {\r
+  //\r
+  // just turn to the next wall and continue\r
+  //\r
+    rightwall->x1 = oldwall->x2;               // common edge with last wall\r
+    rightwall->height1 = oldwall->height2;\r
+    basecolor = tilemap[tile.x][tile.y];\r
+    return;                    // continue from here\r
+  }\r
+\r
+  //\r
+  // back follow the invisable wall until it turns, then follow that\r
+  //\r
+  do\r
+  {\r
+       tile.x += followx[wallon];\r
+    tile.y += followy[wallon];\r
+  } while (tilemap[tile.x][tile.y]);\r
+\r
+  tile.x -= followx[wallon];\r
+  tile.y -= followy[wallon];\r
+\r
+  wallon = cornerwall[wallon]; // turn to first visable face\r
+\r
+  edgex = ((long)tile.x<<16)+point1x[wallon];\r
+  edgey = ((long)tile.y<<16)+point1y[wallon];\r
+\r
+  if (!BackTrace(0))           // backtrace without finishing a wall\r
+  {\r
+    TransformPoint (edgex,edgey,&rightwall->x1,&rightwall->height1);\r
+    basecolor = tilemap[tile.x][tile.y];\r
+  }\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= OutsideCorner\r
+=\r
+=================\r
+*/\r
+\r
+void OutsideCorner (void)\r
+{\r
+  int offset;\r
+\r
+  //\r
+  // edge is the outside edge of a corner, so draw the current wall and\r
+  // turn the corner (+90 degrees)\r
+  //\r
+  FinishWall ();\r
+\r
+  tile.x -= followx[wallon];   // backup to the real tile\r
+  tile.y -= followy[wallon];\r
+  wallon = cornerwall[wallon];\r
+\r
+  //\r
+  // if the new wall is visable, continue following it.  Otherwise\r
+  // trace a ray from the corner to find a wall in the distance to\r
+  // follow\r
+  //\r
+  TESTWALLVISABLE;\r
+\r
+  if (wallvisable)\r
+  {\r
+  //\r
+  // the new wall is visable, so just continue on\r
+  //\r
+    rightwall->x1 = oldwall->x2;               // common edge with last wall\r
+    rightwall->height1 = oldwall->height2;\r
+    return;                    // still on same tile, so color is ok\r
+  }\r
+\r
+//\r
+// start from a new tile further away\r
+//\r
+  ForwardTrace();              // find the next wall further back\r
+\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= FollowWalls\r
+=\r
+= Starts a wall edge at the leftmost edge of tile.x,tile.y and follows it\r
+= until something else is seen or the entire view area is covered\r
+=\r
+=================\r
+*/\r
+\r
+void FollowWalls (void)\r
+{\r
+  int height,newcolor,offset,wall;\r
+\r
+//####################\r
+//\r
+// figure leftmost wall of new tile\r
+//\r
+//####################\r
+\r
+restart:\r
+\r
+  walllength = 1;\r
+\r
+  if (tile.y<focal.y)\r
+       offset = 0;\r
+  else if (tile.y==focal.y)\r
+       offset = 3;\r
+  else\r
+       offset = 6;\r
+  if (tile.x==focal.x)\r
+       offset ++;\r
+  else if (tile.x>focal.x)\r
+       offset += 2;\r
+\r
+  wallon = startwall[offset];\r
+\r
+//\r
+// if the start wall is inside a block, skip it by cornering to the second wall\r
+//\r
+  if ( tilemap [tile.x+sharex[wallon]] [tile.y+sharey[wallon]])\r
+       wallon = cornerwall [wallon];\r
+\r
+//\r
+// transform first edge to screen coordinates\r
+//\r
+  edgex = ((long)tile.x<<16);\r
+  edgey = ((long)tile.y<<16);\r
+\r
+  TransformPoint (edgex+point1x[wallon],edgey+point1y[wallon],\r
+       &rightwall->x1,&rightwall->height1);\r
+\r
+  basecolor = tilemap[tile.x][tile.y];\r
+\r
+//##################\r
+//\r
+// follow the wall as long as possible\r
+//\r
+//##################\r
+\r
+advance:\r
+\r
+  do   // while ( tile.x != right.x || tile.y != right.y)\r
+  {\r
+//\r
+// check for conditions that shouldn't happed...\r
+//\r
+       if (rightwall->x1 > VIEWXH)     // somehow missed right tile...\r
+         return;\r
+\r
+       if (rightwall == &walls[DANGERHIGH])\r
+       {\r
+  //\r
+  // somethiing got messed up!  Correct by thrusting ahead...\r
+  //\r
+//             VW_ColorBorder(6);\r
+               bordertime = 60;\r
+               Thrust(player->angle,TILEGLOBAL/4);\r
+               player->angle+=5;\r
+               if (player->angle>ANGLES)\r
+                       player->angle-=ANGLES;\r
+               aborttrace = true;\r
+               return;\r
+\r
+#if 0\r
+         strcpy (str,"Wall list overflow at LE:");\r
+         itoa(mapon+1,str2,10);\r
+         strcat (str,str2);\r
+         strcat (str," X:");\r
+         ltoa(objlist[0].x,str2,10);\r
+         strcat (str,str2);\r
+         strcat (str," Y:");\r
+         ltoa(objlist[0].y,str2,10);\r
+         strcat (str,str2);\r
+         strcat (str," AN:");\r
+         itoa(objlist[0].angle,str2,10);\r
+         strcat (str,str2);\r
+\r
+         Quit (str);\r
+#endif\r
+       }\r
+\r
+//\r
+// proceed along wall\r
+//\r
+\r
+       edgex = ((long)tile.x<<16)+point2x[wallon];\r
+       edgey = ((long)tile.y<<16)+point2y[wallon];\r
+\r
+       if (BackTrace(1))               // went behind a closer wall\r
+         continue;\r
+\r
+       //\r
+       // advance to next tile along wall\r
+       //\r
+       tile.x += followx[wallon];\r
+       tile.y += followy[wallon];\r
+\r
+       if (tilemap [tile.x+sharex[wallon]] [tile.y+sharey[wallon]])\r
+       {\r
+         InsideCorner ();              // turn at a corner\r
+         continue;\r
+       }\r
+\r
+       newcolor = tilemap[tile.x][tile.y];\r
+\r
+       if (!newcolor)          // turn around an edge\r
+       {\r
+         OutsideCorner ();\r
+         continue;\r
+       }\r
+\r
+       if (newcolor != basecolor)\r
+       {\r
+         //\r
+         // wall changed color, so draw what we have and continue following\r
+         //\r
+         FinishWall ();\r
+         rightwall->x1 = oldwall->x2;  // new wall shares this edge\r
+         rightwall->height1 = oldwall->height2;\r
+         basecolor = newcolor;\r
+\r
+         continue;\r
+       }\r
+       walllength++;\r
+  } while (tile.x != right.x || tile.y != right.y);\r
+\r
+\r
+\r
+//######################\r
+//\r
+// draw the last tile\r
+//\r
+//######################\r
+\r
+  edgex = ((long)tile.x<<16)+point2x[wallon];\r
+  edgey = ((long)tile.y<<16)+point2y[wallon];\r
+  FinishWall();\r
+\r
+  wallon = cornerwall[wallon];\r
+\r
+  //\r
+  // if the corner wall is visable, draw it\r
+  //\r
+  TESTWALLVISABLE;\r
+\r
+  if (wallvisable)\r
+  {\r
+    rightwall->x1 = oldwall->x2;               // common edge with last wall\r
+    rightwall->height1 = oldwall->height2;\r
+    edgex = ((long)tile.x<<16)+point2x[wallon];\r
+    edgey = ((long)tile.y<<16)+point2y[wallon];\r
+    FinishWall();\r
+  }\r
+\r
+}\r
+\r
+//===========================================================================\r
diff --git a/16/cawat/C5_WIZ.C b/16/cawat/C5_WIZ.C
new file mode 100644 (file)
index 0000000..b899ef6
--- /dev/null
@@ -0,0 +1,3296 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// C3_WIZ.C\r
+\r
+#include "DEF.H"\r
+#include "gelib.h"\r
+#pragma hdrstop\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+////////#define NUMSCROLLS     8\r
+\r
+#define        SHOWITEMS       9\r
+\r
+#define        NUKETIME        40\r
+#define NUMBOLTS       10\r
+#define BOLTTICS       6\r
+\r
+#define STATUSCOLOR    8\r
+#define TEXTCOLOR      14\r
+\r
+#define SIDEBARWIDTH   5\r
+\r
+#define BODYLINE    8\r
+#define POWERLINE      80\r
+\r
+#define SPECTILESTART  0                       // 18\r
+\r
+\r
+#define SHOTDAMAGE             1\r
+#define BIGSHOTDAMAGE  3\r
+\r
+\r
+#define PLAYERSPEED    5120\r
+#define RUNSPEED       (8192<<1)\r
+\r
+#define SHOTSPEED      10000\r
+\r
+//#define LASTWALLTILE 47\r
+//#define LASTSPECIALTILE      37\r
+\r
+#define LASTTILE  (LASTWALLPIC-FIRSTWALLPIC)                                                   // 47\r
+\r
+#define FIRETIME       2\r
+\r
+#define HANDPAUSE      60\r
+\r
+#define        RIGHTEDGE       205;\r
+#define        LEFTEDGE        95;\r
+#define        PRNY            32;\r
+#define        WINX            10;\r
+#define        WINY            32;\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+long           lastnuke,lasthand;\r
+int                    lasttext;\r
+int                    handheight;\r
+int                    boltsleft,bolttimer;\r
+short RadarXY[MAX_RADAR_BLIPS][3]={-1,-1,-1};\r
+short radarx=RADARX,radary=RADARY,radar_xcenter=RADAR_XCENTER,radar_ycenter=RADAR_YCENTER;\r
+int key_x[4]={24,27,27,24},key_y[4]={30,57,30,57};\r
+\r
+boolean redraw_gems,button0down;\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+int                    lastradar;\r
+unsigned       lastfiretime;\r
+\r
+int    strafeangle[9] = {0,90,180,270,45,135,225,315,0};\r
+\r
+short RotateAngle = -1;                                // -1 == No Angle to turn to...\r
+short FreezeTime = 0;                          // Stops all think (except player)\r
+short RotateSpeed;                                     // Speed (and dir) to rotate..\r
+\r
+\r
+//===========================================================================\r
+\r
+void CalcBounds(objtype *ob);\r
+boolean VerifyGateExit(void);\r
+void DrawNSEWIcons(void);\r
+void DrawGems(void);\r
+void DrawRadar (void);\r
+void DrawChar (unsigned x, unsigned y, unsigned tile);\r
+void RedrawStatusWindow (void);\r
+void GiveBolt (void);\r
+void TakeBolt (void);\r
+void GiveNuke (void);\r
+void TakeNuke (void);\r
+void GivePotion (void);\r
+void TakePotion (void);\r
+void GiveKey (int keytype);\r
+void TakeKey (int keytype);\r
+////////////void GiveScroll (int scrolltype,boolean show);\r
+////////////void ReadScroll (int scroll);\r
+////////////void DrawScrolls(void);\r
+\r
+void DrawNum(short x,short y,short value,short maxdigits);\r
+\r
+//----------\r
+\r
+void Shoot (void);\r
+void BigShoot (void);\r
+void CastBolt (void);\r
+void CastNuke (void);\r
+void DrinkPotion (void);\r
+\r
+//----------\r
+void DrawHealth(void);\r
+\r
+void SpawnPlayer (int tilex, int tiley, int dir);\r
+void Thrust (int angle, unsigned speed);\r
+void T_Player (objtype *ob);\r
+\r
+//void AddPoints (int points);\r
+\r
+void ClipMove (objtype *ob, long xmove, long ymove);\r
+boolean ShotClipMove (objtype *ob, long xmove, long ymove);\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= DrawChar\r
+=\r
+===============\r
+*/\r
+\r
+void DrawChar (unsigned x, unsigned y, unsigned tile)\r
+{\r
+       unsigned junk = latchpics[0];\r
+\r
+       EGAWRITEMODE(1);\r
+asm    mov     bx,[y]\r
+asm    shl     bx,1\r
+asm    mov     di,[WORD PTR ylookup+bx]\r
+asm    add     di,[x]\r
+asm    mov     si,[tile]\r
+asm    shl     si,1\r
+asm    shl     si,1\r
+asm    shl     si,1\r
+asm    add     si,[junk]               // the damn inline assembler won't reference latchpics\r
+asm    mov     ax,[screenseg]\r
+asm    mov     es,ax\r
+asm    mov     ds,ax\r
+asm    mov     dx,SCREENWIDTH-1\r
+asm    movsb\r
+asm    add     di,dx\r
+asm    movsb\r
+asm    add     di,dx\r
+asm    movsb\r
+asm    add     di,dx\r
+asm    movsb\r
+asm    add     di,dx\r
+asm    movsb\r
+asm    add     di,dx\r
+asm    movsb\r
+asm    add     di,dx\r
+asm    movsb\r
+asm    add     di,dx\r
+asm    movsb\r
+\r
+asm    mov     ax,ss\r
+asm    mov     ds,ax\r
+       EGAWRITEMODE(0);\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= RedrawStatusWindow\r
+=\r
+===============\r
+*/\r
+\r
+void RedrawStatusWindow (void)\r
+{\r
+       short keytype;\r
+\r
+       EGABITMASK(0xff);\r
+       for (keytype=0; keytype<4; keytype++)\r
+               DrawNum(key_x[keytype],key_y[keytype],gamestate.keys[keytype],2);\r
+       DrawNum(20,54,gamestate.potions,2);\r
+       DrawNum(20,36,gamestate.nukes,2);\r
+       DrawNum(20,18,gamestate.bolts,2);\r
+\r
+       DrawHealth();\r
+       DrawRadar();\r
+       EGAWRITEMODE(0);\r
+       DrawGems();\r
+////////       DrawScrolls();\r
+       redraw_gems = false;\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= GiveBolt\r
+=\r
+===============\r
+*/\r
+\r
+void GiveBolt (void)\r
+{\r
+       if (gamestate.bolts == 99)\r
+               return;\r
+\r
+       SD_PlaySound (GETBOLTSND);\r
+       DrawNum(20,18,++gamestate.bolts,2);\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= TakeBolt\r
+=\r
+===============\r
+*/\r
+\r
+void TakeBolt (void)\r
+{\r
+       SD_PlaySound (USEBOLTSND);\r
+       DrawNum(20,18,--gamestate.bolts,2);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= GiveNuke\r
+=\r
+===============\r
+*/\r
+\r
+void GiveNuke (void)\r
+{\r
+       if (gamestate.nukes == 99)\r
+               return;\r
+\r
+       SD_PlaySound (GETNUKESND);\r
+       DrawNum(20,36,++gamestate.nukes,2);\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= TakeNuke\r
+=\r
+===============\r
+*/\r
+\r
+void TakeNuke (void)\r
+{\r
+       SD_PlaySound (USENUKESND);\r
+       DrawNum(20,36,--gamestate.nukes,2);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= GivePotion\r
+=\r
+===============\r
+*/\r
+\r
+void GivePotion (void)\r
+{\r
+       if (gamestate.potions == 99)\r
+               return;\r
+\r
+       SD_PlaySound (GETPOTIONSND);\r
+       DrawNum(20,54,++gamestate.potions,2);\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= TakePotion\r
+=\r
+===============\r
+*/\r
+\r
+void TakePotion (void)\r
+{\r
+       SD_PlaySound (USEPOTIONSND);\r
+       DrawNum(20,54,--gamestate.potions,2);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= GiveKey\r
+=\r
+===============\r
+*/\r
+\r
+void GiveKey (int keytype)\r
+{\r
+       int     i,j,x;\r
+\r
+       if (gamestate.keys[keytype] == 99)\r
+               return;\r
+\r
+       SD_PlaySound (GETKEYSND);\r
+       DrawNum(key_x[keytype],key_y[keytype],++gamestate.keys[keytype],2);\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= TakeKey\r
+=\r
+===============\r
+*/\r
+\r
+void TakeKey (int keytype)\r
+{\r
+       int     i,j,x;\r
+       char *key_colors[] = {"a RED key",\r
+                                                                "a YELLOW key",\r
+                                                                "a GREEN key",\r
+                                                                "a BLUE key"};\r
+\r
+\r
+       SD_PlaySound (USEKEYSND);\r
+       DrawNum(key_x[keytype],key_y[keytype],--gamestate.keys[keytype],2);\r
+       displayofs = bufferofs = screenloc[screenpage];\r
+       CenterWindow(20,5);\r
+       US_CPrint("\nYou use\n");\r
+       US_CPrint(key_colors[keytype]);\r
+       VW_UpdateScreen();\r
+       VW_WaitVBL(120);\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= GiveGem\r
+=\r
+===============\r
+*/\r
+\r
+void GiveGem (int gemtype)\r
+{\r
+#if 0\r
+       int     i,j,x;\r
+\r
+       SD_PlaySound (GETKEYSND);\r
+       DrawNum(key_x[keytype],key_y[keytype],++gamestate.keys[keytype],2);\r
+#endif\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= TakeGem\r
+=\r
+===============\r
+*/\r
+\r
+void TakeGem (int gemtype)\r
+{\r
+#if 0\r
+       int     i,j,x;\r
+\r
+       SD_PlaySound (USEKEYSND);\r
+       DrawNum(key_x[keytype],key_y[keytype],--gamestate.keys[keytype],2);\r
+#endif\r
+}\r
+\r
+/*\r
+===============\r
+=\r
+= DrawGem\r
+=\r
+===============\r
+*/\r
+\r
+void DrawGems()\r
+{\r
+       short loop;\r
+\r
+       redraw_gems = false;\r
+\r
+       bufferofs = 0;\r
+       LatchDrawPic (31,51,RADAR_BOTTOMPIC);\r
+       for (loop=0; loop<5; loop++)\r
+               if (gamestate.gems[loop])\r
+                       LatchDrawPic (32+loop,53,RADAR_RGEMPIC+loop);\r
+}\r
+\r
+//===========================================================================\r
+\r
+#if 0\r
+\r
+/*\r
+===============\r
+=\r
+= GiveScroll\r
+=\r
+===============\r
+*/\r
+\r
+void GiveScroll (int scrolltype,boolean show)\r
+{\r
+       int     i,j,x,y,scrollnum;\r
+\r
+       SD_PlaySound (GETSCROLLSND);\r
+       gamestate.scrolls[scrolltype] = true;\r
+\r
+       y = 30 + ((scrolltype > 3) * 10);\r
+       x = 26 + (scrolltype % 4);\r
+       DrawChar(x,y,SCROLLCHARS+scrolltype);\r
+\r
+       if (show)\r
+               ReadScroll(scrolltype);\r
+}\r
+\r
+/*\r
+===============\r
+=\r
+= DrawScrolls\r
+=\r
+= Force draw of all scrolls\r
+=\r
+===============\r
+*/\r
+void DrawScrolls()\r
+{\r
+       int loop,x,y;\r
+\r
+       VW_Bar(210,30,30,18,0xf);\r
+\r
+       for (loop=0;loop<8;loop++)\r
+               if (gamestate.scrolls[loop])\r
+               {\r
+                       y = 30 + ((loop > 3) * 10);\r
+                       x = 26 + (loop % 4);\r
+                       DrawChar(x,y,SCROLLCHARS+loop);\r
+               }\r
+}\r
+#endif\r
+\r
+\r
+//===========================================================================\r
+\r
+#if 0\r
+/*\r
+===============\r
+=\r
+= GivePoints\r
+=\r
+===============\r
+*/\r
+\r
+void GivePoints (int points)\r
+{\r
+       pointcount = 1;\r
+       pointsleft += points;\r
+}\r
+#endif\r
+\r
+\r
+//===========================================================================\r
+\r
+#if 0\r
+/*\r
+===============\r
+=\r
+= AddPoints\r
+=\r
+===============\r
+*/\r
+\r
+void AddPoints (int points)\r
+{\r
+       char    str[10];\r
+       int             len,x,i;\r
+\r
+       gamestate.score += points;\r
+\r
+       ltoa (gamestate.score,str,10);\r
+       len = strlen (str);\r
+\r
+       x=24+(8-len);\r
+       for (i=0;i<len;i++)\r
+               DrawChar(x++,40,NUMBERCHARS+str[i]-'0');\r
+}\r
+#endif\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= DrawHealth\r
+=\r
+===============\r
+*/\r
+void DrawHealth()\r
+{\r
+       char picnum;\r
+       int percentage;\r
+\r
+       percentage = PERCENTAGE(100,MAXBODY,gamestate.body,9);\r
+\r
+       DrawNum(11,57,percentage,3);\r
+\r
+       if (percentage > 75)\r
+               picnum = FACE1PIC;\r
+       else\r
+       if (percentage > 50)\r
+               picnum = FACE2PIC;\r
+       else\r
+       if (percentage > 25)\r
+               picnum = FACE3PIC;\r
+       else\r
+       if (percentage)\r
+               picnum = FACE4PIC;\r
+       else\r
+       {\r
+               picnum = FACE5PIC;\r
+               CA_CacheGrChunk (picnum);\r
+       }\r
+\r
+       bufferofs = 0;\r
+       if (!percentage)\r
+       {\r
+               UNMARKGRCHUNK(picnum);\r
+//             VW_DrawPic(8,14,picnum);\r
+               VW_DrawPic(10,14,picnum);\r
+       }\r
+       else\r
+               LatchDrawPic(10,14,picnum);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= DrawFreezeTime\r
+=\r
+===============\r
+*/\r
+void DrawFreezeTime()\r
+{\r
+       long percentage;\r
+       percentage = PERCENTAGE(100,MAXFREEZETIME,(long)FreezeTime,7);\r
+       DrawNum(23,70,percentage,3);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= DrawNum\r
+=\r
+===============\r
+*/\r
+void DrawNum(short x,short y,short value,short maxdigits)\r
+{\r
+       char str[10],len,i;\r
+\r
+       itoa(value,str,10);\r
+       len=strlen(str);\r
+\r
+       for (i=len; i<maxdigits; i++)\r
+               DrawChar(x++,y,BLANKCHAR);\r
+\r
+       for (i=0;i<len;i++)\r
+               DrawChar(x++,y,NUMBERCHARS+str[i]-'0');\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= GiveChest\r
+=\r
+===============\r
+*/\r
+\r
+void GiveChest(void)\r
+{\r
+       char i;\r
+\r
+       for (i=0;i<random(4);i++)\r
+       {\r
+               GiveBolt();\r
+               SD_WaitSoundDone();\r
+       }\r
+\r
+       for (i=0;i<random(3);i++)\r
+       {\r
+               GiveNuke();\r
+               SD_WaitSoundDone();\r
+       }\r
+\r
+       for (i=0;i<random(2);i++)\r
+       {\r
+               GivePotion();\r
+               SD_WaitSoundDone();\r
+       }\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= GiveGoal\r
+=\r
+===============\r
+*/\r
+\r
+void GiveGoal (void)\r
+{\r
+       SD_PlaySound (GETPOINTSSND);\r
+       playstate = ex_victorious;\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+#if 0\r
+/*\r
+===============\r
+=\r
+= DrawLevelNumber\r
+=\r
+===============\r
+*/\r
+\r
+void DrawLevelNumber (int number)\r
+{\r
+       char    str[10];\r
+       int             len;\r
+       unsigned        temp;\r
+\r
+       bufferofs = 0;\r
+       if (number<9)\r
+               PrintX=13;\r
+       else\r
+               PrintX = 5;\r
+       PrintY = 4;\r
+       VW_Bar (5,4,16,9,STATUSCOLOR);\r
+       temp = fontcolor;\r
+       fontcolor = TEXTCOLOR^STATUSCOLOR;\r
+       US_PrintUnsigned (number+1);\r
+       fontcolor = temp;\r
+}\r
+#endif\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= DrawText\r
+=\r
+===============\r
+*/\r
+\r
+void DrawText (boolean draw_text_whether_it_needs_it_or_not)\r
+{\r
+       unsigned        number;\r
+       char            str[80];\r
+       char            far *text;\r
+       unsigned        temp;\r
+\r
+       //\r
+       // draw a new text description if needed\r
+       //\r
+       number = *(mapsegs[0]+farmapylookup[player->tiley]+player->tilex)-NAMESTART;\r
+\r
+       if ( number>26 )\r
+               number = 0;\r
+\r
+       if ((number == lasttext) && (!draw_text_whether_it_needs_it_or_not))\r
+               return;\r
+\r
+       lasttext = number;\r
+\r
+       text = (char _seg *)grsegs[LEVEL1TEXT+mapon]+textstarts[number];\r
+\r
+       _fmemcpy (str,text,80);\r
+       DisplayMsg(str,NULL);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= DisplayMsg\r
+=\r
+===============\r
+*/\r
+\r
+char DisplayMsg(char *text,char *choices)\r
+{\r
+       char ch=true;\r
+       short temp;\r
+\r
+       bufferofs = 0;\r
+       PrintY = 1;\r
+       WindowX = 20;\r
+       WindowW = 270;\r
+\r
+       VW_Bar (WindowX,2,WindowW,8,STATUSCOLOR);\r
+       temp = fontcolor;\r
+       fontcolor = TEXTCOLOR^STATUSCOLOR;\r
+       US_CPrintLine (text);\r
+       fontcolor = temp;\r
+\r
+       if (choices)\r
+       {\r
+               ch=GetKeyChoice(choices,true);\r
+               LastScan = 0;\r
+       }\r
+\r
+       return(ch);\r
+}\r
+\r
+/*\r
+===============\r
+=\r
+= DisplaySMsg\r
+=\r
+===============\r
+*/\r
+char DisplaySMsg(char *text,char *choices)\r
+{\r
+       char ch=true;\r
+       short temp;\r
+\r
+       bufferofs = 0;\r
+       PrintY = 69;\r
+       WindowX = 98;\r
+       WindowW = 115;\r
+\r
+       VW_Bar(WindowX,PrintY+1,WindowW,8,STATUSCOLOR);\r
+       temp = fontcolor;\r
+       fontcolor = TEXTCOLOR^STATUSCOLOR;\r
+       US_CPrintLine (text);\r
+       fontcolor = temp;\r
+\r
+       if (choices)\r
+       {\r
+               ch=GetKeyChoice(choices,true);\r
+               LastScan = 0;\r
+       }\r
+\r
+       return(ch);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= DrawRadar\r
+=\r
+===============\r
+*/\r
+\r
+void DrawRadar (void)\r
+{\r
+       int             angle,number;\r
+       short objnum;\r
+\r
+       bufferofs = 0;\r
+       LatchDrawPic (radarx,radary,RADAR_TOPPIC);\r
+\r
+       asm     cli\r
+       asm     mov     dx,GC_INDEX\r
+       asm     mov     ax,2*256+GC_MODE\r
+       asm     out     dx,ax                                           // write mode 2\r
+\r
+       asm     mov     ax,GC_DATAROTATE\r
+       asm     out     dx,ax                // no rotation / logical operation\r
+\r
+       asm     mov     dx,SC_INDEX\r
+       asm     mov     al,SC_MAPMASK\r
+       asm     mov     ah,15\r
+       asm     out     dx,ax                                           // write to all four planes\r
+       asm     sti\r
+\r
+       objnum = 0;\r
+       while (RadarXY[objnum][2] != -1)\r
+       {\r
+               RadarBlip(radar_xcenter+RadarXY[objnum][0],radar_ycenter+RadarXY[objnum][1],RadarXY[objnum][2]);\r
+               objnum++;\r
+       }\r
+\r
+       asm     cli\r
+       asm     mov     dx,GC_INDEX\r
+       asm     mov     ax,255*256+GC_BITMASK\r
+       asm     out     dx,ax                                           // reset bitmask to %11111111\r
+       asm     sti\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+// DrawNSEWIcons(void)\r
+//--------------------------------------------------------------------------\r
+\r
+void DrawRadarObj(short dx, short dy, unsigned sprnum,signed long psin,signed long pcos);\r
+\r
+void DrawNSEWIcons()\r
+{\r
+       signed x,y;\r
+\r
+       x = -FixedByFrac(RADAR_X_IRADIUS,costable[player->angle]);\r
+       y = -FixedByFrac(RADAR_Y_IRADIUS,sintable[player->angle]);\r
+\r
+       VWB_DrawSprite(radar_xcenter+x-3,radar_ycenter+y-3,NORTHICONSPR);\r
+\r
+}\r
+\r
+#if 0\r
+/*\r
+===============\r
+=\r
+= DrawBars\r
+=\r
+===============\r
+*/\r
+\r
+void DrawBars (void)\r
+{\r
+       int                     i;\r
+       unsigned        source,dest,topline;\r
+\r
+       for (i=0;i<3;i++)\r
+       {\r
+               bufferofs = screenloc[i];\r
+               VW_Bar (34*8,POWERLINE,40,MAXSHOTPOWER,1);\r
+       }\r
+       EGAWRITEMODE(1);\r
+       asm     mov     es,[screenseg]\r
+\r
+//\r
+// shot power\r
+//\r
+       if (gamestate.shotpower)\r
+       {\r
+               topline = MAXSHOTPOWER - gamestate.shotpower;\r
+\r
+               source = latchpics[SHOTPOWERPIC-FIRSTLATCHPIC]+topline*SIDEBARWIDTH;\r
+               dest = (POWERLINE+topline)*SCREENWIDTH+34;\r
+\r
+               asm     mov     si,[source]\r
+               asm     mov     di,[dest]\r
+\r
+               asm     mov     cx,[WORD PTR gamestate.shotpower]\r
+newline:\r
+       asm     mov     al,[es:si]\r
+       asm     mov     [es:di+PAGE1START],al\r
+       asm     mov     [es:di+PAGE2START],al\r
+       asm     mov     [es:di+PAGE3START],al\r
+       asm     mov     al,[es:si+1]\r
+       asm     mov     [es:di+1+PAGE1START],al\r
+       asm     mov     [es:di+1+PAGE2START],al\r
+       asm     mov     [es:di+1+PAGE3START],al\r
+       asm     mov     al,[es:si+2]\r
+       asm     mov     [es:di+2+PAGE1START],al\r
+       asm     mov     [es:di+2+PAGE2START],al\r
+       asm     mov     [es:di+2+PAGE3START],al\r
+       asm     mov     al,[es:si+3]\r
+       asm     mov     [es:di+3+PAGE1START],al\r
+       asm     mov     [es:di+3+PAGE2START],al\r
+       asm     mov     [es:di+3+PAGE3START],al\r
+       asm     mov     al,[es:si+4]\r
+       asm     mov     [es:di+4+PAGE1START],al\r
+       asm     mov     [es:di+4+PAGE2START],al\r
+       asm     mov     [es:di+4+PAGE3START],al\r
+\r
+       asm     add     di,SCREENWIDTH\r
+       asm     add     si,5\r
+\r
+               asm     loop    newline\r
+       }\r
+\r
+//\r
+// body\r
+//\r
+       if (gamestate.body)\r
+       {\r
+               source = latchpics[BODYPIC-FIRSTLATCHPIC];\r
+               dest = BODYLINE*SCREENWIDTH+34;\r
+\r
+               asm     mov     si,[source]\r
+               asm     mov     di,[dest]\r
+\r
+               asm     mov     cx,[WORD PTR gamestate.body]\r
+newline2:\r
+       asm     mov     al,[es:si]\r
+       asm     mov     [es:di+PAGE1START],al\r
+       asm     mov     [es:di+PAGE2START],al\r
+       asm     mov     [es:di+PAGE3START],al\r
+       asm     mov     al,[es:si+1]\r
+       asm     mov     [es:di+1+PAGE1START],al\r
+       asm     mov     [es:di+1+PAGE2START],al\r
+       asm     mov     [es:di+1+PAGE3START],al\r
+       asm     mov     al,[es:si+2]\r
+       asm     mov     [es:di+2+PAGE1START],al\r
+       asm     mov     [es:di+2+PAGE2START],al\r
+       asm     mov     [es:di+2+PAGE3START],al\r
+       asm     mov     al,[es:si+3]\r
+       asm     mov     [es:di+3+PAGE1START],al\r
+       asm     mov     [es:di+3+PAGE2START],al\r
+       asm     mov     [es:di+3+PAGE3START],al\r
+       asm     mov     al,[es:si+4]\r
+       asm     mov     [es:di+4+PAGE1START],al\r
+       asm     mov     [es:di+4+PAGE2START],al\r
+       asm     mov     [es:di+4+PAGE3START],al\r
+\r
+       asm     add     di,SCREENWIDTH\r
+       asm     add     si,5\r
+\r
+               asm     loop    newline2\r
+       }\r
+\r
+       if (gamestate.body != MAXBODY)\r
+       {\r
+               source = latchpics[NOBODYPIC-FIRSTLATCHPIC]+gamestate.body*SIDEBARWIDTH;\r
+               dest = (BODYLINE+gamestate.body)*SCREENWIDTH+34;\r
+               topline = MAXBODY-gamestate.body;\r
+\r
+               asm     mov     si,[source]\r
+               asm     mov     di,[dest]\r
+\r
+               asm     mov     cx,[WORD PTR topline]\r
+newline3:\r
+       asm     mov     al,[es:si]\r
+       asm     mov     [es:di+PAGE1START],al\r
+       asm     mov     [es:di+PAGE2START],al\r
+       asm     mov     [es:di+PAGE3START],al\r
+       asm     mov     al,[es:si+1]\r
+       asm     mov     [es:di+1+PAGE1START],al\r
+       asm     mov     [es:di+1+PAGE2START],al\r
+       asm     mov     [es:di+1+PAGE3START],al\r
+       asm     mov     al,[es:si+2]\r
+       asm     mov     [es:di+2+PAGE1START],al\r
+       asm     mov     [es:di+2+PAGE2START],al\r
+       asm     mov     [es:di+2+PAGE3START],al\r
+       asm     mov     al,[es:si+3]\r
+       asm     mov     [es:di+3+PAGE1START],al\r
+       asm     mov     [es:di+3+PAGE2START],al\r
+       asm     mov     [es:di+3+PAGE3START],al\r
+       asm     mov     al,[es:si+4]\r
+       asm     mov     [es:di+4+PAGE1START],al\r
+       asm     mov     [es:di+4+PAGE2START],al\r
+       asm     mov     [es:di+4+PAGE3START],al\r
+\r
+       asm     add     di,SCREENWIDTH\r
+       asm     add     si,5\r
+\r
+               asm     loop    newline3\r
+       }\r
+\r
+       EGAWRITEMODE(0);\r
+}\r
+#endif\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+//  Check the object and make sure it is a monster.  Used in making the sound\r
+//  of a monster being shot.\r
+//\r
+/////////////////////////////////////////////////////////////////////////////\r
+\r
+boolean PlayMonsterSound(classtype objclass)\r
+{\r
+       switch (objclass)\r
+       {\r
+               case solidobj:\r
+               case realsolidobj:\r
+                       return false;\r
+               default:\r
+                       return true;\r
+       }\r
+}\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                       SHOTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+void T_Pshot (objtype *ob);\r
+\r
+\r
+extern statetype s_pshot1;\r
+extern statetype s_pshot2;\r
+\r
+//extern       statetype s_bigpshot1;\r
+//extern       statetype s_bigpshot2;\r
+\r
+\r
+statetype s_pshot1 = {PSHOT1PIC,8,&T_Pshot,&s_pshot2};\r
+statetype s_pshot2 = {PSHOT2PIC,8,&T_Pshot,&s_pshot1};\r
+\r
+\r
+statetype s_pshot_exp1 = {PSHOT_EXP1PIC,7,NULL,&s_pshot_exp2};\r
+statetype s_pshot_exp2 = {PSHOT_EXP2PIC,7,NULL,&s_pshot_exp3};\r
+statetype s_pshot_exp3 = {PSHOT_EXP3PIC,7,NULL,NULL};\r
+\r
+\r
+//statetype s_shotexplode = {PSHOT2PIC,8,NULL,NULL};\r
+\r
+//statetype s_bigpshot1 = {BIGPSHOT1PIC,8,&T_Pshot,&s_bigpshot2};\r
+//statetype s_bigpshot2 = {BIGPSHOT2PIC,8,&T_Pshot,&s_bigpshot1};\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= SpawnPShot\r
+=\r
+===================\r
+*/\r
+\r
+void SpawnPShot (void)\r
+{\r
+       DSpawnNewObjFrac (player->x,player->y,&s_pshot1,PIXRADIUS*7);\r
+       new->obclass = pshotobj;\r
+       new->speed = SHOTSPEED;\r
+       new->angle = player->angle;\r
+       new->active = always;\r
+}\r
+\r
+#if 0\r
+void SpawnBigPShot (void)\r
+{\r
+       SpawnNewObjFrac (player->x,player->y,&s_bigpshot1,24*PIXRADIUS);\r
+       new->obclass = bigpshotobj;\r
+       new->speed = SHOTSPEED;\r
+       new->angle = player->angle;\r
+}\r
+#endif\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= JimsShotClipMove\r
+=\r
+= Only checks corners, so the object better be less than one tile wide!\r
+=\r
+===================\r
+*/\r
+boolean JimsShotClipMove (objtype *ob, long xmove, long ymove)\r
+{\r
+       int                     xl,yl,xh,yh,tx,ty,nt1,nt2,x,y;\r
+       long            intersect,basex,basey,pointx,pointy;\r
+       unsigned        inside,total,tile;\r
+       objtype         *check;\r
+       boolean         moveok;\r
+\r
+//\r
+// move player and check to see if any corners are in solid tiles\r
+//\r
+//     basex = ob->x;\r
+//     basey = ob->y;\r
+\r
+//     ob->x += xmove;\r
+//     ob->y += ymove;\r
+\r
+//     CalcBounds (ob);\r
+\r
+       xl = ob->xl>>TILESHIFT;\r
+       yl = ob->yl>>TILESHIFT;\r
+\r
+       xh = ob->xh>>TILESHIFT;\r
+       yh = ob->yh>>TILESHIFT;\r
+\r
+       for (y=yl;y<=yh;y++)\r
+               for (x=xl;x<=xh;x++)\r
+               {\r
+                       check = actorat[x][y];\r
+\r
+                       if ((!check) || (check == player) || (!(check->flags & of_shootable)))\r
+                               continue;\r
+\r
+                       ob->x -= xmove;\r
+                       ob->y -= ymove;\r
+\r
+                       if (check->obclass != solidobj && check->obclass != hbunnyobj)\r
+                       {\r
+                               if (PlayMonsterSound(check->obclass))\r
+                                       SD_PlaySound (SHOOTMONSTERSND);\r
+                               if (ob->obclass == bigpshotobj)\r
+                                       ShootActor (check,BIGSHOTDAMAGE);\r
+                               else\r
+                                       ShootActor (check,SHOTDAMAGE);\r
+                       }\r
+                       else\r
+                               if (check->obclass == solidobj && (check->flags & of_forcefield))\r
+                               {\r
+                                       if (PlayMonsterSound(check->obclass))\r
+                                               SD_PlaySound (SHOOTMONSTERSND);\r
+                                       if (ob->obclass == bigpshotobj)\r
+                                               ShootActor (check,BIGSHOTDAMAGE);\r
+                                       else\r
+                                               ShootActor (check,SHOTDAMAGE);\r
+                               }\r
+                       ob->state = &s_pshot_exp1;\r
+                       ob->ticcount = ob->state->tictime;\r
+                       return(true);\r
+               }\r
+\r
+       return(false);          // move is OK!\r
+\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= T_Pshot\r
+=\r
+===============\r
+*/\r
+#if 0\r
+void T_Pshot (objtype *ob)\r
+{\r
+       objtype *check;\r
+       long    xmove,ymove,speed;\r
+\r
+//\r
+// check current position for monsters having moved into it\r
+//\r
+       for (check = player->next; check; check=check->next)\r
+               if ((check->flags & of_shootable)\r
+               && ob->xl <= check->xh\r
+               && ob->xh >= check->xl\r
+               && ob->yl <= check->yh\r
+               && ob->yh >= check->yl)\r
+               {\r
+\r
+                       if (check->obclass != solidobj)\r
+                       {\r
+                               if (PlayMonsterSound(check->obclass))\r
+                                       SD_PlaySound (SHOOTMONSTERSND);\r
+                               if (ob->obclass == bigpshotobj)\r
+                                       ShootActor (check,BIGSHOTDAMAGE);\r
+                               else\r
+                                       ShootActor (check,SHOTDAMAGE);\r
+                       }\r
+\r
+                       ob->state = &s_pshot_exp1;\r
+                       ob->ticcount = ob->state->tictime;\r
+                       return;\r
+               }\r
+\r
+\r
+//\r
+// move ahead, possibly hitting a wall\r
+//\r
+       speed = ob->speed*tics;\r
+\r
+       xmove = FixedByFrac(speed,costable[ob->angle]);\r
+       ymove = -FixedByFrac(speed,sintable[ob->angle]);\r
+\r
+       if (ShotClipMove(ob,xmove,ymove))\r
+       {\r
+               ob->state = &s_pshot_exp1;\r
+               ob->ticcount = ob->state->tictime;\r
+               return;\r
+       }\r
+\r
+       ob->tilex = ob->x >> TILESHIFT;\r
+       ob->tiley = ob->y >> TILESHIFT;\r
+\r
+//\r
+// check final position for monsters hit\r
+//\r
+       for (check = player->next; check; check=check->next)\r
+               if ((ob->flags & of_shootable)\r
+               && ob->xl <= check->xh\r
+               && ob->xh >= check->xl\r
+               && ob->yl <= check->yh\r
+               && ob->yh >= check->yl)\r
+               {\r
+                       ShootActor (check,SHOTDAMAGE);\r
+                       ob->state = &s_pshot_exp1;\r
+                       ob->ticcount = ob->state->tictime;\r
+                       return;\r
+               }\r
+}\r
+#endif\r
+\r
+\r
+\r
+void T_Pshot (objtype *ob)\r
+{\r
+       objtype *check;\r
+       long    xmove,ymove,speed;\r
+       int                     xl,yl,xh,yh,tx,ty,nt1,nt2,x,y;\r
+       long            intersect,basex,basey,pointx,pointy;\r
+       unsigned        inside,total,tile;\r
+       boolean         moveok;\r
+\r
+//\r
+// check current position for monsters having moved into it\r
+//\r
+       for (check = player->next; check; check=check->next)\r
+               if ((check->flags & of_shootable)\r
+               && ob->xl <= check->xh\r
+               && ob->xh >= check->xl\r
+               && ob->yl <= check->yh\r
+               && ob->yh >= check->yl)\r
+               {\r
+\r
+                       if (check->obclass != solidobj && check->obclass != hbunnyobj)\r
+                       {\r
+                               if (PlayMonsterSound(check->obclass))\r
+                                       SD_PlaySound (SHOOTMONSTERSND);\r
+                               if (ob->obclass == bigpshotobj)\r
+                                       ShootActor (check,BIGSHOTDAMAGE);\r
+                               else\r
+                                       ShootActor (check,SHOTDAMAGE);\r
+                       }\r
+\r
+                       ob->state = &s_pshot_exp1;\r
+                       ob->obclass = expobj;\r
+                       ob->ticcount = ob->state->tictime;\r
+                       return;\r
+               }\r
+\r
+\r
+//\r
+// move ahead, possibly hitting a wall\r
+//\r
+       speed = ob->speed*tics;\r
+\r
+       xmove = FixedByFrac(speed,costable[ob->angle]);\r
+       ymove = -FixedByFrac(speed,sintable[ob->angle]);\r
+\r
+       if (ShotClipMove(ob,xmove,ymove))\r
+       {\r
+               ob->state = &s_pshot_exp1;\r
+               ob->obclass = expobj;\r
+               ob->ticcount = ob->state->tictime;\r
+               return;\r
+       }\r
+\r
+       ob->tilex = ob->x >> TILESHIFT;\r
+       ob->tiley = ob->y >> TILESHIFT;\r
+\r
+//\r
+// check final position for monsters hit\r
+//\r
+\r
+       JimsShotClipMove(obj,xmove,ymove);\r
+\r
+}\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                       PLAYER ACTIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+/*\r
+===============\r
+=\r
+= BuildShotPower\r
+=\r
+===============\r
+*/\r
+\r
+void BuildShotPower (void)\r
+{\r
+       int             newlines,topline;\r
+       long    i;\r
+       unsigned        source,dest;\r
+\r
+       if (gamestate.shotpower == MAXSHOTPOWER)\r
+               return;\r
+\r
+       newlines = 0;\r
+       for (i=lasttimecount-realtics;i<lasttimecount;i++)\r
+               newlines += (i&1);\r
+\r
+       gamestate.shotpower += newlines;\r
+\r
+       if (gamestate.shotpower > MAXSHOTPOWER)\r
+       {\r
+               newlines -= (gamestate.shotpower - MAXSHOTPOWER);\r
+               gamestate.shotpower = MAXSHOTPOWER;\r
+       }\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= ClearShotPower\r
+=\r
+===============\r
+*/\r
+\r
+void ClearShotPower (void)\r
+{\r
+       unsigned        source,dest,topline;\r
+\r
+#if 0\r
+       topline = MAXSHOTPOWER - gamestate.shotpower;\r
+\r
+       source = latchpics[L_NOSHOT]+topline*SIDEBARWIDTH;\r
+       dest = (POWERLINE+topline)*SCREENWIDTH+34;\r
+\r
+       asm     mov     es,[screenseg]\r
+       asm     mov     si,[source]\r
+       asm     mov     di,[dest]\r
+\r
+       if (!gamestate.shotpower)\r
+               return;\r
+\r
+       EGAWRITEMODE(1);\r
+\r
+       asm     mov     cx,[WORD PTR gamestate.shotpower]\r
+newline:\r
+       asm     mov     al,[es:si]\r
+       asm     mov     [es:di+PAGE1START],al\r
+       asm     mov     [es:di+PAGE2START],al\r
+       asm     mov     [es:di+PAGE3START],al\r
+       asm     mov     al,[es:si+1]\r
+       asm     mov     [es:di+1+PAGE1START],al\r
+       asm     mov     [es:di+1+PAGE2START],al\r
+       asm     mov     [es:di+1+PAGE3START],al\r
+       asm     mov     al,[es:si+2]\r
+       asm     mov     [es:di+2+PAGE1START],al\r
+       asm     mov     [es:di+2+PAGE2START],al\r
+       asm     mov     [es:di+2+PAGE3START],al\r
+       asm     mov     al,[es:si+3]\r
+       asm     mov     [es:di+3+PAGE1START],al\r
+       asm     mov     [es:di+3+PAGE2START],al\r
+       asm     mov     [es:di+3+PAGE3START],al\r
+       asm     mov     al,[es:si+4]\r
+       asm     mov     [es:di+4+PAGE1START],al\r
+       asm     mov     [es:di+4+PAGE2START],al\r
+       asm     mov     [es:di+4+PAGE3START],al\r
+\r
+       asm     add     di,SCREENWIDTH\r
+       asm     add     si,5\r
+\r
+       asm     loop    newline\r
+\r
+       EGAWRITEMODE(0);\r
+#endif\r
+\r
+       gamestate.shotpower = 0;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= Shoot\r
+=\r
+===============\r
+*/\r
+\r
+void Shoot (void)\r
+{\r
+       ClearShotPower ();\r
+       SD_PlaySound (SHOOTSND);\r
+       SpawnPShot ();\r
+}\r
+\r
+//===========================================================================\r
+\r
+#if 0\r
+/*\r
+===============\r
+=\r
+= BigShoot\r
+=\r
+===============\r
+*/\r
+\r
+void BigShoot (void)\r
+{\r
+       ClearShotPower ();\r
+       SD_PlaySound (BIGSHOOTSND);\r
+       SpawnBigPShot ();\r
+}\r
+#endif\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= CastBolt\r
+=\r
+===============\r
+*/\r
+\r
+void CastBolt (void)\r
+{\r
+       if (!gamestate.bolts)\r
+       {\r
+               SD_PlaySound (NOITEMSND);\r
+               return;\r
+       }\r
+\r
+       TakeBolt ();\r
+       boltsleft = NUMBOLTS;\r
+       bolttimer = BOLTTICS;\r
+       Shoot ();\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= ContinueBolt\r
+=\r
+===============\r
+*/\r
+\r
+void ContinueBolt (void)\r
+{\r
+       bolttimer-=realtics;\r
+       if (bolttimer<0)\r
+       {\r
+               boltsleft--;\r
+               bolttimer = BOLTTICS;\r
+               Shoot ();\r
+       }\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= CastNuke\r
+=\r
+===============\r
+*/\r
+\r
+void CastNuke (void)\r
+{\r
+       extern boolean autofire;\r
+\r
+       int     angle;\r
+\r
+       if (!gamestate.nukes)\r
+       {\r
+               SD_PlaySound (NOITEMSND);\r
+               return;\r
+       }\r
+\r
+       if (!autofire)\r
+               TakeNuke ();\r
+       lastnuke = TimeCount;\r
+\r
+       for (angle = 0; angle < ANGLES; angle+= ANGLES/16)\r
+       {\r
+               DSpawnNewObjFrac (player->x,player->y,&s_pshot1,24*PIXRADIUS);\r
+               new->obclass = bigpshotobj;\r
+               new->speed = SHOTSPEED;\r
+               new->angle = angle;\r
+               new->active = always;\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= DrinkPotion\r
+=\r
+===============\r
+*/\r
+\r
+void DrinkPotion (void)\r
+{\r
+       unsigned        source,dest,topline;\r
+\r
+       if (!gamestate.potions)\r
+       {\r
+               SD_PlaySound (NOITEMSND);\r
+               return;\r
+       }\r
+\r
+       DisplaySMsg("Curing", NULL);\r
+       TakePotion ();\r
+       gamestate.body = MAXBODY;\r
+       VW_WaitVBL(30);\r
+       status_flag    = S_NONE;\r
+\r
+#if 0\r
+//\r
+// draw a full up bar\r
+//\r
+       source = latchpics[L_BODYBAR];\r
+       dest = BODYLINE*SCREENWIDTH+34;\r
+\r
+       asm     mov     es,[screenseg]\r
+       asm     mov     si,[source]\r
+       asm     mov     di,[dest]\r
+\r
+       EGAWRITEMODE(1);\r
+\r
+       asm     mov     cx,MAXBODY\r
+newline:\r
+       asm     mov     al,[es:si]\r
+       asm     mov     [es:di+PAGE1START],al\r
+       asm     mov     [es:di+PAGE2START],al\r
+       asm     mov     [es:di+PAGE3START],al\r
+       asm     mov     al,[es:si+1]\r
+       asm     mov     [es:di+1+PAGE1START],al\r
+       asm     mov     [es:di+1+PAGE2START],al\r
+       asm     mov     [es:di+1+PAGE3START],al\r
+       asm     mov     al,[es:si+2]\r
+       asm     mov     [es:di+2+PAGE1START],al\r
+       asm     mov     [es:di+2+PAGE2START],al\r
+       asm     mov     [es:di+2+PAGE3START],al\r
+       asm     mov     al,[es:si+3]\r
+       asm     mov     [es:di+3+PAGE1START],al\r
+       asm     mov     [es:di+3+PAGE2START],al\r
+       asm     mov     [es:di+3+PAGE3START],al\r
+       asm     mov     al,[es:si+4]\r
+       asm     mov     [es:di+4+PAGE1START],al\r
+       asm     mov     [es:di+4+PAGE2START],al\r
+       asm     mov     [es:di+4+PAGE3START],al\r
+       asm     add     di,SCREENWIDTH\r
+       asm     add     si,5\r
+\r
+       asm     loop    newline\r
+\r
+       EGAWRITEMODE(0);\r
+#endif\r
+}\r
+\r
+\r
+\r
+//===========================================================================\r
+\r
+#if 0\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+//   GetScrollText\r
+//\r
+//   parms   - scroll -- the number of the scroll to display\r
+//   returns - a far pointer to the scroll text\r
+//\r
+////////////////////////////////////////////////////////////////////////////\r
+\r
+char far *GetScrollText (int scroll)\r
+{\r
+       boolean found;\r
+       int     i;\r
+       char far *txt;\r
+       unsigned ofset;\r
+\r
+       CA_CacheGrChunk(SCROLLTEXT);\r
+\r
+       found = false;\r
+       i     = 0;\r
+\r
+       txt = (char _seg *)grsegs[SCROLLTEXT];\r
+\r
+       while (!found)\r
+       {\r
+               while (*txt != '\n')\r
+               {\r
+                       if (*txt == '\r')\r
+                               *txt = 0;\r
+                       txt++;\r
+               }\r
+               txt++;\r
+               if (i == scroll)\r
+               {\r
+                       found   = true;\r
+                       ofset = FP_OFF(txt);\r
+\r
+                       while (*txt != '\n')\r
+                       {\r
+                               if (*txt == '\r')\r
+                                       *txt = 0;\r
+                               txt++;\r
+                       }\r
+               }\r
+               i++;\r
+       }\r
+       txt = (char _seg *)grsegs[SCROLLTEXT]+ofset;\r
+\r
+       UNMARKGRCHUNK(SCROLLTEXT);\r
+       return(txt);\r
+}      //End of GetScrollText\r
+\r
+//===========================================================================\r
+\r
+/*\r
+===============\r
+=\r
+= ReadScroll\r
+=\r
+===============\r
+*/\r
+\r
+extern boolean tileneeded[NUMFLOORS];\r
+\r
+void ReadScroll (int scroll)\r
+{\r
+       PresenterInfo pi;\r
+       int     i;\r
+       unsigned *skytemp,*gndtemp,blackcolor=0;\r
+       char far *scrolltext;\r
+\r
+       DisplaySMsg("Reading Scroll", NULL);\r
+       bufferofs = displayofs = screenloc[screenpage];\r
+\r
+       if (status_flag != S_TIMESTOP)\r
+               status_flag = S_NONE;\r
+\r
+       FreeUpMemory();\r
+\r
+       CA_CacheGrChunk (SCROLLTOPPIC);\r
+       CA_CacheGrChunk (SCROLL1PIC);\r
+       CA_CacheGrChunk (SCROLLBOTTOMPIC);\r
+\r
+       skytemp = skycolor;\r
+       gndtemp = groundcolor;\r
+       skycolor = groundcolor = &blackcolor;\r
+\r
+       VW_Bar(0,0,VIEWWIDTH,VIEWHEIGHT,0);\r
+       VW_DrawPic (10,0,SCROLLTOPPIC);\r
+       VW_DrawPic (10,32,SCROLL1PIC);\r
+       VW_DrawPic (10,88,SCROLLBOTTOMPIC);\r
+\r
+       scrolltext = GetScrollText(scroll);\r
+\r
+       pi.xl = LEFTEDGE;\r
+       pi.yl = PRNY;\r
+       pi.xh = RIGHTEDGE;\r
+       pi.yh = PRNY+1;\r
+       pi.bgcolor = 7;\r
+       pi.script[0] = (char far *)scrolltext;\r
+       Presenter(&pi);\r
+\r
+       skycolor = skytemp;\r
+       groundcolor = gndtemp;\r
+\r
+       UNMARKGRCHUNK(SCROLL1PIC);\r
+       UNMARKGRCHUNK(SCROLLTOPPIC);\r
+       UNMARKGRCHUNK(SCROLLBOTTOMPIC);\r
+       MM_FreePtr (&grsegs[SCROLL1PIC]);\r
+       MM_FreePtr (&grsegs[SCROLLTOPPIC]);\r
+       MM_FreePtr (&grsegs[SCROLLBOTTOMPIC]);\r
+\r
+       CacheScaleds();\r
+\r
+       IN_ClearKeysDown ();\r
+       lasttext = -1;\r
+       DisplayMsg("Press ENTER or ESC to exit.",NULL);\r
+       while ((!Keyboard[sc_Escape]) && (!Keyboard[sc_Enter]));\r
+       IN_ClearKeysDown ();\r
+\r
+       if (status_flag == S_TIMESTOP)\r
+               DisplaySMsg("Time Stopped:     ",NULL);\r
+}\r
+\r
+#endif\r
+\r
+\r
+//===============\r
+//\r
+// StopTime()\r
+//\r
+//\r
+//===============\r
+void StopTime()\r
+{\r
+       FreezeTime = MAXFREEZETIME;\r
+       SD_PlaySound(FREEZETIMESND);\r
+       DisplaySMsg("Time Stopped:     ",NULL);\r
+       status_flag = S_TIMESTOP;\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= TakeDamage\r
+=\r
+===============\r
+*/\r
+\r
+void TakeDamage (int points)\r
+{\r
+       unsigned        source,dest,topline;\r
+\r
+       if (!gamestate.body || (bordertime && bcolor==FLASHCOLOR) || godmode)\r
+               return;\r
+\r
+       if (points != 1)\r
+               points = EasyDoDamage(points);\r
+\r
+       if (points >= gamestate.body)\r
+       {\r
+               points = gamestate.body;\r
+               Flags |= FL_DEAD;\r
+       }\r
+\r
+       bordertime = FLASHTICS<<2;\r
+       bcolor = FLASHCOLOR;\r
+       VW_ColorBorder (FLASHCOLOR);\r
+\r
+       DisplaySMsg("Damaging blows!", NULL);\r
+       status_flag  = S_NONE;\r
+       status_delay = 80;\r
+\r
+       if (gamestate.body<MAXBODY/3)\r
+               SD_PlaySound (TAKEDMGHURTSND);\r
+       else\r
+               SD_PlaySound (TAKEDAMAGESND);\r
+\r
+       gamestate.body -= points;\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                       INTERACTION\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+#if 0\r
+/*\r
+==================\r
+=\r
+= OpenDoor\r
+=\r
+==================\r
+*/\r
+\r
+void OpenDoor (unsigned bx, unsigned by, unsigned doorbase)\r
+{\r
+       int x,y;\r
+       unsigned        far *map;\r
+\r
+       x=bx;\r
+       y=by;\r
+       map = mapsegs[0]+farmapylookup[y]+x;\r
+       while (tilemap[x][y]-doorbase<4)\r
+       {\r
+               tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;\r
+               map--;\r
+               x--;\r
+       }\r
+       x=bx+1;\r
+       map = mapsegs[0]+farmapylookup[y]+x;\r
+       while (tilemap[x][y]-doorbase<4)\r
+       {\r
+               tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;\r
+               map++;\r
+               x++;\r
+       }\r
+       x=bx;\r
+       y=by-1;\r
+       map = mapsegs[0]+farmapylookup[y]+x;\r
+       while (tilemap[x][y]-doorbase<4)\r
+       {\r
+               tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;\r
+               map-=mapwidth;\r
+               y--;\r
+       }\r
+       y=by+1;\r
+       map = mapsegs[0]+farmapylookup[y]+x;\r
+       while (tilemap[x][y]-doorbase<4)\r
+       {\r
+               tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;\r
+               map+=mapwidth;\r
+               y++;\r
+       }\r
+}\r
+#endif\r
+\r
+#if 0\r
+/*\r
+==================\r
+=\r
+= RemoveWalls - similar to OpenDoor(), but on a different plane\r
+=\r
+==================\r
+*/\r
+void RemoveWalls (unsigned bx, unsigned by, unsigned remove_code)\r
+{\r
+       int x,y;\r
+       unsigned        far *map,*p2;\r
+\r
+       x=bx;\r
+       y=by;\r
+       p2 = *(mapsegs[2]+farmapylookup[y]+x);\r
+       map = mapsegs[0]+farmapylookup[y]+x;\r
+       while (*p2 == remove_code)\r
+       {\r
+               tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;\r
+               map--;\r
+               p2--;\r
+               x--;\r
+       }\r
+       x=bx+1;\r
+       p2 = *(mapsegs[2]+farmapylookup[y]+x);\r
+       map = mapsegs[0]+farmapylookup[y]+x;\r
+       while (*p2 == remove_code)\r
+       {\r
+               tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;\r
+               map++;\r
+               p2++;\r
+               x++;\r
+       }\r
+       x=bx;\r
+       y=by-1;\r
+       p2 = *(mapsegs[2]+farmapylookup[y]+x);\r
+       map = mapsegs[0]+farmapylookup[y]+x;\r
+       while (*p2 == remove_code)\r
+       {\r
+               tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;\r
+               map-=mapwidth;\r
+               p2 -= mapwidth;\r
+               y--;\r
+       }\r
+       y=by+1;\r
+       p2 = *(mapsegs[2]+farmapylookup[y]+x);\r
+       map = mapsegs[0]+farmapylookup[y]+x;\r
+       while (*p2 == remove_code)\r
+       {\r
+               tilemap[x][y] = (unsigned)actorat[x][y] = *map = 0;\r
+               map+=mapwidth;\r
+               p2 += mapwidth;\r
+               y++;\r
+       }\r
+}\r
+#endif\r
+\r
+/*\r
+==================\r
+=\r
+= HitSpecialTile\r
+=\r
+= Returns true if the move is blocked\r
+=\r
+==================\r
+*/\r
+\r
+boolean HitSpecialTile (unsigned x, unsigned y, unsigned tile)\r
+{\r
+       objtype *check;\r
+       short keyspot;\r
+       unsigned        temp,spot,curmap=gamestate.mapon,newlevel;\r
+       char *key_colors[] = {"a RED key",\r
+                             "a YELLOW key",\r
+                             "a GREEN key",\r
+                             "a BLUE key"};\r
+\r
+       switch (tile)\r
+       {\r
+               case 65:\r
+                       playstate = ex_victorious;\r
+               break;\r
+\r
+               case 9:\r
+               case 15:\r
+               case 27:\r
+               case 30:\r
+               case 40:\r
+               case 42:\r
+               case 43:\r
+               case 45:\r
+               case 46:\r
+               case 47:\r
+               case 49:\r
+               case 76:\r
+               case 77:\r
+\r
+                       if (!playstate && !FreezeTime)\r
+                       {\r
+\r
+                       // Is this an openable door? (Is "openable" a word?)\r
+                       //\r
+                               spot = (*(mapsegs[2]+farmapylookup[y]+x)) >> 8;\r
+                               if (spot == CANT_OPEN_CODE)     // CAN'T EVER OPEN (it's just for looks)\r
+                               {\r
+                                       CenterWindow(30,4);\r
+                                       US_CPrint("\nThis door is permanently blocked");\r
+                                       VW_UpdateScreen();\r
+                                       IN_ClearKeysDown();\r
+                                       IN_Ack();\r
+                                       return;\r
+                               }\r
+\r
+                               // make sure player has key to get into door\r
+                               //\r
+\r
+                               if (TILE_FLAGS(tile) & tf_EMBEDDED_KEY_COLOR)\r
+                                       keyspot = GATE_KEY_COLOR(tile);\r
+                               else\r
+                                       keyspot = (*(mapsegs[2]+farmapylookup[y+1]+x)) >> 8;\r
+\r
+                               if (keyspot--)\r
+                                       if (!gamestate.keys[keyspot])\r
+                                       {\r
+                                               SD_PlaySound(HIT_GATESND);\r
+                                               CenterWindow(20,5);\r
+                                               US_CPrint("\nYou need\n");\r
+                                               US_CPrint(key_colors[keyspot]);\r
+                                               VW_UpdateScreen();\r
+                                               IN_ClearKeysDown();\r
+                                               IN_Ack();\r
+                                               return;\r
+                                       }\r
+\r
+                       //\r
+                       // deal with this gate (warp? simply open? whatever...)\r
+                       //\r
+                               switch (spot)\r
+                               {\r
+                                       case NEXT_LEVEL_CODE:           // WARP TO NEXT LEVEL\r
+                                               newlevel = gamestate.mapon+1;\r
+                                               playstate = ex_warped;\r
+                                       break;\r
+\r
+                                       case REMOVE_DOOR_CODE:          // REMOVE DOOR\r
+                                               (unsigned)actorat[x][y] = tilemap[x][y] =       *(mapsegs[0]+farmapylookup[y]+x) = 0;\r
+                                               *(mapsegs[2]+farmapylookup[y+1]+x) = 0; // key no longer needed\r
+                                               if (keyspot>=0)\r
+                                                       TakeKey(keyspot);\r
+                                       break;\r
+\r
+                                       default:                        // WARP TO A LEVEL\r
+                                               newlevel = spot;\r
+                                               playstate = ex_warped;\r
+                                       break;\r
+                               }\r
+\r
+                               if (playstate == ex_warped)\r
+                               {\r
+                                       SD_PlaySound(HIT_GATESND);\r
+//                                     levelinfo *li=&gamestate.levels[curmap];\r
+\r
+//                                     OldAngle = FaceDoor(x,y);\r
+\r
+                                       if (!VerifyGateExit())\r
+                                       {\r
+                                               IN_ClearKeysDown ();\r
+                                               playstate = ex_stillplaying;\r
+                                               break;\r
+                                       }\r
+\r
+//                                     FaceAngle(OldAngle);\r
+\r
+                                       if (keyspot>=0)\r
+                                               TakeKey(keyspot);\r
+                                       *(mapsegs[2]+farmapylookup[y+1]+x) = 0; // key no longer needed\r
+\r
+                                       gamestate.mapon = newlevel;\r
+                                       SD_PlaySound(WARPUPSND);\r
+                                       IN_ClearKeysDown ();\r
+\r
+//                                     li->x = player->tilex;\r
+//                                     li->y = player->tiley;\r
+//                                     li->angle = player->angle+180;\r
+//                                     if (li->angle > 360)\r
+//                                             li->angle -= 360;\r
+                               }\r
+                       }\r
+               break;\r
+       }\r
+\r
+       return true;\r
+}\r
+\r
+//-------------------------------------------------------------------------\r
+// VerifyGateExit()\r
+//-------------------------------------------------------------------------\r
+boolean VerifyGateExit()\r
+{\r
+       char choices[] = {sc_Escape,sc_Y,sc_N,0},ch;\r
+\r
+       ch=DisplayMsg("Pass this way?      Y/N",choices);\r
+       DrawText(true);\r
+\r
+       return(ch == sc_Y);\r
+}\r
+\r
+\r
+/*\r
+==================\r
+=\r
+= TouchActor\r
+=\r
+= Returns true if the move is blocked\r
+=\r
+==================\r
+*/\r
+\r
+boolean TouchActor (objtype *ob, objtype *check)\r
+{\r
+       if (ob->xh < check->xl || ob->xl > check->xh ||\r
+               ob->yh < check->yl || ob->yl > check->yh)\r
+               return false;                           // not quite touching\r
+\r
+       switch (check->obclass)\r
+       {\r
+               case bonusobj:\r
+                       switch (check->temp1)\r
+                       {\r
+                               case B_BOLT:            GiveBolt ();            break;\r
+\r
+                               case B_NUKE:            GiveNuke ();            break;\r
+\r
+                               case B_POTION:          GivePotion ();          break;\r
+\r
+//                             case B_RKEY2:           GiveKey(B_RKEY-B_RKEY);                                 break;\r
+\r
+                               case B_RKEY:\r
+                               case B_YKEY:\r
+                               case B_GKEY:\r
+                               case B_BKEY:            GiveKey (check->temp1-B_RKEY);          break;\r
+\r
+#if 0\r
+                               case B_SCROLL1:\r
+                               case B_SCROLL2:\r
+                               case B_SCROLL3:\r
+                               case B_SCROLL4:\r
+                               case B_SCROLL5:\r
+                               case B_SCROLL6:\r
+                               case B_SCROLL7:\r
+                               case B_SCROLL8: GiveScroll (check->temp1-B_SCROLL1,true);       break;\r
+#endif\r
+\r
+                               case B_CHEST:           GiveChest ();           break;\r
+\r
+                               case B_RGEM:\r
+                               case B_YGEM:\r
+                               case B_GGEM:\r
+                               case B_BGEM:\r
+                               case B_PGEM:\r
+                                       SD_PlaySound(GETGEMSND);\r
+                                       gamestate.gems[check->temp1-B_RGEM] = GEM_DELAY_TIME;\r
+                                       redraw_gems = true;\r
+                               break;\r
+\r
+                               default:\r
+                                       Quit("TouchActor(): INVALID BONUS");\r
+                               break;\r
+                       }\r
+\r
+                       (unsigned)actorat[check->tilex][check->tiley] = 0;\r
+                       RemoveObj (check);\r
+\r
+                       return false;\r
+\r
+               case freezeobj:\r
+                       StopTime();\r
+                       (unsigned)actorat[check->tilex][check->tiley] = 0;\r
+                       RemoveObj(check);\r
+                       return(false);\r
+\r
+               case cloudobj:\r
+                       TakeDamage(2);\r
+                       return false;\r
+       }\r
+\r
+       return  true;\r
+}\r
+\r
+\r
+/*\r
+==================\r
+=\r
+= CalcBounds\r
+=\r
+==================\r
+*/\r
+\r
+void CalcBounds (objtype *ob)\r
+{\r
+//\r
+// calculate hit rect\r
+//\r
+  ob->xl = ob->x - ob->size;\r
+  ob->xh = ob->x + ob->size;\r
+  ob->yl = ob->y - ob->size;\r
+  ob->yh = ob->y + ob->size;\r
+}\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= LocationInActor\r
+=\r
+===================\r
+*/\r
+\r
+boolean LocationInActor (objtype *ob)\r
+{\r
+       int     x,y,xmin,ymin,xmax,ymax;\r
+       objtype *check;\r
+\r
+       CalcBounds (ob);\r
+\r
+       xmin = (ob->x >> TILESHIFT)-2;\r
+       ymin = (ob->y >> TILESHIFT)-2;\r
+       xmax = xmin+5;\r
+       ymax = ymin+5;\r
+\r
+       for (x=xmin;x<xmax;x++)\r
+               for (y=ymin;y<ymax;y++)\r
+               {\r
+                       check = actorat[x][y];\r
+                       if (check>(objtype *)LASTTILE\r
+                               && (check->flags & of_shootable)\r
+                               &&      (check->obclass != bonusobj)\r
+                               && (check->obclass != freezeobj)\r
+                               && (check->obclass != solidobj)\r
+                               && ob->xl-SIZE_TEST <= check->xh\r
+                               && ob->xh+SIZE_TEST >= check->xl\r
+                               && ob->yl-SIZE_TEST <= check->yh\r
+                               && ob->yh+SIZE_TEST >= check->yl)\r
+                                       return true;\r
+               }\r
+\r
+       return false;\r
+}\r
+\r
+/*\r
+===================\r
+=\r
+= ClipXMove\r
+=\r
+= Only checks corners, so the object better be less than one tile wide!\r
+=\r
+===================\r
+*/\r
+void ClipXMove (objtype *ob, long xmove)\r
+{\r
+       int                     xl,yl,xh,yh,tx,ty,nt1,nt2,x,y;\r
+       long            intersect,basex,basey,pointx,pointy;\r
+       unsigned        inside,total,tile;\r
+       objtype         *check;\r
+       boolean         moveok;\r
+       boolean         invisible_present = false;\r
+\r
+//\r
+// move player and check to see if any corners are in solid tiles\r
+//\r
+       basex = ob->x;\r
+       basey = ob->y;\r
+\r
+       ob->x += xmove;\r
+\r
+       CalcBounds (ob);\r
+\r
+       xl = ob->xl>>TILESHIFT;\r
+       yl = ob->yl>>TILESHIFT;\r
+\r
+       xh = ob->xh>>TILESHIFT;\r
+       yh = ob->yh>>TILESHIFT;\r
+\r
+       for (y=yl;y<=yh;y++)\r
+               for (x=xl;x<=xh;x++)\r
+               {\r
+                       check = actorat[x][y];\r
+\r
+                       if (!check)\r
+                               continue;               // blank floor, walk ok\r
+\r
+                       if ((unsigned)check <= LASTTILE)\r
+                       {\r
+                               if (TILE_FLAGS((unsigned)check) & tf_SPECIAL)\r
+                               {\r
+                                       HitSpecialTile(x,y,(unsigned)check-SPECTILESTART);\r
+                                       goto blockmove;\r
+                               }\r
+\r
+                               if (TILE_FLAGS((unsigned)check) & tf_INVISIBLE_WALL)\r
+                               {\r
+                                       invisible_present = true;\r
+                                       goto blockmove;\r
+                               }\r
+\r
+\r
+                               if (TILE_FLAGS((unsigned)check) & tf_SOLID)\r
+                               {\r
+                                       goto blockmove;                 // solid wall\r
+                               }\r
+                       }\r
+\r
+                       TouchActor(ob,check);           // pick up items\r
+               }\r
+\r
+//\r
+// check nearby actors\r
+//\r
+       if (LocationInActor(ob))\r
+       {\r
+               ob->x -= xmove;\r
+               if (LocationInActor(ob))\r
+               {\r
+                       ob->x += xmove;\r
+                       if (LocationInActor(ob))\r
+                               ob->x -= xmove;\r
+               }\r
+       }\r
+       return;         // move is OK!\r
+\r
+\r
+blockmove:\r
+\r
+//     if (!SD_SoundPlaying())\r
+//             SD_PlaySound (HITWALLSND);\r
+\r
+       moveok = false;\r
+\r
+       do\r
+       {\r
+               xmove /= 2;\r
+               if (moveok)\r
+               {\r
+                       ob->x += xmove;\r
+               }\r
+               else\r
+               {\r
+                       ob->x -= xmove;\r
+               }\r
+               CalcBounds (ob);\r
+               xl = ob->xl>>TILESHIFT;\r
+               yl = ob->yl>>TILESHIFT;\r
+               xh = ob->xh>>TILESHIFT;\r
+               yh = ob->yh>>TILESHIFT;\r
+               if (tilemap[xl][yl] || tilemap[xh][yl]\r
+               || tilemap[xh][yh] || tilemap[xl][yh] )\r
+               {\r
+                       moveok = false;\r
+                       if (xmove>=-2048 && xmove <=2048)\r
+                       {\r
+                               ob->x = basex;\r
+                               ob->y = basey;\r
+                               return;\r
+                       }\r
+               }\r
+               else\r
+                       if (invisible_present)\r
+                       {\r
+                               moveok = false;\r
+                               if (xmove>=-2048 && xmove <=2048)\r
+                               {\r
+                                       ob->x = basex;\r
+                                       ob->y = basey;\r
+                                       return;\r
+                               }\r
+                       }\r
+                       else\r
+                               if (xmove>=-2048 && xmove <=2048)\r
+                                       return;\r
+                               moveok = true;\r
+       } while (1);\r
+}\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= ClipYMove\r
+=\r
+= Only checks corners, so the object better be less than one tile wide!\r
+=\r
+===================\r
+*/\r
+void ClipYMove (objtype *ob, long ymove)\r
+{\r
+       int                     xl,yl,xh,yh,tx,ty,nt1,nt2,x,y;\r
+       long            intersect,basex,basey,pointx,pointy;\r
+       unsigned        inside,total,tile;\r
+       objtype         *check;\r
+       boolean         moveok;\r
+       boolean         invisible_present = false;\r
+\r
+//\r
+// move player and check to see if any corners are in solid tiles\r
+//\r
+       basex = ob->x;\r
+       basey = ob->y;\r
+\r
+       ob->y += ymove;\r
+\r
+       CalcBounds (ob);\r
+\r
+       xl = ob->xl>>TILESHIFT;\r
+       yl = ob->yl>>TILESHIFT;\r
+\r
+       xh = ob->xh>>TILESHIFT;\r
+       yh = ob->yh>>TILESHIFT;\r
+\r
+       for (y=yl;y<=yh;y++)\r
+               for (x=xl;x<=xh;x++)\r
+               {\r
+                       check = actorat[x][y];\r
+                       if (!check)\r
+                               continue;               // blank floor, walk ok\r
+\r
+                       if ((unsigned)check <= LASTTILE)\r
+                       {\r
+                               if (TILE_FLAGS((unsigned)check) & tf_SPECIAL)           // <=LASTSPECIALTILE)\r
+                               {\r
+                                       HitSpecialTile (x,y,(unsigned)check-SPECTILESTART);\r
+                                       goto blockmove;\r
+                               }\r
+\r
+                               if (TILE_FLAGS((unsigned)check) & tf_INVISIBLE_WALL)\r
+                               {\r
+                                       invisible_present = true;\r
+                                       goto blockmove;\r
+                               }\r
+\r
+\r
+                               if (TILE_FLAGS((unsigned)check) & tf_SOLID)             // LASTWALLTILE)\r
+                               {\r
+                                       goto blockmove; // solid wall\r
+                               }\r
+                       }\r
+\r
+                       TouchActor(ob,check);           // pick up items\r
+               }\r
+\r
+//\r
+// check nearby actors\r
+//\r
+       if (LocationInActor(ob))\r
+       {\r
+               if (LocationInActor(ob))\r
+               {\r
+                       ob->y -= ymove;\r
+               }\r
+       }\r
+       return;         // move is OK!\r
+\r
+\r
+blockmove:\r
+\r
+//     if (!SD_SoundPlaying())\r
+//             SD_PlaySound (HITWALLSND);\r
+\r
+       moveok = false;\r
+\r
+       do\r
+       {\r
+               ymove /= 2;\r
+               if (moveok)\r
+               {\r
+                       ob->y += ymove;\r
+               }\r
+               else\r
+               {\r
+                       ob->y -= ymove;\r
+               }\r
+               CalcBounds (ob);\r
+               xl = ob->xl>>TILESHIFT;\r
+               yl = ob->yl>>TILESHIFT;\r
+               xh = ob->xh>>TILESHIFT;\r
+               yh = ob->yh>>TILESHIFT;\r
+               if (tilemap[xl][yl] || tilemap[xh][yl]\r
+               || tilemap[xh][yh] || tilemap[xl][yh] )\r
+               {\r
+                       moveok = false;\r
+                       if (ymove>=-2048 && ymove <=2048)\r
+                       {\r
+                               ob->x = basex;\r
+                               ob->y = basey;\r
+                               return;\r
+                       }\r
+               }\r
+               else\r
+                       if (invisible_present)\r
+                       {\r
+                               moveok = false;\r
+                               if (ymove>=-2048 && ymove <=2048)\r
+                               {\r
+                                       ob->x = basex;\r
+                                       ob->y = basey;\r
+                                       return;\r
+                               }\r
+                       }\r
+                       else\r
+                               if (ymove>=-2048 && ymove <=2048)\r
+                                       return;\r
+                               moveok = true;\r
+       } while (1);\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= ShotClipMove\r
+=\r
+= Only checks corners, so the object better be less than one tile wide!\r
+=\r
+===================\r
+*/\r
+\r
+boolean ShotClipMove (objtype *ob, long xmove, long ymove)\r
+{\r
+       int                     xl,yl,xh,yh,tx,ty,nt1,nt2,x,y;\r
+       long            intersect,basex,basey,pointx,pointy;\r
+       unsigned        inside,total,spot,tile;\r
+       objtype         *check;\r
+       boolean         moveok;\r
+\r
+//\r
+// move shot and check to see if any corners are in solid tiles\r
+//\r
+       basex = ob->x;\r
+       basey = ob->y;\r
+\r
+       ob->x += xmove;\r
+       ob->y += ymove;\r
+\r
+       CalcBounds (ob);\r
+\r
+       xl = ob->xl>>TILESHIFT;\r
+       yl = ob->yl>>TILESHIFT;\r
+\r
+       xh = ob->xh>>TILESHIFT;\r
+       yh = ob->yh>>TILESHIFT;\r
+\r
+       for (y=yl;y<=yh;y++)\r
+               for (x=xl;x<=xh;x++)\r
+               {\r
+                       spot = (*(mapsegs[2]+farmapylookup[y]+x)) >> 8;\r
+                       if (spot == EXP_WALL_CODE)\r
+                               switch (ob->obclass)\r
+                               {\r
+                                       case pshotobj:\r
+                                       case bigpshotobj:\r
+                                               ExplodeWall (x,y);\r
+                                               goto blockmove;\r
+                                       break;\r
+                               }\r
+\r
+                       tile = *(mapsegs[0]+farmapylookup[y]+x);\r
+                       if (TILE_FLAGS(tile) & tf_SOLID)\r
+                               goto blockmove;\r
+               }\r
+       return false;           // move is OK!\r
+\r
+\r
+blockmove:\r
+\r
+       SD_PlaySound (SHOOTWALLSND);\r
+\r
+       moveok = false;\r
+\r
+       do\r
+       {\r
+               xmove /= 2;\r
+               ymove /= 2;\r
+               if (moveok)\r
+               {\r
+                       ob->x += xmove;\r
+                       ob->y += ymove;\r
+               }\r
+               else\r
+               {\r
+                       ob->x -= xmove;\r
+                       ob->y -= ymove;\r
+               }\r
+               CalcBounds (ob);\r
+               xl = ob->xl>>TILESHIFT;\r
+               yl = ob->yl>>TILESHIFT;\r
+               xh = ob->xh>>TILESHIFT;\r
+               yh = ob->yh>>TILESHIFT;\r
+               if (tilemap[xl][yl] || tilemap[xh][yl]\r
+               || tilemap[xh][yh] || tilemap[xl][yh] )\r
+               {\r
+                       moveok = false;\r
+                       if (xmove>=-2048 && xmove <=2048 && ymove>=-2048 && ymove <=2048)\r
+                       {\r
+                               ob->x = basex;\r
+                               ob->y = basey;\r
+                               return true;\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       if (xmove>=-2048 && xmove <=2048 && ymove>=-2048 && ymove <=2048)\r
+                               return true;\r
+                       moveok = true;\r
+               }\r
+       } while (1);\r
+}\r
+\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                       PLAYER CONTROL\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+\r
+void   T_Player (objtype *ob);\r
+\r
+statetype s_player = {0,0,&T_Player,&s_player};\r
+\r
+/*\r
+===============\r
+=\r
+= SpawnPlayer\r
+=\r
+===============\r
+*/\r
+\r
+void SpawnPlayer (int tilex, int tiley, int dir)\r
+{\r
+#if 0\r
+       levelinfo *li=&gamestate.levels[gamestate.mapon];\r
+\r
+       if (li->x != -1)\r
+       {\r
+               tilex = li->x;\r
+               tiley = li->y;\r
+               player->angle = li->angle;\r
+       }\r
+       else\r
+               player->angle = (1-dir)*90;\r
+#endif\r
+\r
+       player->obclass = playerobj;\r
+       player->active = always;\r
+       player->tilex = tilex;\r
+       player->tiley = tiley;\r
+       player->x = ((long)tilex<<TILESHIFT)+TILEGLOBAL/2;\r
+       player->y = ((long)tiley<<TILESHIFT)+TILEGLOBAL/2;\r
+       player->state = &s_player;\r
+       player->size = MINDIST;\r
+       CalcBounds(player);\r
+       player->angle = (1-dir)*90;\r
+       if (player->angle<0)\r
+               player->angle += ANGLES;\r
+}\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= Thrust\r
+=\r
+===================\r
+*/\r
+\r
+void Thrust (int angle, unsigned speed)\r
+{\r
+       long xmove,ymove;\r
+\r
+       if (lasttimecount>>5 != ((lasttimecount-tics)>>5) )\r
+       {\r
+       //\r
+       // walk sound\r
+       //\r
+               if (lasttimecount&32)\r
+                       SD_PlaySound (WALK1SND);\r
+               else\r
+                       SD_PlaySound (WALK2SND);\r
+       }\r
+\r
+       xmove = FixedByFrac(speed,costable[angle]);\r
+       ymove = -FixedByFrac(speed,sintable[angle]);\r
+\r
+       ClipXMove(player,xmove);\r
+       ClipYMove(player,ymove);\r
+       player->tilex = player->x >> TILESHIFT;\r
+       player->tiley = player->y >> TILESHIFT;\r
+}\r
+\r
+\r
+\r
+/*\r
+=======================\r
+=\r
+= ControlMovement\r
+=\r
+=======================\r
+*/\r
+\r
+void ControlMovement (objtype *ob)\r
+{\r
+       int     angle;\r
+       long    speed;\r
+\r
+\r
+       if (control.button1)\r
+       {\r
+       //\r
+       // strafing\r
+       //\r
+               //\r
+               // side to side move\r
+               //\r
+               if (!mousexmove)\r
+                       speed = 0;\r
+               else if (mousexmove<0)\r
+                       speed = -(long)mousexmove*300;\r
+               else\r
+                       speed = -(long)mousexmove*300;\r
+\r
+               if (control.xaxis == -1)\r
+               {\r
+                       speed += PLAYERSPEED*tics;\r
+               }\r
+               else if (control.xaxis == 1)\r
+               {\r
+                       speed -= PLAYERSPEED*tics;\r
+               }\r
+\r
+               if (speed > 0)\r
+               {\r
+                       if (speed >= TILEGLOBAL)\r
+                               speed = TILEGLOBAL-1;\r
+                       angle = ob->angle + ANGLES/4;\r
+                       if (angle >= ANGLES)\r
+                               angle -= ANGLES;\r
+                       Thrust (angle,speed);                           // move to left\r
+               }\r
+               else if (speed < 0)\r
+               {\r
+                       if (speed <= -TILEGLOBAL)\r
+                               speed = -TILEGLOBAL+1;\r
+                       angle = ob->angle - ANGLES/4;\r
+                       if (angle < 0)\r
+                               angle += ANGLES;\r
+                       Thrust (angle,-speed);                          // move to right\r
+               }\r
+       }\r
+       else\r
+       {\r
+       //\r
+       // not strafing\r
+       //\r
+\r
+               //\r
+               // turning\r
+               //\r
+               if (control.xaxis == 1)\r
+               {\r
+                       ob->angle -= tics;\r
+                       if (running)                            // fast turn\r
+                               ob->angle -= (tics<<1);\r
+               }\r
+               else if (control.xaxis == -1)\r
+               {\r
+                       ob->angle+= tics;\r
+                       if (running)                            // fast turn\r
+                               ob->angle += (tics<<1);\r
+               }\r
+\r
+               ob->angle -= (mousexmove/10);\r
+\r
+               if (ob->angle >= ANGLES)\r
+                       ob->angle -= ANGLES;\r
+               if (ob->angle < 0)\r
+                       ob->angle += ANGLES;\r
+\r
+       }\r
+\r
+       //\r
+       // forward/backwards move\r
+       //\r
+       if (!mouseymove)\r
+               speed = 0;\r
+       else if (mouseymove<0)\r
+               speed = -(long)mouseymove*500;\r
+       else\r
+               speed = -(long)mouseymove*200;\r
+\r
+       if (control.yaxis == -1)\r
+       {\r
+               speed += PLAYERSPEED*tics;\r
+       }\r
+       else if (control.yaxis == 1)\r
+       {\r
+               speed -= PLAYERSPEED*tics;\r
+       }\r
+\r
+       if (speed > 0)\r
+       {\r
+               if (speed >= TILEGLOBAL)\r
+                       speed = TILEGLOBAL-1;\r
+               Thrust (ob->angle,speed);                       // move forwards\r
+       }\r
+       else if (speed < 0)\r
+       {\r
+               if (speed <= -TILEGLOBAL)\r
+                       speed = -TILEGLOBAL+1;\r
+               angle = ob->angle + ANGLES/2;\r
+               if (angle >= ANGLES)\r
+                       angle -= ANGLES;\r
+               Thrust (angle,-speed);                          // move backwards\r
+       }\r
+}\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= T_Player\r
+=\r
+===============\r
+*/\r
+\r
+void   T_Player (objtype *ob)\r
+{\r
+       extern boolean autofire;\r
+\r
+       int     angle,speed,scroll,loop;\r
+       unsigned        text,tilex,tiley;\r
+       long    lspeed;\r
+\r
+//     boolean radar_moved=false;\r
+\r
+\r
+       ControlMovement (ob);\r
+\r
+\r
+       //\r
+       // firing\r
+       //\r
+       if (boltsleft)\r
+       {\r
+               handheight+=(realtics<<2);\r
+               if (handheight>MAXHANDHEIGHT)\r
+                       handheight = MAXHANDHEIGHT;\r
+\r
+               ContinueBolt ();\r
+               lasthand = lasttimecount;\r
+       }\r
+       else\r
+       {\r
+               if (control.button0)\r
+               {\r
+                       handheight+=(realtics<<2);\r
+                       if (handheight>MAXHANDHEIGHT)\r
+                               handheight = MAXHANDHEIGHT;\r
+                       lasthand = lasttimecount;\r
+\r
+                       if (!button0down)\r
+                               Shoot();\r
+\r
+                       if (!autofire)\r
+                               button0down=true;\r
+               }\r
+               else\r
+               {\r
+                       if (lasttimecount > lasthand+HANDPAUSE)\r
+                       {\r
+                               handheight-=(realtics<<1);\r
+                               if (handheight<0)\r
+                                       handheight = 0;\r
+                       }\r
+\r
+                       button0down = false;\r
+               }\r
+}\r
+\r
+#if 0\r
+               if (control.button0)\r
+               {\r
+                       handheight+=(realtics<<2);\r
+                       if (handheight>MAXHANDHEIGHT)\r
+                               handheight = MAXHANDHEIGHT;\r
+\r
+                       if ((unsigned)TimeCount/FIRETIME != lastfiretime)\r
+                               BuildShotPower ();\r
+                       lasthand = lasttimecount;\r
+               }\r
+               else\r
+               {\r
+                       if (lasttimecount > lasthand+HANDPAUSE)\r
+                       {\r
+                               handheight-=(realtics<<1);\r
+                               if (handheight<0)\r
+                                       handheight = 0;\r
+                       }\r
+\r
+                       if (gamestate.shotpower)\r
+                       {\r
+                               lastfiretime = (unsigned)TimeCount/FIRETIME;\r
+                               Shoot ();\r
+                       }\r
+               }\r
+       }\r
+#endif\r
+\r
+       //\r
+       // special actions\r
+       //\r
+\r
+       if ((Keyboard[sc_Space] || Keyboard[sc_C]) && gamestate.body != MAXBODY)\r
+               DrinkPotion ();\r
+\r
+       if (Keyboard[sc_Z] && !boltsleft)\r
+               CastBolt ();\r
+\r
+       if ( (Keyboard[sc_Enter] || Keyboard[sc_X]) && ((TimeCount-lastnuke > NUKETIME) || (autofire)))\r
+               CastNuke ();\r
+\r
+#if 0\r
+       scroll = LastScan-2;\r
+       if ( scroll>=0 && scroll<NUMSCROLLS && gamestate.scrolls[scroll])\r
+               ReadScroll (scroll);\r
+#endif\r
+\r
+       DrawText(false);\r
+       DrawHealth();\r
+       if (FreezeTime)\r
+               DrawFreezeTime();\r
+       DrawRadar();\r
+       EGAWRITEMODE(0);\r
+       DrawNSEWIcons();\r
+\r
+       if (redraw_gems)\r
+               DrawGems();\r
+\r
+#if 0\r
+// gems fade out over time...\r
+//\r
+       for (loop=0; loop<5; loop++)\r
+               if (gamestate.gems[loop])\r
+               {\r
+                       gamestate.gems[loop] -= realtics;\r
+                       if (gamestate.gems[loop] < 0)\r
+                       {\r
+                               gamestate.gems[loop] = 0;\r
+                               redraw_gems = true;\r
+                       }\r
+               }\r
+#endif\r
+}\r
+\r
+#if 0\r
+//------------------------------------------------------------------------\r
+// FaceDir() -\r
+//\r
+// PARAMS : x,y - pixle coords to bring in to view.\r
+//\r
+// NOTE : Params CAN NOT be shifted fracs!\r
+//------------------------------------------------------------------------\r
+void FaceDir(short x,short y,boolean StopTime)\r
+{\r
+       short diff;\r
+\r
+       RotateAngle = CalcAngle(x-(player->x>>16l),(player->y>>16l)-y);\r
+       FreezeTime = StopTime;\r
+\r
+       diff = player->angle - RotateAngle;\r
+\r
+       if (((diff>0) && (diff<180)) || ((diff<0) && (diff>-180)))\r
+               RotateSpeed = -ROTATE_SPEED;\r
+       else\r
+               RotateSpeed = ROTATE_SPEED;\r
+}\r
+#endif\r
+\r
+#if 0\r
+//------------------------------------------------------------------------\r
+// CalcAngle() -\r
+//\r
+// DESC: Calculates the angle from a given dy & dx\r
+//------------------------------------------------------------------------\r
+short CalcAngle(short dx,short dy)\r
+{\r
+       #define degtorad                                (180/PI)\r
+       float angle;\r
+       short diff;\r
+       float rad_angle;\r
+\r
+       if (dx)\r
+       {\r
+               angle = atan((float)dy/dx)* degtorad;\r
+               if (angle<=0)\r
+                       angle += 180;\r
+               if (dy>0)\r
+                       angle += 180;\r
+       }\r
+       else\r
+       {\r
+               // 90 Deg shift\r
+\r
+               if (dy < 0)\r
+                       angle = 0 + 90;                         // Above player (NORTH)\r
+               else\r
+                       angle = 180 + 90;                       // Below player (SOUTH)\r
+       }\r
+\r
+       if (!angle)                             // HACK\r
+               angle++;\r
+\r
+       return((short)abs(angle));\r
+}\r
+\r
+#endif\r
+\r
+#if 0\r
+\r
+//-------------------------------------------------------------------------\r
+// RotateView() -\r
+//\r
+// DESC : Rotates view (current view of game) to a dest angle.\r
+//-------------------------------------------------------------------------\r
+void RotateView()\r
+{\r
+       short LastPos;\r
+\r
+       // Store old angle position then change angle...\r
+       //\r
+\r
+       LastPos = player->angle;\r
+\r
+       player->angle += RotateSpeed;\r
+\r
+       // Check to see if we cranked past out dest angle...\r
+       //\r
+\r
+\r
+       if ((player->angle>ANGLES) || (!player->angle))\r
+               player->angle = 1;\r
+       else\r
+       if (player->angle<1)\r
+               player->angle = ANGLES;\r
+\r
+       // Check to see if we over shot our dest angle...\r
+       //\r
+\r
+       if (((LastPos < RotateAngle) && (player->angle > RotateAngle) && (RotateSpeed > 0)) ||\r
+               ((LastPos > RotateAngle) && (player->angle < RotateAngle) && (RotateSpeed < 0)))\r
+               player->angle = RotateAngle;\r
+\r
+       // Check for ending force turn....\r
+       //\r
+\r
+       if (player->angle == RotateAngle)\r
+               RotateAngle = -1;\r
+\r
+}\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+// InitRotate()\r
+//--------------------------------------------------------------------------\r
+void InitRotate(short DestAngle)\r
+{\r
+       if (player->angle != DestAngle)\r
+       {\r
+               RotateAngle = DestAngle;\r
+\r
+               if (player->angle > DestAngle)\r
+                       RotateSpeed = -ROTATE_SPEED;\r
+               else\r
+                       RotateSpeed = ROTATE_SPEED;\r
+\r
+               if (abs(player->angle - RotateAngle) > 180)\r
+                       RotateSpeed =- RotateSpeed;\r
+       }\r
+}\r
+\r
+\r
+\r
+//------------------------------------------------------------------------\r
+// FaceAngle() -\r
+//\r
+// PARAMS : DestAngle - Destination angle to turn to\r
+//------------------------------------------------------------------------\r
+void FaceAngle(short DestAngle)\r
+{\r
+       signed long dx,dy,radius,psin,pcos,newx,newy;\r
+       int             give;\r
+       short objnum,LastPos;\r
+       signed long ox,oy,xl,xh,yl,yh,px,py,norm_dx,norm_dy;\r
+       short o_radius;\r
+       void (*think)();\r
+\r
+\r
+       // Calculate the direction we want to turn to...\r
+       //\r
+\r
+       InitRotate(DestAngle);\r
+\r
+       RedrawStatusWindow();\r
+\r
+       while (RotateAngle != -1)\r
+       {\r
+\r
+               RotateView();\r
+\r
+//             PollControls();\r
+\r
+               objnum=0;\r
+\r
+               for (obj = player;obj;obj = obj->next)\r
+               {\r
+                       if (obj->active >= yes)\r
+                       {\r
+\r
+                       // keep a list of objects around the player for radar updates\r
+                       //\r
+                               if (obj == player)\r
+                               {\r
+                                       px = player->x;\r
+                                       py = player->y;\r
+                                       psin = sintable[player->angle];\r
+                                       pcos = costable[player->angle];\r
+                                       xl = px-((long)RADAR_WIDTH<<TILESHIFT)/2;\r
+                                       xh = px+((long)RADAR_WIDTH<<TILESHIFT)/2-1;\r
+                                       yl = py-((long)RADAR_HEIGHT<<TILESHIFT)/2;\r
+                                       yh = py+((long)RADAR_HEIGHT<<TILESHIFT)/2;\r
+                               }\r
+\r
+                               if (objnum > MAX_RADAR_BLIPS-2)\r
+                                       objnum = MAX_RADAR_BLIPS-2;\r
+\r
+                               ox = obj->x;\r
+                               oy = obj->y;\r
+\r
+\r
+                               if ((ox >= xl) && (ox <= xh) && (oy >= yl) && (oy <= yh))\r
+                               {\r
+                                       norm_dx = (dx = px-ox)>>TILESHIFT;\r
+                                       norm_dy = (dy = oy-py)>>TILESHIFT;\r
+\r
+                                       o_radius = IntSqrt((norm_dx * norm_dx) + (norm_dy * norm_dy));\r
+\r
+                                       if (o_radius < RADAR_RADIUS)\r
+                                       {\r
+                                               newx = FixedByFrac(dy,pcos)-FixedByFrac(dx,psin);\r
+                                               newy = FixedByFrac(dy,psin)+FixedByFrac(dx,pcos);\r
+\r
+                                               RadarXY[objnum][0]=newx>>TILESHIFT;\r
+                                               RadarXY[objnum][1]=newy>>TILESHIFT;\r
+\r
+                                               // Define color to use for this object...\r
+                                               //\r
+\r
+                                               switch (obj->obclass)\r
+                                               {\r
+                                                       case playerobj:\r
+                                                               RadarXY[objnum++][2]=15;\r
+                                                       break;\r
+\r
+                                       // RED GEM\r
+                                       //\r
+                                                       case godessobj:\r
+                                                               if (gamestate.gems[B_RGEM-B_RGEM])\r
+                                                                       if (obj->active == always)\r
+                                                                               RadarXY[objnum++][2]=12;\r
+                                                       break;\r
+\r
+                                       // GREEN GEM\r
+                                       //\r
+                                                       case fatdemonobj:\r
+                                                               if (gamestate.gems[B_GGEM-B_RGEM])\r
+                                                                       if (obj->active == always)\r
+                                                                               RadarXY[objnum++][2]=10;\r
+                                                       break;\r
+\r
+                                       // YELLOW GEM\r
+                                       //\r
+                                                       case skeletonobj:\r
+                                                               if (gamestate.gems[B_YGEM-B_RGEM])\r
+                                                                       if (obj->active == always)\r
+                                                                               RadarXY[objnum++][2]=14;\r
+                                                       break;\r
+\r
+                                       // BLUE GEM\r
+                                       //\r
+                                                       case mageobj:\r
+                                                       case wetobj:\r
+                                                               if (gamestate.gems[B_BGEM-B_RGEM])\r
+                                                                       if (obj->active == always)\r
+                                                                               RadarXY[objnum++][2]=9;\r
+                                                       break;\r
+\r
+                                       // PURPLE GEM\r
+                                       //\r
+                                                       case zombieobj:\r
+                                                               if (gamestate.gems[B_PGEM-B_RGEM])\r
+                                                                       if (obj->active == always)\r
+                                                                               RadarXY[objnum++][2]=13;\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+\r
+               RadarXY[objnum][2]=-1;          // Signals end of RadarXY list...\r
+\r
+// refresh all\r
+//\r
+\r
+               ThreeDRefresh();\r
+               DrawRadar();\r
+               EGAWRITEMODE(0);\r
+               DrawNSEWIcons();\r
+\r
+//             CheckKeys();\r
+       }\r
+}\r
+\r
+\r
+//-------------------------------------------------------------------------\r
+// FaceDoor() - Turns the player to face a door (a tile) at a given TILE x & y\r
+//\r
+// RETURNS : Returns the orginal angle of the player.\r
+//------------------------------------------------------------------------\r
+short FaceDoor(short x, short y)\r
+{\r
+       short p_x,p_y,angle,old_angle;\r
+\r
+       old_angle = player->angle;\r
+\r
+       p_x = player->x>>16l;\r
+       p_y = player->y>>16l;\r
+\r
+       if (p_x != x)\r
+       {\r
+               if (p_x > x)\r
+                       angle = 180;            // Face Left\r
+               else\r
+                       angle = 1;                      // Face Right\r
+       }\r
+\r
+       if (p_y != y)\r
+       {\r
+               if (p_y > y)\r
+                       angle = 90;                     // Face Up\r
+               else\r
+                       angle = 270;            // Face Down\r
+       }\r
+\r
+       FaceAngle(angle);\r
+\r
+       return(old_angle);\r
+}\r
+\r
+\r
+#endif\r
+\r
+\r
+\r
+/*==========================================================================\r
+\r
+                                                               EXPLOSION SPAWNING ROUTINES\r
+\r
+===========================================================================*/\r
+\r
+statetype s_explode = {0,1,T_ExpThink,&s_explode};\r
+\r
+//-------------------------------------------------------------------------\r
+// SpawnExplosion()\r
+//------------------------------------------------------------------------\r
+void SpawnExplosion(fixed x, fixed y, short Delay)\r
+{\r
+       DSpawnNewObjFrac(x,y,&s_explode,PIXRADIUS*7);\r
+       new->obclass = expobj;\r
+       new->active = always;\r
+       new->temp1 = Delay;\r
+}\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// T_ExpThink()\r
+//---------------------------------------------------------------------------\r
+void T_ExpThink(objtype *obj)\r
+{\r
+       if (obj->temp1)\r
+       {\r
+               if ((obj->temp1-=realtics) <= 0)\r
+                       obj->temp1 = 0;\r
+       }\r
+       else\r
+       {\r
+               obj->state = &s_pshot_exp1;\r
+               obj->ticcount = obj->state->tictime;\r
+               SD_PlaySound(BOOMSND);\r
+       }\r
+}\r
+\r
+\r
+\r
+//-------------------------------------------------------------------------\r
+// SpawnBigExplosion()\r
+//------------------------------------------------------------------------\r
+void SpawnBigExplosion(fixed x, fixed y, short Delay, fixed Range)\r
+{\r
+       SpawnExplosion(x-random(Range),y+random(Range),random(Delay));\r
+       SpawnExplosion(x+random(Range),y-random(Range),random(Delay));\r
+       SpawnExplosion(x-random(Range),y-random(Range),random(Delay));\r
+       SpawnExplosion(x+random(Range),y+random(Range),random(Delay));\r
+}\r
+\r
diff --git a/16/cawat/COPYING b/16/cawat/COPYING
new file mode 100644 (file)
index 0000000..d159169
--- /dev/null
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/16/cawat/DEF.H b/16/cawat/DEF.H
new file mode 100644 (file)
index 0000000..63c5a05
--- /dev/null
@@ -0,0 +1,865 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#include "ID_HEADS.H"\r
+#include <MATH.H>\r
+#include <VALUES.H>\r
+\r
+//#define PROFILE\r
+\r
+#define DEBUG_KEYS_AVAILABLE 0\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                GLOBAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+//\r
+// SOFTLIB GFX FILENAME\r
+//\r
+#define        SLIB_GFX                        "ARM_SLIB."EXT\r
+\r
+\r
+#define USE_INERT_LIST false\r
+\r
+\r
+//\r
+// MAX_BASE - represents 100 percent in 1st base\r
+// MAX_PERC - represents 100 percent in 2nd base\r
+// PERC - fractional portion of 2nd base\r
+// SCALE - arbitrary scaling value (bigger number means more accurate)\r
+//\r
+// ex: PERCENTAGE(320,16,8,7)    returns  160\r
+//\r
+// Make sure values used won't overflow a WORD! In general, if largest number\r
+// to be used (320 in ex: above) * (1<<SCALE) is greater than 65535, use\r
+// LONG_PERCENTAGE or a lower SCALE. Using a SCALE of 8 in the example\r
+// above would overflow a WORD in some circumstances!\r
+//\r
+// LONG_PERCENTAGE is to be used for larger SCALEs, thus, giving you\r
+// massive accuracy!\r
+//\r
+#define PERCENTAGE(MAX_BASE,MAX_PERC,PERC,SCALE) ((unsigned)(MAX_BASE*((PERC<<SCALE)/MAX_PERC))>>SCALE)\r
+#define LONG_PERCENTAGE(MAX_BASE,MAX_PERC,PERC,SCALE) (((long)MAX_BASE*(((long)PERC<<SCALE)/MAX_PERC))>>SCALE)\r
+\r
+#define PI     3.141592657\r
+\r
+//#define SIZE_TEST            65000\r
+#define SIZE_TEST              0\r
+\r
+\r
+#define FL_QUICK                       0x01\r
+#define FL_NOMEMCHECK  0x02\r
+#define FL_HELPTEST            0x04\r
+\r
+#define FL_CLEAR               (FL_QUICK|FL_NOMEMCHECK|FL_HELPTEST)\r
+\r
+#if 0\r
+#define GEM_SHIFT              2\r
+#define FL_RGEM                0x04\r
+#define FL_GGEM                0x08\r
+#define FL_BGEM                0x10\r
+#define FL_YGEM                0x20\r
+#define FL_PGEM                0x40\r
+#endif\r
+\r
+#define FL_DEAD                0x80\r
+\r
+\r
+\r
+#define MAXBOLTS               10\r
+#define MAXNUKES               10\r
+#define MAXPOTIONS     10\r
+\r
+#define NUKE_COST              (1000)\r
+#define BOLT_COST    (1200)\r
+#define POTION_COST    (1300)\r
+\r
+#define NUKE_COST_TXT          ("1000")                // Allows for Q&D positioning..\r
+#define BOLT_COST_TXT          ("1200")\r
+#define POTION_COST_TXT                ("1300")\r
+\r
+#define RADARX 31                                                                                                                      // bytes\r
+#define RADARY 11                                           // pixels\r
+#define RADAR_WIDTH     51                                  //   "\r
+#define RADAR_HEIGHT           51                                  //   "\r
+#define RADAR_XCENTER  ((RADARX*8)+(RADAR_WIDTH/2)+3)      //   "\r
+#define RADAR_YCENTER  ((RADARY-8)+(RADAR_HEIGHT/2)+5)       //   "\r
+#define MAX_RADAR_BLIPS        60\r
+\r
+\r
+#define RADAR_RADIUS                   17\r
+#define RADAR_RADIUS_NSEW      15\r
+#define RADAR_X_IRADIUS                (113/5)\r
+#define RADAR_Y_IRADIUS                (113/7)\r
+#define RADAR_ICON_CENTER      4                                               // Center offset into icon.\r
+\r
+#define NAMESTART      180\r
+#define REMOVED_DOOR_TILE      NAMESTART\r
+\r
+#define NEXT_LEVEL_CODE                0xff\r
+#define REMOVE_DOOR_CODE       0xfe\r
+#define CANT_OPEN_CODE         0xfd\r
+#define EXP_WALL_CODE          0xfc\r
+\r
+\r
+#define UNMARKGRCHUNK(chunk)   (grneeded[chunk]&=~ca_levelbit)\r
+\r
+#define MOUSEINT       0x33\r
+\r
+#define EXPWALLSTART   8\r
+#define NUMEXPWALLS    7\r
+#define WALLEXP                59\r
+#define WATEREXP       62\r
+#define NUMFLOORS      80      //71\r
+\r
+#define NUMLATCHPICS    (FIRSTWALLPIC-FIRSTLATCHPIC) //+5\r
+#define NUMSCALEPICS    (FIRSTWALLPIC-FIRSTSCALEPIC) //+5\r
+#define NUMSCALEWALLS (LASTWALLPIC-FIRSTWALLPIC) //+5\r
+\r
+\r
+#define FLASHCOLOR     12\r
+#define FLASHTICS      4\r
+\r
+\r
+#define NUMLEVELS      32              //21\r
+\r
+#define VIEWX          0               // corner of view window\r
+#define VIEWY          0\r
+#define VIEWWIDTH      (40*8)          // size of view window                          // 33\r
+#define VIEWHEIGHT     (15*8)                                                                                          // 18\r
+#define VIEWXH         (VIEWX+VIEWWIDTH-1)\r
+#define VIEWYH         (VIEWY+VIEWHEIGHT-1)\r
+\r
+#define CENTERX                (VIEWX+VIEWWIDTH/2-1)   // middle of view window\r
+#define CENTERY                (VIEWY+VIEWHEIGHT/2-1)\r
+\r
+#define GLOBAL1                (1l<<16)\r
+#define TILEGLOBAL  GLOBAL1\r
+#define TILESHIFT      16l\r
+\r
+#define MINDIST                (2*GLOBAL1/5)\r
+#define FOCALLENGTH    (TILEGLOBAL)    // in global coordinates\r
+\r
+#define ANGLES         360             // must be divisable by 4\r
+\r
+#define MAPSIZE                64              // maps are 64*64 max\r
+#define MAXACTORS      100             // max number of tanks, etc / map\r
+\r
+#define NORTH  0\r
+#define EAST   1\r
+#define SOUTH  2\r
+#define WEST   3\r
+\r
+#define SIGN(x) ((x)>0?1:-1)\r
+#define ABS(x) ((int)(x)>0?(x):-(x))\r
+#define LABS(x) ((long)(x)>0?(x):-(x))\r
+\r
+#define        MAXSCALE        (VIEWWIDTH/2)\r
+\r
+\r
+#define MAXBODY                100\r
+#define MAXSHOTPOWER   56\r
+\r
+#define SCREEN1START   0\r
+#define SCREEN2START   8320\r
+\r
+#define STATUSLEN                      0xc80\r
+#define PAGELEN                        0x1700\r
+\r
+#define PAGE1START     STATUSLEN\r
+#define PAGE2START     (PAGE1START+PAGELEN)\r
+#define PAGE3START     (PAGE2START+PAGELEN)\r
+#define FREESTART              (PAGE3START+PAGELEN)\r
+\r
+#define PIXRADIUS              512\r
+\r
+#define STATUSLINES            (200-VIEWHEIGHT)\r
+\r
+enum bonusnumbers {B_BOLT,B_NUKE,B_POTION,B_RKEY,B_YKEY,B_GKEY,B_BKEY,B_SCROLL1,\r
+ B_SCROLL2,B_SCROLL3,B_SCROLL4,B_SCROLL5,B_SCROLL6,B_SCROLL7,B_SCROLL8,\r
+ B_GOAL,B_CHEST,B_RGEM,B_GGEM,B_BGEM,B_YGEM,B_PGEM};\r
+\r
+\r
+#define MAX_DOOR_STORAGE 5\r
+\r
+#define GEM_DELAY_TIME (120*60)\r
+\r
+#define ROTATE_SPEED           (6)\r
+\r
+#define WALL_SKELETON_CODE 6\r
+\r
+#define MAXREALTICS (2*60)\r
+\r
+#define MAXFREEZETIME                  (100*30)                                // 50 secs (100 half)\r
+\r
+#define INVISIBLEWALL                  0x46\r
+\r
+#define USE_STRIPS             FALSE\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                  GLOBAL TYPES\r
+\r
+=============================================================================\r
+*/\r
+\r
+enum {BLANKCHAR=9,BOLTCHAR,NUKECHAR,POTIONCHAR,KEYCHARS,SCROLLCHARS=17,\r
+       NUMBERCHARS=25};\r
+\r
+typedef long fixed;\r
+\r
+typedef struct {int x,y;} tilept;\r
+typedef struct {fixed x,y;} globpt;\r
+\r
+typedef struct\r
+{\r
+  int  x1,x2,leftclip,rightclip;// first pixel of wall (may not be visable)\r
+  unsigned     height1,height2,color,walllength,side;\r
+       long    planecoord;\r
+} walltype;\r
+\r
+typedef enum\r
+  {nothing,playerobj,bonusobj,succubusobj,batobj,skeletonobj,fatdemonobj,godessobj,\r
+  mageobj,pshotobj,bigpshotobj,mshotobj,inertobj,bounceobj,grelmobj,sshotobj,\r
+  gateobj,zombieobj,antobj,wetobj,expobj,eyeobj,wallskelobj,eshotobj,treeobj,\r
+  gshotobj,reddemonobj,freezeobj,solidobj,cloudobj,dshotobj,hbunnyobj,bunnyobj,\r
+  realsolidobj} classtype;\r
+\r
+typedef enum {north,east,south,west,northeast,southeast,southwest,\r
+                 northwest,nodir} dirtype;             // a catacombs 2 carryover\r
+\r
+typedef struct statestruct\r
+{\r
+       int             shapenum;\r
+       int             tictime;\r
+       void    (*think) ();\r
+       struct  statestruct     far *next;\r
+} statetypestruct;\r
+\r
+#define statetype statetypestruct far\r
+\r
+#define of_shootable           0x01\r
+#define of_damagedone  0x02\r
+#define of_forcefield  0x40            // defines a solid object as a forcefield???????????\r
+#define of_tree                        0x80            // used to identify between a tree and a statue --\r
+                                                                                       //                      last minute changes for Greg\r
+\r
+typedef struct objstruct\r
+{\r
+\r
+       int ticcount;                                           //\r
+       statetype *state;                                       // THESE MEMBERS MUST BE IN THE SAME\r
+       fixed x,y;                    // ORDER AS THE MEMBERS DEFINED IN\r
+       int viewx;                    // IOBJSTRUCT OR ALL HELL WILL BREAK\r
+       unsigned tilex,tiley;         // LOOSE!!\r
+       unsigned viewheight;          //\r
+       unsigned size;                //\r
+       struct  objstruct       *next;   //\r
+\r
+\r
+  struct objstruct *prev;\r
+  enum {no,noalways,yes,always}        active;\r
+  classtype    obclass;\r
+\r
+  unsigned char flags;\r
+\r
+  long         distance;\r
+  dirtype      dir;\r
+\r
+  int          angle;\r
+  int          hitpoints;\r
+  long         speed;\r
+\r
+  fixed                xl,xh,yl,yh;    // hit rectangle\r
+\r
+  int          temp1,temp2;\r
+} objtype;\r
+\r
+#if USE_INERT_LIST\r
+\r
+#define MAXINERTOBJ 20\r
+\r
+typedef struct iobjstruct {\r
+       int ticcount;\r
+       statetype *state;\r
+       fixed x,y;\r
+       int viewx;\r
+       unsigned tilex,tiley;\r
+       unsigned viewheight;\r
+       unsigned size;\r
+       struct iobjstruct *next;\r
+} inertobjtype;\r
+\r
+#endif\r
+\r
+\r
+typedef        enum    {ex_stillplaying,ex_died,ex_warped,ex_resetgame\r
+       ,ex_loadedgame,ex_victorious,ex_turning,ex_abort} exittype;\r
+\r
+\r
+typedef enum {S_NONE,     S_HELP,    S_SND,     S_SAVING,   S_RESTORING,\r
+                       S_JOYSTICK, S_RETREAT, S_ADVANCE, S_SIDESTEP, S_QTURN,\r
+                       S_MISSLE,   S_ZAPPER,  S_XTER,    S_CURING,   S_READ,\r
+                       S_VIEWING,  S_ITEMDES,  S_DAMAGE,  S_TURN,    S_TIMESTOP} status_flags;\r
+\r
+typedef struct {\r
+       char x,y;\r
+       unsigned ondoor,underdoor;\r
+} doorinfo;\r
+\r
+typedef struct {\r
+       char x,y;\r
+       short angle;\r
+       doorinfo doors[MAX_DOOR_STORAGE];\r
+} levelinfo;\r
+\r
+typedef        struct\r
+{\r
+       int             difficulty;\r
+       int             mapon;\r
+       int             bolts,nukes,potions,keys[4],scrolls[8];\r
+\r
+       int             gems[5];                                // "int allgems[5]" is used for 1:1 comparison\r
+                                                                               // in play loop for radar... CHANGE IT, TOO!\r
+\r
+       long    score;\r
+       int             body,shotpower;\r
+       short mapwidth,mapheight;\r
+//     levelinfo levels[NUMLEVELS];\r
+} gametype;\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                C3_MAIN DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern char inlevel[][2];\r
+extern char            str[80],str2[20];\r
+extern unsigned        tedlevelnum;\r
+extern boolean         tedlevel;\r
+extern gametype        gamestate;\r
+extern exittype        playstate;\r
+extern         char SlowMode;\r
+extern   unsigned Flags;\r
+extern boolean LoadShapes;\r
+extern boolean EASYMODEON;\r
+\r
+\r
+void NewGame (void);\r
+boolean        SaveTheGame(int file);\r
+boolean        LoadTheGame(int file);\r
+void ResetGame(void);\r
+void ShutdownId (void);\r
+void InitGame (void);\r
+void Quit (char *error, ...);\r
+void TEDDeath(void);\r
+void DemoLoop (void);\r
+void SetupScalePic (unsigned picnum);\r
+void SetupScaleWall (unsigned picnum);\r
+void SetupScaling (void);\r
+void main (void);\r
+void Display320(void);\r
+void Display640(void);\r
+void PrintHelp(void);\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                C3_GAME DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern unsigned        latchpics[NUMLATCHPICS];\r
+extern unsigned        tileoffsets[NUMTILE16];\r
+extern unsigned        textstarts[27];\r
+\r
+\r
+#define        L_CHARS         0\r
+#define L_NOSHOT       1\r
+#define L_SHOTBAR      2\r
+#define L_NOBODY       3\r
+#define L_BODYBAR      4\r
+\r
+\r
+void ScanInfoPlane (void);\r
+void ScanText (void);\r
+void SetupGameLevel(void);\r
+void Victory (boolean playsounds);\r
+void Died (void);\r
+void NormalScreen (void);\r
+void DrawPlayScreen (void);\r
+void LoadLatchMem (void);\r
+void FizzleFade (unsigned source, unsigned dest,\r
+       unsigned width,unsigned height, boolean abortable);\r
+void FizzleOut (int showlevel);\r
+void FreeUpMemory (void);\r
+void GameLoop (void);\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                C3_PLAY DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define BGF_NIGHT                      0x01            // it is officially night\r
+#define BGF_NOT_LIGHTNING              0x02            // lightning flash has ended\r
+\r
+extern byte BGFLAGS,bcolor;\r
+\r
+extern unsigned *skycolor,*groundcolor;\r
+\r
+extern ControlInfo     control;\r
+extern boolean         running,slowturn;\r
+\r
+extern int                     bordertime;\r
+\r
+extern byte            tilemap[MAPSIZE][MAPSIZE];\r
+extern objtype         *actorat[MAPSIZE][MAPSIZE];\r
+extern byte            spotvis[MAPSIZE][MAPSIZE];\r
+\r
+extern objtype         objlist[MAXACTORS],*new,*obj,*player;\r
+\r
+extern unsigned        farmapylookup[MAPSIZE];\r
+extern byte            *nearmapylookup[MAPSIZE];\r
+extern byte            update[];\r
+\r
+extern boolean         godmode,singlestep;\r
+extern int                     extravbls;\r
+\r
+extern int                     mousexmove,mouseymove;\r
+extern int                     pointcount,pointsleft;\r
+extern status_flags    status_flag;\r
+extern  int             status_delay;\r
+\r
+extern         objtype                 dummyobj;\r
+extern         short           BeepTime;\r
+extern  unsigned       scolor,gcolor;\r
+\r
+void CenterWindow(word w,word h);\r
+void DebugMemory (void);\r
+void PicturePause (void);\r
+int  DebugKeys (void);\r
+void CheckKeys (void);\r
+void InitObjList (void);\r
+void GetNewObj (boolean usedummy);\r
+void RemoveObj (objtype *gone);\r
+void PollControlls (void);\r
+void PlayLoop (void);\r
+void InitBgChange(short stimer, unsigned *scolors, short gtimer, unsigned *gcolors, byte flag);\r
+\r
+void DisplayStatus (status_flags *stat_flag);\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                C3_STATE DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+//void SpawnNewObj (unsigned x, unsigned y, statetype *state, unsigned size);\r
+//void SpawnNewObjFrac (long x, long y, statetype *state, unsigned size);\r
+\r
+void Internal_SpawnNewObj (unsigned x, unsigned y, statetype *state, unsigned size, boolean UseDummy, boolean PutInActorat);\r
+void Internal_SpawnNewObjFrac (long x, long y, statetype *state, unsigned size,boolean UseDummy);\r
+\r
+#define DSpawnNewObj(x, y, state, size)                                Internal_SpawnNewObj(x,y,state,size,true,true)\r
+#define SpawnNewObj(x, y, state, size)                                 Internal_SpawnNewObj(x,y,state,size,false,true)\r
+#define ASpawnNewObj(x, y, state, size)                                        Internal_SpawnNewObj(x,y,state,size,false,false)\r
+#define SpawnNewObjFrac(x, y, state, size,Dummy)       Internal_SpawnNewObjFrac(x, y, state, size,false)\r
+#define DSpawnNewObjFrac(x, y, state, size)                    Internal_SpawnNewObjFrac(x, y, state, size,true)\r
+\r
+boolean CheckHandAttack (objtype *ob);\r
+void T_DoDamage (objtype *ob);\r
+boolean Walk (objtype *ob);\r
+void ChaseThink (objtype *obj, boolean diagonal);\r
+void MoveObj (objtype *ob, long move);\r
+boolean Chase (objtype *ob, boolean diagonal);\r
+\r
+extern dirtype opposite[9];\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                C3_TRACE DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+int FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max);\r
+int BackTrace (int finish);\r
+void ForwardTrace (void);\r
+int FinishWall (void);\r
+void InsideCorner (void);\r
+void OutsideCorner (void);\r
+void FollowWalls (void);\r
+\r
+extern boolean aborttrace;\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                C3_DRAW DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define MAXWALLS       50\r
+#define DANGERHIGH     45\r
+\r
+#define        MIDWALL         (MAXWALLS/2)\r
+\r
+//==========================================================================\r
+\r
+extern tilept  tile,lasttile,focal,left,mid,right;\r
+\r
+extern globpt  edge,view;\r
+\r
+extern unsigned screenloc[3];\r
+extern unsigned freelatch;\r
+\r
+extern int screenpage;\r
+\r
+extern boolean         fizzlein;\r
+\r
+extern long lasttimecount;\r
+\r
+extern int firstangle,lastangle;\r
+\r
+extern fixed prestep;\r
+\r
+extern int traceclip,tracetop;\r
+\r
+extern fixed sintable[ANGLES+ANGLES/4],*costable;\r
+\r
+extern fixed   viewx,viewy,viewsin,viewcos;                    // the focal point\r
+extern int     viewangle;\r
+\r
+extern fixed scale,scaleglobal;\r
+extern unsigned slideofs;\r
+\r
+extern int zbuffer[VIEWXH+1];\r
+\r
+extern walltype        walls[MAXWALLS],*leftwall,*rightwall;\r
+\r
+\r
+extern fixed   tileglobal;\r
+extern fixed   focallength;\r
+extern fixed   mindist;\r
+extern int             viewheight;\r
+extern fixed scale;\r
+\r
+extern int     far walllight1[NUMFLOORS];\r
+extern int     far walldark1[NUMFLOORS];\r
+\r
+extern unsigned topcolor,bottomcolor;\r
+\r
+extern char wall_anim_info[NUMFLOORS];\r
+extern char wall_anim_pos[NUMFLOORS];\r
+\r
+//==========================================================================\r
+\r
+void   DrawLine (int xl, int xh, int y,int color);\r
+void   DrawWall (walltype *wallptr);\r
+void   TraceRay (unsigned angle);\r
+fixed  FixedByFrac (fixed a, fixed b);\r
+void   TransformPoint (fixed gx, fixed gy, int *screenx, unsigned *screenheight);\r
+fixed  TransformX (fixed gx, fixed gy);\r
+int    FollowTrace (fixed tracex, fixed tracey, long deltax, long deltay, int max);\r
+void   ForwardTrace (void);\r
+int    FinishWall (void);\r
+int    TurnClockwise (void);\r
+int    TurnCounterClockwise (void);\r
+void   FollowWall (void);\r
+\r
+void   NewScene (void);\r
+void   BuildTables (void);\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                C3_SCALE DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+#define COMPSCALECODESTART     (65*6)          // offset to start of code in comp scaler\r
+\r
+typedef struct\r
+{\r
+       unsigned        codeofs[65];\r
+       unsigned        start[65];\r
+       unsigned        width[65];\r
+       byte            code[];\r
+}      t_compscale;\r
+\r
+typedef struct\r
+{\r
+       unsigned        width;\r
+       unsigned        codeofs[64];\r
+}      t_compshape;\r
+\r
+\r
+extern unsigned        scaleblockwidth,\r
+               scaleblockheight,\r
+               scaleblockdest;\r
+\r
+extern byte    plotpix[8];\r
+extern byte    bitmasks1[8][8];\r
+extern byte    bitmasks2[8][8];\r
+\r
+\r
+extern t_compscale _seg *scaledirectory[NUMSCALEPICS];\r
+extern t_compshape _seg *shapedirectory[NUMSCALEPICS];\r
+extern memptr                  walldirectory[NUMSCALEWALLS];\r
+extern unsigned        shapesize[NUMSCALEPICS];\r
+\r
+void           DeplanePic (int picnum);\r
+void ScaleShape (int xcenter, t_compshape _seg *compshape, unsigned scale);\r
+unsigned       BuildCompShape (t_compshape _seg **finalspot);\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                C3_ASM DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern unsigned        wallheight      [VIEWWIDTH];\r
+extern unsigned        wallwidth       [VIEWWIDTH];\r
+extern unsigned        wallseg         [VIEWWIDTH];\r
+extern unsigned        wallofs         [VIEWWIDTH];\r
+extern unsigned        screenbyte      [VIEWWIDTH];\r
+extern unsigned        screenbit       [VIEWWIDTH];\r
+extern unsigned        bitmasks        [64];\r
+\r
+extern long            wallscalecall;\r
+\r
+void   ScaleWalls (void);\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                C3_WIZ DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define MAXHANDHEIGHT  72\r
+\r
+extern statetype s_pshot_exp1;\r
+extern statetype s_pshot_exp2;\r
+extern statetype s_pshot_exp3;\r
+\r
+extern long    lastnuke;\r
+extern   int lasttext;\r
+extern int             handheight;\r
+extern int             boltsleft,bolttimer;\r
+extern short RadarXY[][3];\r
+\r
+extern short RotateAngle;\r
+extern short FreezeTime;\r
+\r
+//void FaceDir(short x, short y, boolean StopTime);\r
+//short CalcAngle(short dx, short dy);\r
+\r
+void FaceAngle(short DestAngle);\r
+void RotateView();\r
+void InitRotate(short DestAngle);\r
+short FaceDoor(short x, short y);\r
+\r
+char DisplayMsg(char *text,char *choices);\r
+char DisplaySMsg(char *text,char *choices);\r
+\r
+extern statetype s_explode;\r
+\r
+void SpawnExplosion(fixed x, fixed y,short Delay);\r
+void T_ExpThink(objtype *obj);\r
+void SpawnBigExplosion(fixed x, fixed y, short Delay, fixed Range);\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                C3_ACT1 DEFINITIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+int EasyHitPoints(int NrmHitPts);\r
+int EasyDoDamage(int Damage);\r
+\r
+#define zombie_mode    ob->temp1\r
+#define zombie_delay   ob->temp2\r
+\r
+enum zombie_modes {zm_wait_for_dark,zm_wait_to_rise,zm_active};\r
+\r
+enum eye_modes {em_other1,em_player1,em_other2,em_player2,em_other3,em_player3,em_other4,em_player4,em_dummy};\r
+\r
+#define MSHOTDAMAGE    2\r
+#define MSHOTSPEED     10000\r
+\r
+#define ESHOTDAMAGE    1\r
+#define ESHOTSPEED     5000\r
+\r
+#define SSHOTDAMAGE    3\r
+#define SSHOTSPEED     6500\r
+\r
+#define RANDOM_ATTACK 20\r
+\r
+extern dirtype dirtable[];\r
+extern short other_x[],        other_y[];\r
+extern short zombie_base_delay;\r
+\r
+extern statetype s_fatdemon_ouch;\r
+extern statetype s_fatdemon_blowup1;\r
+\r
+extern statetype s_succubus_ouch;\r
+extern statetype s_succubus_death1;\r
+\r
+extern statetype s_godessouch;\r
+extern statetype s_godessdie1;\r
+\r
+extern statetype s_mageouch;\r
+extern statetype s_magedie1;\r
+\r
+extern statetype s_grelouch;\r
+extern statetype s_greldie1;\r
+\r
+extern statetype s_batdie1;\r
+\r
+extern statetype s_zombie_death1;\r
+extern statetype s_zombie_ouch;\r
+\r
+extern statetype s_zombie_rise1;\r
+extern statetype s_zombie_rise2;\r
+extern statetype s_zombie_rise3;\r
+extern statetype s_zombie_rise4;\r
+\r
+extern statetype s_ant_wait;\r
+extern statetype s_ant_egg;\r
+extern statetype s_ant_walk1;\r
+extern statetype s_ant_walk2;\r
+extern statetype s_ant_walk3;\r
+extern statetype s_ant_attack1;\r
+extern statetype s_ant_pause;\r
+extern statetype s_ant_ouch;\r
+extern statetype s_ant_die1;\r
+extern statetype s_ant_die2;\r
+extern statetype s_ant_die3;\r
+\r
+extern statetype s_skel_pause;\r
+extern statetype s_skel_1;\r
+extern statetype s_skel_2;\r
+extern statetype s_skel_3;\r
+extern statetype s_skel_4;\r
+extern statetype s_skel_attack1;\r
+extern statetype s_skel_attack2;\r
+extern statetype s_skel_attack3;\r
+extern statetype s_skel_ouch;\r
+extern statetype s_skel_die1;\r
+extern statetype s_skel_die2;\r
+extern statetype s_skel_die3;\r
+\r
+extern statetype s_wet_pause;\r
+\r
+extern statetype s_wet_bubbles1;\r
+extern statetype s_wet_bubbles2;\r
+extern statetype s_wet_bubbles3;\r
+extern statetype s_wet_bubbles4;\r
+\r
+extern statetype s_wet_peek;\r
+\r
+extern statetype s_wet_rise1;\r
+extern statetype s_wet_rise2;\r
+extern statetype s_wet_rise3;\r
+extern statetype s_wet_rise4;\r
+extern statetype s_wet_rise5;\r
+\r
+extern statetype s_wet_sink1;\r
+extern statetype s_wet_sink2;\r
+extern statetype s_wet_sink3;\r
+\r
+extern statetype s_wet_walk1;\r
+extern statetype s_wet_walk2;\r
+extern statetype s_wet_walk3;\r
+extern statetype s_wet_walk4;\r
+\r
+extern statetype s_wet_attack1;\r
+extern statetype s_wet_attack2;\r
+extern statetype s_wet_attack3;\r
+extern statetype s_wet_attack4;\r
+\r
+extern statetype s_wet_ouch;\r
+\r
+extern statetype s_wet_die1;\r
+extern statetype s_wet_die2;\r
+extern statetype s_wet_die3;\r
+extern statetype s_wet_die4;\r
+extern statetype s_wet_die5;\r
+\r
+extern statetype s_obj_gate1;\r
+extern statetype s_obj_gate2;\r
+extern statetype s_obj_gate3;\r
+extern statetype s_obj_gate4;\r
+\r
+extern statetype s_eye_pause;\r
+\r
+extern statetype s_eye_1;\r
+extern statetype s_eye_2;\r
+extern statetype s_eye_3;\r
+extern statetype s_eye_4;\r
+\r
+extern statetype s_eye_ouch;\r
+extern statetype s_eye_ouch2;\r
+\r
+extern statetype s_eye_die1;\r
+extern statetype s_eye_die2;\r
+extern statetype s_eye_die3;\r
+\r
+extern statetype s_mshot1;\r
+extern statetype s_mshot2;\r
+\r
+extern statetype s_bonus_die;\r
+\r
+extern statetype s_red_demonouch;\r
+extern statetype s_red_demondie1;\r
+\r
+extern statetype s_bunny_death1;\r
+extern statetype s_bunny_ouch;\r
+\r
+extern statetype s_tree_death1;\r
+extern statetype s_tree_ouch;\r
+\r
+extern statetype s_force_field_die;
\ No newline at end of file
diff --git a/16/cawat/GELIB.C b/16/cawat/GELIB.C
new file mode 100644 (file)
index 0000000..5e0bd6f
--- /dev/null
@@ -0,0 +1,2876 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#include <dos.h>\r
+#include <conio.h>\r
+#include <stdio.h>\r
+#include <dir.h>\r
+#include "mem.h"\r
+#include "string.h"\r
+#include "time.h"\r
+#include "stdarg.h"\r
+#include "io.h"\r
+\r
+#include "DEF.H"\r
+#include "gelib.h"\r
+#include "sl_file.h"\r
+\r
+\r
+#define MAX_GAMELIST_NAMES 20\r
+#define FNAME_LEN                              9\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// Global variables\r
+//\r
+boolean InLoadSaveGame = false;\r
+//AudioDeviceType ge_DigiMode;\r
+boolean ConserveMemory = false;\r
+char GameListNames[MAX_GAMELIST_NAMES+1][FNAME_LEN],current_disk=1;\r
+short NumGames;\r
+short PPT_LeftEdge=0,PPT_RightEdge=320;\r
+boolean LeaveDriveOn=false,ge_textmode=true;\r
+char Filename[FILENAME_LEN+1], ID[sizeof(GAMENAME)], VER[sizeof(SAVEVER_DATA)];\r
+short wall_anim_delay,wall_anim_time = 7;\r
+BufferedIO lzwBIO;\r
+\r
+\r
+\r
+\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// CalibrateJoystick()\r
+//\r
+void CalibrateJoystick(short joynum)\r
+{\r
+       word    minx,maxx,\r
+                       miny,maxy;\r
+\r
+       IN_ClearKeysDown();\r
+\r
+       VW_HideCursor();\r
+\r
+       VW_FixRefreshBuffer();\r
+       CenterWindow(30,8);\r
+\r
+       US_Print("\n");\r
+       US_CPrintLine("Move joystick to the upper-left");\r
+       US_CPrintLine("and press one of the buttons.");\r
+       VW_UpdateScreen();\r
+\r
+       while ((LastScan != sc_Escape) && !IN_GetJoyButtonsDB(joynum));\r
+       if (LastScan == sc_Escape)\r
+               return;\r
+\r
+       IN_GetJoyAbs(joynum,&minx,&miny);\r
+       while (IN_GetJoyButtonsDB(joynum));\r
+\r
+       US_Print("\n");\r
+       US_CPrintLine("Move joystick to the lower-right");\r
+       US_CPrintLine("and press one of the buttons.");\r
+       VW_UpdateScreen();\r
+\r
+       while ((LastScan != sc_Escape) && !IN_GetJoyButtonsDB(joynum));\r
+       if (LastScan == sc_Escape)\r
+               return;\r
+\r
+       IN_GetJoyAbs(joynum,&maxx,&maxy);\r
+       if ((minx == maxx) && (miny == maxy))\r
+               return;\r
+\r
+       IN_SetupJoy(joynum,minx,maxx,miny,maxy);\r
+\r
+       while (IN_GetJoyButtonsDB(joynum));\r
+       if (LastScan)\r
+               IN_ClearKeysDown();\r
+\r
+       JoystickCalibrated = true;\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// WaitKeyVBL()\r
+//\r
+void WaitKeyVBL(short key, short vbls)\r
+{\r
+       while (vbls--)\r
+       {\r
+               VW_WaitVBL(1);\r
+               IN_ReadControl(0,&control);\r
+               if ((control.button0|control.button1)||(Keyboard[key]))\r
+                       break;\r
+       }\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// MoveScreen()\r
+//\r
+// panadjust must be saved and restored if MoveScreen is being called from\r
+// inside a level.\r
+//\r
+void MoveScreen(short x, short y)\r
+{\r
+       unsigned address;\r
+\r
+       address = (y*linewidth)+(x/8);\r
+       VW_SetScreen(address,0);\r
+       bufferofs = displayofs = address;\r
+       panadjust=0;\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// MoveGfxDst()\r
+//\r
+void MoveGfxDst(short x, short y)\r
+{\r
+       unsigned address;\r
+\r
+       address = (y*linewidth)+(x/8);\r
+       bufferofs = displayofs = address;\r
+}\r
+\r
+#if 0\r
+\r
+#if GRAPHIC_PIRATE\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// DoPiracy() - Graphics piracy code...\r
+//\r
+void DoPiracy()\r
+{\r
+       struct Shape Pirate1Shp;\r
+       struct Shape Pirate2Shp;\r
+\r
+       VW_SetScreenMode (EGA320GR);\r
+       VW_ClearVideo(BLACK);\r
+\r
+       // Load shapes...\r
+       //\r
+       if (LoadShape("PIRATE1E."EXT,&Pirate1Shp))\r
+               TrashProg("Can't load PIRATE1E.BIO");\r
+\r
+       if (LoadShape("PIRATE2E."EXT,&Pirate2Shp))\r
+               TrashProg("Can't load PIRATE2E.BIO");\r
+\r
+       // Deal with shapes...\r
+       //\r
+       VW_SetLineWidth(40);\r
+\r
+       VW_FadeOut();\r
+\r
+       MoveScreen(0,0);\r
+       UnpackEGAShapeToScreen(&Pirate1Shp,(linewidth-Pirate1Shp.BPR)<<2,0);\r
+\r
+       MoveScreen(0,200);\r
+       UnpackEGAShapeToScreen(&Pirate2Shp,(linewidth-Pirate2Shp.BPR)<<2,0);\r
+\r
+       MoveScreen(0,0);\r
+       VW_FadeIn();\r
+       WaitKeyVBL(57,200);\r
+       while (Keyboard[57]);\r
+\r
+       SD_PlaySound(GOOD_PICKSND);\r
+\r
+       MoveScreen(0,200);\r
+       WaitKeyVBL(57,300);\r
+       while (Keyboard[57]);\r
+       VW_FadeOut();\r
+\r
+       FreeShape(&Pirate1Shp);\r
+       FreeShape(&Pirate2Shp);\r
+}\r
+\r
+#else\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// DoPiracy() - Text-based piracy code...\r
+//\r
+void DoPiracy()\r
+{\r
+}\r
+\r
+#endif\r
+#endif\r
+\r
+//--------------------------------------------------------------------------\r
+// BlackPalette()\r
+//--------------------------------------------------------------------------\r
+void BlackPalette()\r
+{\r
+       extern char colors[7][17];\r
+\r
+       _ES=FP_SEG(&colors[0]);\r
+       _DX=FP_OFF(&colors[0]);\r
+       _AX=0x1002;\r
+       geninterrupt(0x10);\r
+       screenfaded = true;\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// ColoredPalette()\r
+//--------------------------------------------------------------------------\r
+void ColoredPalette()\r
+{\r
+       extern char colors[7][17];\r
+\r
+       _ES=FP_SEG(&colors[3]);\r
+       _DX=FP_OFF(&colors[3]);\r
+       _AX=0x1002;\r
+       geninterrupt(0x10);\r
+       screenfaded = false;\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// Verify()\r
+//\r
+long Verify(char *filename)\r
+{\r
+       int handle;\r
+       long size;\r
+\r
+       if ((handle=open(filename,O_BINARY))==-1)\r
+               return (0);\r
+       size=filelength(handle);\r
+       close(handle);\r
+       return(size);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     GE_SaveGame\r
+//\r
+//     Handles user i/o for saving a game\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+\r
+void GE_SaveGame()\r
+{\r
+       boolean GettingFilename=true;\r
+//     char Filename[FILENAME_LEN+1]; //, ID[sizeof(GAMENAME)], VER[sizeof(SAVEVER_DATA)];\r
+       int handle;\r
+       struct dfree dfree;\r
+       long davail;\r
+\r
+       VW_FixRefreshBuffer();\r
+       ReadGameList();\r
+       while (GettingFilename)\r
+       {\r
+               DisplayGameList(2,7,3,10);\r
+               US_DrawWindow(5,1,30,3);\r
+               memset(Filename,0,sizeof(Filename));\r
+               US_CPrint("Enter name to SAVE this game:");\r
+               VW_UpdateScreen();\r
+               if (screenfaded)\r
+                       VW_FadeIn();\r
+               if (!US_LineInput((linewidth<<2)-32,20,Filename,"",true,8,0))\r
+                       goto EXIT_FUNC;\r
+               if (!strlen(Filename))\r
+                       goto EXIT_FUNC;\r
+               getdfree(getdisk()+1,&dfree);\r
+               davail = (long)dfree.df_avail*(long)dfree.df_bsec*(long)dfree.df_sclus;\r
+               if (davail < 10000)\r
+               {\r
+                       US_CenterWindow(22,4);\r
+                       US_Print("\n");\r
+                       US_CPrintLine("Disk Full: Can't save game.");\r
+                       US_CPrintLine("Try inserting another disk.");\r
+                       VW_UpdateScreen();\r
+\r
+                       IN_Ack();\r
+               }\r
+               else\r
+               {\r
+                       strcat(Filename,".SAV");\r
+                       GettingFilename = false;\r
+                       if (Verify(Filename))                                                           // FILE EXISTS\r
+                       {\r
+                               US_CenterWindow(22,4);\r
+                               US_CPrintLine("That file already exists...");\r
+                               US_CPrintLine("Overwrite it ????");\r
+                               US_CPrintLine("(Y)es or (N)o?");\r
+                               VW_UpdateScreen();\r
+\r
+                               while((!Keyboard[21]) && (!Keyboard[49]) && !Keyboard[27]);\r
+\r
+                               if (Keyboard[27])\r
+                                       goto EXIT_FUNC;\r
+                               if (Keyboard[49])\r
+                               {\r
+                                       GettingFilename = true;\r
+                                       VW_UpdateScreen();\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       handle = open(Filename,O_RDWR|O_CREAT|O_BINARY,S_IREAD|S_IWRITE);\r
+       if (handle==-1)\r
+               goto EXIT_FUNC;\r
+\r
+       if ((!CA_FarWrite(handle,(void far *)GAMENAME,sizeof(GAMENAME))) || (!CA_FarWrite(handle,(void far *)SAVEVER_DATA,sizeof(SAVEVER_DATA))))\r
+       {\r
+               if (!screenfaded)\r
+                       VW_FadeOut();\r
+\r
+               return;\r
+       }\r
+\r
+       if (!USL_SaveGame(handle))\r
+               Quit("Save game error");\r
+\r
+\r
+\r
+EXIT_FUNC:;\r
+\r
+       if (handle!=-1)\r
+               close(handle);\r
+\r
+       if (handle==-1)\r
+       {\r
+               remove(Filename);\r
+               US_CenterWindow(22,6);\r
+               US_CPrintLine("DISK ERROR");\r
+               US_CPrintLine("Check: Write protect...");\r
+               US_CPrintLine("File name...");\r
+               US_CPrintLine("Bytes free on disk...");\r
+               US_CPrintLine("Press SPACE to continue.");\r
+               VW_UpdateScreen();\r
+               while (!Keyboard[57]);\r
+               while (Keyboard[57]);\r
+       }\r
+\r
+       while (Keyboard[1]);\r
+\r
+       if (!screenfaded)\r
+               VW_FadeOut();\r
+}\r
+\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     GE_LoadGame\r
+//\r
+//     Handles user i/o for loading a game\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+\r
+boolean GE_LoadGame()\r
+{\r
+       boolean GettingFilename=true,rt_code=false;\r
+       int handle;\r
+\r
+       IN_ClearKeysDown();\r
+       memset(ID,0,sizeof(ID));\r
+       memset(VER,0,sizeof(VER));\r
+       VW_FixRefreshBuffer();\r
+       ReadGameList();\r
+       while (GettingFilename)\r
+       {\r
+               DisplayGameList(2,7,3,10);\r
+               US_DrawWindow(5,1,30,3);\r
+               memset(Filename,0,sizeof(Filename));\r
+               US_CPrint("Enter name of game to RESTORE:");\r
+               VW_UpdateScreen();\r
+               if (screenfaded)\r
+                       VW_FadeIn();\r
+               if (!US_LineInput((linewidth<<2)-32,20,Filename,"",true,8,0))\r
+                       goto EXIT_FUNC;\r
+               strcat(Filename,".SAV");\r
+               GettingFilename = false;\r
+\r
+               if (!Verify(Filename))                                                          // FILE DOESN'T EXIST\r
+               {\r
+                       US_CenterWindow(22,3);\r
+                       US_CPrintLine(" That file doesn't exist....");\r
+                       US_CPrintLine("Press SPACE to try again.");\r
+                       VW_UpdateScreen();\r
+\r
+                       while (!Keyboard[57]);\r
+                       while (Keyboard[57]);\r
+                       GettingFilename = true;\r
+               }\r
+       }\r
+\r
+       handle = open(Filename,O_RDWR|O_BINARY);\r
+       if (handle==-1)\r
+               goto EXIT_FUNC;\r
+\r
+       if ((!CA_FarRead(handle,(void far *)&ID,sizeof(ID))) || (!CA_FarRead(handle,(void far *)&VER,sizeof(VER))))\r
+               return(false);\r
+\r
+       if ((strcmp(ID,GAMENAME)) || (strcmp(VER,SAVEVER_DATA)))\r
+       {\r
+               US_CenterWindow(32,4);\r
+               US_CPrintLine("That isn't a "GAMENAME);\r
+               US_CPrintLine(".SAV file.");\r
+               US_CPrintLine("Press SPACE to continue.");\r
+               VW_UpdateScreen();\r
+               while (!Keyboard[57]);\r
+               while (Keyboard[57]);\r
+\r
+               if (!screenfaded)\r
+                       VW_FadeOut();\r
+\r
+               return(false);\r
+       }\r
+\r
+       if (!USL_LoadGame(handle))\r
+               Quit("Load game error.");\r
+\r
+       rt_code = true;\r
+\r
+\r
+EXIT_FUNC:;\r
+       if (handle==-1)\r
+       {\r
+               US_CenterWindow(22,3);\r
+               US_CPrintLine("DISK ERROR ** LOAD **");\r
+               US_CPrintLine("Press SPACE to continue.");\r
+               while (!Keyboard[57]);\r
+               while (Keyboard[57]);\r
+       }\r
+       else\r
+               close(handle);\r
+\r
+       if (!screenfaded)\r
+               VW_FadeOut();\r
+\r
+       return(rt_code);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     GE_HardError() - Handles the Abort/Retry/Fail sort of errors passed\r
+//                     from DOS. Hard coded to ignore if during Load/Save Game.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+#pragma        warn    -par\r
+#pragma        warn    -rch\r
+int GE_HardError(word errval,int ax,int bp,int si)\r
+{\r
+#define IGNORE  0\r
+#define RETRY   1\r
+#define        ABORT   2\r
+extern void    ShutdownId(void);\r
+\r
+static char            buf[32];\r
+static WindowRec       wr;\r
+static boolean         oldleavedriveon;\r
+               int                     di;\r
+               char            c,*s,*t;\r
+boolean holdscreenfaded;\r
+\r
+       if (InLoadSaveGame)\r
+               hardresume(IGNORE);\r
+\r
+\r
+       di = _DI;\r
+\r
+       oldleavedriveon = LeaveDriveOn;\r
+       LeaveDriveOn = false;\r
+\r
+       if (ax < 0)\r
+               s = "Device Error";\r
+       else\r
+       {\r
+               if ((di & 0x00ff) == 0)\r
+                       s = "Drive ~ is Write Protected";\r
+               else\r
+                       s = "Error on Drive ~";\r
+               for (t = buf;*s;s++,t++)        // Can't use sprintf()\r
+                       if ((*t = *s) == '~')\r
+                               *t = (ax & 0x00ff) + 'A';\r
+               *t = '\0';\r
+               s = buf;\r
+       }\r
+\r
+       c = peekb(0x40,0x49);   // Get the current screen mode\r
+       if ((c < 4) || (c == 7))\r
+               goto oh_kill_me;\r
+\r
+       // DEBUG - handle screen cleanup\r
+       holdscreenfaded=screenfaded;\r
+\r
+       US_SaveWindow(&wr);\r
+       VW_ClearVideo(0);            ////////////// added for exiting\r
+       US_CenterWindow(30,3);\r
+       US_CPrint(s);\r
+       US_CPrint("(R)etry or (A)bort?");\r
+       VW_UpdateScreen();\r
+       if (holdscreenfaded)\r
+               VW_FadeIn();\r
+       IN_ClearKeysDown();\r
+\r
+asm    sti     // Let the keyboard interrupts come through\r
+\r
+       while (true)\r
+       {\r
+               switch (IN_WaitForASCII())\r
+               {\r
+               case key_Escape:\r
+               case 'a':\r
+               case 'A':\r
+                       goto oh_kill_me;\r
+                       break;\r
+               case key_Return:\r
+               case key_Space:\r
+               case 'r':\r
+               case 'R':\r
+                       if (holdscreenfaded)\r
+                               VW_FadeOut();\r
+                       US_ClearWindow();\r
+                       VW_UpdateScreen();\r
+                       US_RestoreWindow(&wr);\r
+                       LeaveDriveOn = oldleavedriveon;\r
+                       return(RETRY);\r
+                       break;\r
+               }\r
+       }\r
+\r
+oh_kill_me:\r
+       abortprogram = s;\r
+       TrashProg("Terminal Error: %s\n",s);\r
+//     if (tedlevel)\r
+//             fprintf(stderr,"You launched from TED. I suggest that you reboot...\n");\r
+\r
+       return(ABORT);\r
+#undef IGNORE\r
+#undef RETRY\r
+#undef ABORT\r
+}\r
+#pragma        warn    +par\r
+#pragma        warn    +rch\r
+\r
+//--------------------------------------------------------------------------\r
+//\r
+//\r
+//                          B O B   ROUTINES\r
+//\r
+//\r
+//--------------------------------------------------------------------------\r
+\r
+\r
+\r
+#ifdef BOBLIST\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// UpdateBOBList() - Adds a sprite to an objects BOBlist.  The BOB List\r
+//                                                     must already be allocated and have an available slot.\r
+//\r
+//     RETURNS : true = Success adding Sprite / false = Failure.\r
+//\r
+// NOTE : This also sets the users 'needtoreact' flag to true.\r
+//\r
+boolean UpdateBOBList(objtype *obj,struct Simple_Shape *Shape,shapeclass Class, short priority, spriteflags sprflags)\r
+{\r
+       struct BOB_Shape *CurBOBShape = NULL;\r
+\r
+#pragma warn -pia\r
+\r
+       if (CurBOBShape = obj->nextshape)\r
+       {\r
+               // Treverse down BOBList looking for a sprite with the same class\r
+               // OR an empty shape struct to store the new shape.\r
+\r
+               while ((CurBOBShape->class != Class) && (CurBOBShape->class) && CurBOBShape)\r
+               {\r
+                       CurBOBShape = CurBOBShape->nextshape;\r
+               }\r
+\r
+               if (CurBOBShape)\r
+               {\r
+                       RF_RemoveSprite(&CurBOBShape->sprite);\r
+                       CurBOBShape->shapenum = Shape->shapenum;\r
+                       CurBOBShape->x_offset = Shape->x_offset;\r
+                       CurBOBShape->y_offset = Shape->y_offset;\r
+                       CurBOBShape->priority = priority;\r
+                       CurBOBShape->sprflags = sprflags;\r
+                       CurBOBShape->class = Class;\r
+                       return(true);\r
+               }\r
+       }\r
+       return(false);\r
+\r
+#pragma warn +pia\r
+\r
+}\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+// RemoveBOBShape() - Removes a sprite from a BOBList.\r
+//\r
+// RETURNS : true = Success / false = Failure (shape not found)\r
+//\r
+boolean RemoveBOBShape(objtype *obj, shapeclass Class)\r
+{\r
+       struct BOB_Shape *CurBOBShape = NULL;\r
+\r
+#pragma warn -pia\r
+\r
+       if (CurBOBShape = obj->nextshape)\r
+       {\r
+               while ((CurBOBShape->class != Class) && (!CurBOBShape->class) && CurBOBShape)\r
+               {\r
+                       CurBOBShape = CurBOBShape->nextshape;\r
+               }\r
+\r
+               if (CurBOBShape)\r
+               {\r
+                       CurBOBShape->class = noshape;\r
+                       return(true);\r
+               }\r
+       }\r
+       return(false);\r
+\r
+#pragma warn +pia\r
+\r
+}\r
+\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+// RemoveBOBList() - Removes an entire BOBList attached to an object.\r
+//\r
+//\r
+void RemoveBOBList(objtype *obj)\r
+{\r
+       struct BOB_Shape *CurBOBShape;\r
+\r
+#pragma warn -pia\r
+\r
+       if (CurBOBShape = obj->nextshape)\r
+       {\r
+               // Treverse down BOBList looking for a sprite with the same class\r
+               // OR an empty shape struct to store the new shape.\r
+\r
+               while (CurBOBShape)\r
+               {\r
+                       if (CurBOBShape->class)\r
+                       {\r
+                               CurBOBShape->class = noshape;\r
+                               RF_RemoveSprite (&CurBOBShape->sprite);\r
+                       }\r
+                       CurBOBShape = CurBOBShape->nextshape;\r
+               }\r
+       }\r
+\r
+#pragma warn +pia\r
+\r
+}\r
+\r
+\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+// InitBOBList() -- This initializes a BOB list for all the possible shapes\r
+//                                               attached at one time.  This is done with an array of\r
+//                                               BOB_Shape structs and links the 'nextshape' pointer to\r
+//                                               to the next element.\r
+//\r
+//\r
+void InitBOBList(objtype *obj, struct BOB_Shape *BOB_Shape, short NumElements)\r
+{\r
+       struct BOB_Shape *CurShape;\r
+       short loop;\r
+\r
+       obj->nextshape = BOB_Shape;\r
+\r
+       for (loop=1;loop<NumElements;loop++)\r
+       {\r
+               CurShape = BOB_Shape++;\r
+               CurShape->nextshape = BOB_Shape;\r
+       }\r
+\r
+       BOB_Shape->nextshape = NULL;\r
+}\r
+\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// RefreshBOBList() -- This routine updates all sprites attached to the\r
+//                                                       BOBList and refreshes there position in the sprite\r
+//                                                       list.\r
+//\r
+void RefreshBOBList(objtype *obj)\r
+{\r
+       struct BOB_Shape *Shape;\r
+\r
+       Shape = obj->nextshape;\r
+\r
+       while (Shape)\r
+       {\r
+               if (Shape->class)\r
+                       RF_PlaceSprite(&Shape->sprite,obj->x+Shape->x_offset,obj->y+Shape->y_offset, Shape->shapenum, spritedraw,Shape->priority,Shape->sprflags);\r
+               Shape = Shape->nextshape;\r
+       }\r
+}\r
+#endif\r
+\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+// InitBufferedIO()\r
+//--------------------------------------------------------------------------\r
+memptr InitBufferedIO(int handle, BufferedIO *bio)\r
+{\r
+       bio->handle = handle;\r
+       bio->offset = BIO_BUFFER_LEN;\r
+       bio->status = 0;\r
+       MM_GetPtr(&bio->buffer,BIO_BUFFER_LEN);\r
+\r
+       return(bio->buffer);\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// FreeBufferedIO()\r
+//--------------------------------------------------------------------------\r
+void FreeBufferedIO(BufferedIO *bio)\r
+{\r
+       if (bio->buffer)\r
+               MM_FreePtr(&bio->buffer);\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// bio_readch()\r
+//--------------------------------------------------------------------------\r
+byte bio_readch(BufferedIO *bio)\r
+{\r
+       byte far *buffer;\r
+\r
+       if (bio->offset == BIO_BUFFER_LEN)\r
+       {\r
+               bio->offset = 0;\r
+               bio_fillbuffer(bio);\r
+       }\r
+\r
+       buffer = MK_FP(bio->buffer,bio->offset++);\r
+\r
+       return(*buffer);\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// bio_fillbuffer()\r
+//\r
+// BUGS (Not really bugs... More like RULES!)\r
+//\r
+//    1) This code assumes BIO_BUFFER_LEN is no smaller than\r
+//       NEAR_BUFFER_LEN!!\r
+//\r
+//    2) BufferedIO.status should be altered by this code to report\r
+//       read errors, end of file, etc... If you know how big the file\r
+//       is you're reading, determining EOF should be no problem.\r
+//\r
+//--------------------------------------------------------------------------\r
+void bio_fillbuffer(BufferedIO *bio)\r
+{\r
+       #define NEAR_BUFFER_LEN (64)\r
+       byte near_buffer[NEAR_BUFFER_LEN];\r
+       short bio_length,bytes_read,bytes_requested;\r
+\r
+       bytes_read = 0;\r
+       bio_length = BIO_BUFFER_LEN;\r
+       while (bio_length)\r
+       {\r
+               if (bio_length > NEAR_BUFFER_LEN-1)\r
+                       bytes_requested = NEAR_BUFFER_LEN;\r
+               else\r
+                       bytes_requested = bio_length;\r
+\r
+               read(bio->handle,near_buffer,bytes_requested);\r
+               _fmemcpy(MK_FP(bio->buffer,bytes_read),near_buffer,bytes_requested);\r
+\r
+               bio_length -= bytes_requested;\r
+               bytes_read += bytes_requested;\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SwapLong()\r
+//\r
+void SwapLong(long far *Var)\r
+{\r
+       asm             les     bx,Var\r
+       asm             mov     ax,[es:bx]\r
+       asm             xchg    ah,al\r
+       asm             xchg    ax,[es:bx+2]\r
+       asm             xchg    ah,al\r
+       asm             mov     [es:bx],ax\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// SwapWord()\r
+//\r
+void SwapWord(unsigned int far *Var)\r
+{\r
+       asm             les     bx,Var\r
+       asm             mov     ax,[es:bx]\r
+       asm             xchg    ah,al\r
+       asm             mov     [es:bx],ax\r
+}\r
+\r
+\r
+#if 0\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// LoadShape()\r
+//\r
+int LoadShape(char *Filename,struct Shape *SHP)\r
+{\r
+       #define CHUNK(Name)     (*ptr == *Name) &&                      \\r
+                                                               (*(ptr+1) == *(Name+1)) &&      \\r
+                                                               (*(ptr+2) == *(Name+2)) &&      \\r
+                                                               (*(ptr+3) == *(Name+3))\r
+\r
+\r
+       int RT_CODE;\r
+//     struct ffblk ffblk;\r
+       FILE *fp;\r
+       char CHUNK[5];\r
+       char far *ptr;\r
+       memptr IFFfile = NULL;\r
+       unsigned long FileLen, size, ChunkLen;\r
+       int loop;\r
+\r
+\r
+       RT_CODE = 1;\r
+\r
+       // Decompress to ram and return ptr to data and return len of data in\r
+       //      passed variable...\r
+\r
+       if (!(FileLen = BLoad(Filename,&IFFfile)))\r
+               TrashProg("Can't load Compressed Shape - Possibly corrupt file!");\r
+\r
+       // Evaluate the file\r
+       //\r
+       ptr = MK_FP(IFFfile,0);\r
+       if (!CHUNK("FORM"))\r
+               goto EXIT_FUNC;\r
+       ptr += 4;\r
+\r
+       FileLen = *(long far *)ptr;\r
+       SwapLong((long far *)&FileLen);\r
+       ptr += 4;\r
+\r
+       if (!CHUNK("ILBM"))\r
+               goto EXIT_FUNC;\r
+       ptr += 4;\r
+\r
+       FileLen += 4;\r
+       while (FileLen)\r
+       {\r
+               ChunkLen = *(long far *)(ptr+4);\r
+               SwapLong((long far *)&ChunkLen);\r
+               ChunkLen = (ChunkLen+1) & 0xFFFFFFFE;\r
+\r
+               if (CHUNK("BMHD"))\r
+               {\r
+                       ptr += 8;\r
+                       SHP->bmHdr.w = ((struct BitMapHeader far *)ptr)->w;\r
+                       SHP->bmHdr.h = ((struct BitMapHeader far *)ptr)->h;\r
+                       SHP->bmHdr.x = ((struct BitMapHeader far *)ptr)->x;\r
+                       SHP->bmHdr.y = ((struct BitMapHeader far *)ptr)->y;\r
+                       SHP->bmHdr.d = ((struct BitMapHeader far *)ptr)->d;\r
+                       SHP->bmHdr.trans = ((struct BitMapHeader far *)ptr)->trans;\r
+                       SHP->bmHdr.comp = ((struct BitMapHeader far *)ptr)->comp;\r
+                       SHP->bmHdr.pad = ((struct BitMapHeader far *)ptr)->pad;\r
+                       SwapWord(&SHP->bmHdr.w);\r
+                       SwapWord(&SHP->bmHdr.h);\r
+                       SwapWord(&SHP->bmHdr.x);\r
+                       SwapWord(&SHP->bmHdr.y);\r
+                       ptr += ChunkLen;\r
+               }\r
+               else\r
+               if (CHUNK("BODY"))\r
+               {\r
+                       ptr += 4;\r
+                       size = *((long far *)ptr);\r
+                       ptr += 4;\r
+                       SwapLong((long far *)&size);\r
+                       SHP->BPR = (SHP->bmHdr.w+7) >> 3;\r
+                       MM_GetPtr(&SHP->Data,size);\r
+                       if (!SHP->Data)\r
+                               goto EXIT_FUNC;\r
+                       movedata(FP_SEG(ptr),FP_OFF(ptr),FP_SEG(SHP->Data),0,size);\r
+                       ptr += ChunkLen;\r
+\r
+                       break;\r
+               }\r
+               else\r
+                       ptr += ChunkLen+8;\r
+\r
+               FileLen -= ChunkLen+8;\r
+       }\r
+\r
+       RT_CODE = 0;\r
+\r
+EXIT_FUNC:;\r
+       if (IFFfile)\r
+       {\r
+//             segptr = (memptr)FP_SEG(IFFfile);\r
+               MM_FreePtr(&IFFfile);\r
+       }\r
+\r
+       return (RT_CODE);\r
+}\r
+\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// FreeShape()\r
+//\r
+void FreeShape(struct Shape *shape)\r
+{\r
+       if (shape->Data)\r
+               MM_FreePtr(&shape->Data);\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// UnpackEGAShapeToScreen()\r
+//\r
+int UnpackEGAShapeToScreen(struct Shape *SHP,int startx,int starty)\r
+{\r
+       int currenty;\r
+       signed char n, Rep, far *Src, far *Dst[8], loop, Plane;\r
+       unsigned int BPR, Height;\r
+       boolean NotWordAligned;\r
+\r
+       NotWordAligned = SHP->BPR & 1;\r
+       startx>>=3;\r
+       Src = MK_FP(SHP->Data,0);\r
+       currenty = starty;\r
+       Plane = 0;\r
+       Height = SHP->bmHdr.h;\r
+       while (Height--)\r
+       {\r
+               Dst[0] = (MK_FP(0xA000,displayofs));\r
+               Dst[0] += ylookup[currenty];\r
+               Dst[0] += startx;\r
+               for (loop=1; loop<SHP->bmHdr.d; loop++)\r
+                       Dst[loop] = Dst[0];\r
+\r
+\r
+               for (Plane=0; Plane<SHP->bmHdr.d; Plane++)\r
+               {\r
+                       outport(0x3c4,((1<<Plane)<<8)|2);\r
+\r
+                       BPR = ((SHP->BPR+1) >> 1) << 1;               // IGNORE WORD ALIGN\r
+                       while (BPR)\r
+                       {\r
+                               if (SHP->bmHdr.comp)\r
+                                       n = *Src++;\r
+                               else\r
+                                       n = BPR-1;\r
+\r
+                               if (n < 0)\r
+                               {\r
+                                       if (n != -128)\r
+                                       {\r
+                                               n = (-n)+1;\r
+                                               BPR -= n;\r
+                                               Rep = *Src++;\r
+                                               if ((!BPR) && (NotWordAligned))   // IGNORE WORD ALIGN\r
+                                                       n--;\r
+\r
+                                               while (n--)\r
+                                                       *Dst[Plane]++ = Rep;\r
+                                       }\r
+                                       else\r
+                                               BPR--;\r
+                               }\r
+                               else\r
+                               {\r
+                                       n++;\r
+                                       BPR -= n;\r
+                                       if ((!BPR) && (NotWordAligned))     // IGNORE WORD ALIGN\r
+                                               n--;\r
+\r
+                                       while (n--)\r
+                                               *Dst[Plane]++ = *Src++;\r
+\r
+                                       if ((!BPR) && (NotWordAligned))     // IGNORE WORD ALIGN\r
+                                               Src++;\r
+                               }\r
+                       }\r
+               }\r
+               currenty++;\r
+       }\r
+\r
+       return(0);\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// GetKeyChoice()\r
+//\r
+char GetKeyChoice(char *choices,boolean clear)\r
+{\r
+       extern void DoEvents(void);\r
+\r
+       boolean waiting;\r
+       char *s,*ss;\r
+\r
+       IN_ClearKeysDown();\r
+\r
+       waiting = true;\r
+       while (waiting)\r
+       {\r
+               s = choices;\r
+               while (*s)\r
+               {\r
+                       if (Keyboard[*s++])\r
+                       {\r
+                               waiting=false;\r
+                               break;\r
+                       }\r
+               }\r
+       }\r
+\r
+       IN_ClearKeysDown();\r
+\r
+       return(*(--s));\r
+}\r
+\r
+#if 0\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// AnimateObj()\r
+//\r
+boolean AnimateObj(objtype *obj)\r
+{\r
+       boolean Done;\r
+\r
+       Done = false;\r
+\r
+       if (obj->animtype == at_NONE)           // Animation finished?\r
+               return(true);                                           // YEP!\r
+\r
+       if (obj->animdelay)                                     // Check animation delay.\r
+       {\r
+               obj->animdelay -= tics;\r
+               if (obj->animdelay < 0)\r
+                       obj->animdelay = 0;\r
+               return(false);\r
+       }\r
+\r
+       switch (obj->animtype)                          // Animate this object!\r
+       {\r
+               case at_ONCE:\r
+               case at_CYCLE:\r
+                       switch (obj->animdir)\r
+                       {\r
+                               case at_FWD:\r
+                                       if (obj->curframe < obj->maxframe)\r
+                                               AdvanceAnimFWD(obj);\r
+                                       else\r
+                                               if (obj->animtype == at_CYCLE)\r
+                                               {\r
+                                                       obj->curframe = 0;               // RESET CYCLE ANIMATION\r
+                                                       obj->animdelay=1;\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       obj->animtype = at_NONE; // TERMINATE ONCE ANIM\r
+                                                       Done = true;\r
+                                               }\r
+                                       break;\r
+\r
+                               case at_REV:\r
+                                       if (obj->curframe > 0)\r
+                                               AdvanceAnimREV(obj);\r
+                                       else\r
+                                               if (obj->animtype == at_CYCLE)\r
+                                               {\r
+                                                       obj->curframe = obj->maxframe;  // RESET CYCLE ANIMATION\r
+                                                       obj->animdelay = 1;\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       obj->animtype = at_NONE;   // TERMINATE ONCE ANIM\r
+                                                       Done = true;\r
+                                               }\r
+                                       break; // REV\r
+                       }\r
+                       break;\r
+\r
+               case at_REBOUND:\r
+                       switch (obj->animdir)\r
+                       {\r
+                               case at_FWD:\r
+                                       if (obj->curframe < obj->maxframe)\r
+                                               AdvanceAnimFWD(obj);\r
+                                       else\r
+                                       {\r
+                                               obj->animdir = at_REV;\r
+                                               obj->animdelay = 1;\r
+                                       }\r
+                                       break;\r
+\r
+                               case at_REV:\r
+                                       if (obj->curframe > 0)\r
+                                               AdvanceAnimREV(obj);\r
+                                       else\r
+                                       {\r
+                                               obj->animdir = at_FWD;\r
+                                               obj->animdelay = 1;\r
+                                               Done = true;\r
+                                       }\r
+                                       break;\r
+                       }\r
+                       break; /* REBOUND */\r
+\r
+               case at_WAIT:\r
+                       Done = true;\r
+                       break;\r
+       }\r
+\r
+       return(Done);\r
+}\r
+\r
+void AdvanceAnimFWD(objtype *obj)      // Advances a Frame of ANIM for ONCE,CYCLE, REBOUND\r
+{\r
+       obj->curframe++; // INC frames\r
+       obj->animdelay = obj->maxdelay;          // Init Delay Counter.\r
+       obj->needtoreact = true;\r
+}\r
+\r
+\r
+void AdvanceAnimREV(objtype *obj)  // Advances a Frame of ANIM for ONCE,CYCLE, REBOUND\r
+{\r
+       obj->curframe--; // DEC frames\r
+       obj->animdelay = obj->maxdelay;          // Init Delay Counter.\r
+       obj->needtoreact = true;\r
+}\r
+#endif\r
+\r
+#if 0\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// LoadASArray()  - Load an array of audio samples in FAR memory.\r
+//\r
+void LoadASArray(struct Sample *ASArray)\r
+{\r
+       int loop = 0;\r
+\r
+       while (ASArray[loop].filename)\r
+       {\r
+         if (!BLoad(ASArray[loop].filename,(memptr *)&ASArray[loop].data))\r
+                 TrashProg("Unable to load sample in LoadASArray()");\r
+         loop++;\r
+       }\r
+}\r
+\r
+\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// FreeASArray() - Frees an ASArray from memory that has been loaded with\r
+//                                              LoadASArray()\r
+//\r
+void FreeASArray(struct Sample *ASArray)\r
+{\r
+       unsigned loop = 0;\r
+\r
+       while (ASArray[loop].data)\r
+       {\r
+               MM_SetPurge((memptr *)&ASArray[loop++].data,3);\r
+               MM_FreePtr((memptr *)&ASArray[loop++].data);\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// GE_LoadAllDigiSounds()      - This is a hook that CA_LoadAllSounds()\r
+//                                                                       calls to load all of the Digitized sounds for\r
+//                                                                       Sound Blaster & Sound Source.\r
+//\r
+// NOTE : This stub would do any other necessary routines for DigiSounds\r
+//                      specific to GAMERS EDGE code. (Keeping seperate from ID's)\r
+//\r
+void GE_LoadAllDigiSounds()\r
+{\r
+       LoadASArray(DigiSounds);\r
+}\r
+\r
+\r
+\r
+/////////////////////////////////////////////////////////////////////////\r
+//\r
+// GE_FreeAllDigiSounds() -- This is a hook that CA_LoadAllSounds()\r
+//                                                                       calls to free all digitized sounds for\r
+//                                                                       which ever hardware and allow for any necessary\r
+//                                                                       clean up.\r
+//\r
+//\r
+// NOTE : This stub would do any other necessary routines for DigiSounds\r
+//                      specific to GAMERS EDGE code. (Keeping seperate from ID's)\r
+//\r
+void GE_FreeAllDigiSounds()\r
+{\r
+       FreeASArray(DigiSounds);\r
+}\r
+\r
+\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// GE_LoadAllSounds()  - Loads ALL sounds needed for detected hardware.\r
+//\r
+void GE_LoadAllSounds()\r
+{\r
+       unsigned i,start;\r
+\r
+       start = STARTPCSOUNDS;\r
+       for (i=0;i<NUMSOUNDS;i++,start++)\r
+               CA_CacheAudioChunk (start);\r
+\r
+       if (AdLibPresent)\r
+       {\r
+               start = STARTADLIBSOUNDS;\r
+               for (i=0;i<NUMSOUNDS;i++,start++)\r
+                       CA_CacheAudioChunk (start);\r
+       }\r
+\r
+       if (SoundBlasterPresent)\r
+               LoadASArray(DigiSounds);\r
+}\r
+\r
+\r
+//////////////////////////////////////////////////////////////////////////\r
+//\r
+// GE_PurgeAllSounds() - Frees all sounds that were loaded.\r
+//\r
+void GE_PurgeAllSounds()\r
+{\r
+       unsigned start,i;\r
+\r
+       start = STARTPCSOUNDS;\r
+       for (i=0;i<NUMSOUNDS;i++,start++)\r
+               if (audiosegs[start])\r
+                       MM_SetPurge (&(memptr)audiosegs[start],3);              // make purgable\r
+\r
+\r
+       if (AdLibPresent)\r
+       {\r
+               start = STARTADLIBSOUNDS;\r
+               for (i=0;i<NUMSOUNDS;i++,start++)\r
+                       if (audiosegs[start])\r
+                               MM_SetPurge (&(memptr)audiosegs[start],3);              // make purgable\r
+       }\r
+\r
+       if (SoundBlasterPresent)\r
+               GE_FreeAllDigiSounds();\r
+}\r
+\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+// PlaySample() -- Plays a DIGITIZED sample using SoundBlaster OR SoundSource\r
+//\r
+// PARAMETERS : Sample Number (Corresponding to ASArray "DigiSounds[]".\r
+//\r
+void PlaySample(unsigned SampleNum)\r
+{\r
+\r
+       if (!DigiSounds[SampleNum].data)\r
+               TrashProg("PlaySample - Trying to play an unloaded digi sound!");\r
+\r
+\r
+       switch (SoundMode)                              // external variable in ID_SD for sound mode..\r
+       {\r
+               case sdm_SoundBlaster:\r
+               case sdm_SoundBlasterAdLib:\r
+                       SDL_SBPlaySample(MK_FP(DigiSounds[SampleNum].data,0));\r
+                       break;\r
+\r
+               default:\r
+                       TrashProg("PlaySample() - incorrect SoundMode for PlaySample()");\r
+                       break;\r
+       }\r
+}\r
+\r
+\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+// SelectDigiAudio() -- This routine intergrates multi sound hardware with\r
+//                                                             id's routines.\r
+//\r
+void SelectDigiAudio(AudioDeviceType Device)\r
+{\r
+       switch (Device)\r
+       {\r
+               case ged_SoundSource:\r
+               case ged_SoundBlaster:\r
+                       break;\r
+       }\r
+}\r
+#endif\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// DisplayGameList()\r
+//\r
+void DisplayGameList(short winx, short winy, short list_width, short list_height)\r
+{\r
+       #define SPACES 2\r
+\r
+       short width,col,row,orgcol,games_printed=0,h;\r
+\r
+       // Possibly shrink window.\r
+       //\r
+       h = (NumGames / list_width) + ((NumGames % list_width) > 0);\r
+       if (h < list_height)\r
+               list_height = h;\r
+\r
+       // Open window and print header...\r
+       //\r
+       US_DrawWindow(winx,winy,list_width*(8+SPACES*2),list_height+3);\r
+       US_CPrintLine("LIST OF SAVED GAMES");\r
+       US_Print("\n");\r
+\r
+       col = orgcol = PrintX;\r
+       row = PrintY;\r
+\r
+       // Display as many 'save game' files as can fit in the window.\r
+       //\r
+       width = list_width;\r
+       while ((games_printed<NumGames) && (list_height))\r
+       {\r
+               // Print filename and padding spaces.\r
+               //\r
+               US_Printxy(col+(SPACES*8),row,GameListNames[games_printed]);\r
+               col += 8*((SPACES*2)+8);\r
+\r
+               // Check for end-of-line or end-of-window.\r
+               //\r
+               width--;\r
+               if (!width)\r
+               {\r
+                       col = orgcol;\r
+                       row += 8;\r
+                       width = list_width;\r
+                       list_height--;\r
+                       US_Print("\n");\r
+               }\r
+\r
+               games_printed++;\r
+       }\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// ReadGameList()\r
+//\r
+void ReadGameList()\r
+{\r
+       struct ffblk ffblk;\r
+       short done,len;\r
+\r
+       NumGames = -1;\r
+       done = findfirst("*.sav",&ffblk,0);\r
+\r
+       while (!done)\r
+       {\r
+               if (NumGames == MAX_GAMELIST_NAMES)\r
+                       memcpy(GameListNames,GameListNames[1],MAX_GAMELIST_NAMES*sizeof(GameListNames[0]));\r
+               else\r
+                       NumGames++;\r
+\r
+               fnsplit(ffblk.ff_name,NULL,NULL,GameListNames[NumGames],NULL);\r
+\r
+               done=findnext(&ffblk);\r
+       }\r
+\r
+       NumGames++;\r
+}\r
+\r
+#if 0\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// CenterObj()\r
+//\r
+void CenterObj(objtype *obj, unsigned x, unsigned y)\r
+{\r
+       spritetabletype far *sprite;\r
+       unsigned width, height;\r
+\r
+       sprite=&spritetable[obj->baseshape+obj->curframe-STARTSPRITES];\r
+\r
+       width = (sprite->xh-sprite->xl+(1<<G_P_SHIFT)) >> 1;\r
+       if (obj->sprflags&sf_vertflip)\r
+       {\r
+               height = (sprite->yl-(sprite->height<<G_P_SHIFT) + sprite->yh+(1<<G_P_SHIFT)) >> 1;\r
+       }\r
+       else\r
+               height = (sprite->yh-sprite->yl+(1<<G_P_SHIFT)) >> 1;\r
+\r
+       obj->x = x-width;\r
+       obj->y = y-height;\r
+}\r
+#endif\r
+\r
+#if 0\r
+//-------------------------------------------------------------------------\r
+// cacheout()\r
+//-------------------------------------------------------------------------\r
+void cacheout(short s,short e)\r
+{\r
+       short i;\r
+\r
+       for(i=(s);i<=(e);i++)\r
+       {\r
+               grneeded[i]&=~ca_levelbit;\r
+               if (grsegs[i])\r
+                       MM_SetPurge(&grsegs[i],3);\r
+       }\r
+\r
+}\r
+\r
+//-------------------------------------------------------------------------\r
+// cachein()\r
+//-------------------------------------------------------------------------\r
+void cachein(short s,short e)\r
+{\r
+       short i;\r
+\r
+       for(i=(s);i<=(e);i++)\r
+       {\r
+               CA_MarkGrChunk(i);\r
+               if (grsegs[i])\r
+                       MM_SetPurge(&grsegs[i],0);\r
+       }\r
+}\r
+#endif\r
+\r
+#if 0\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// SetUpdateBlock()\r
+//\r
+void SetUpdateBlock(unsigned x,unsigned y,unsigned width,unsigned height,char refresh)\r
+{\r
+       eraseblocktype *erase;\r
+\r
+#if 0 //SP unsure if this is needed\r
+       x = (x+((MAPBORDER+MAPXLEFTOFFSET)<<4))>>3;\r
+       y += ((MAPBORDER+MAPYTOPOFFSET)<<4);\r
+#else\r
+       x = (x+(MAPBORDER<<4))>>3;\r
+       y += (MAPBORDER<<4);\r
+#endif\r
+       width >>= 3;\r
+\r
+       if (refresh & 1)\r
+       {\r
+               erase = eraselistptr[0]++;\r
+               erase->screenx=x;\r
+               erase->screeny=y;\r
+               erase->width=width;\r
+               erase->height=height;\r
+       }\r
+\r
+       if (refresh & 2)\r
+       {\r
+               erase = eraselistptr[1]++;\r
+               erase->screenx=x;\r
+               erase->screeny=y;\r
+               erase->width=width;\r
+               erase->height=height;\r
+       }\r
+}\r
+\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// ObjHeight\r
+//\r
+unsigned ObjHeight(objtype *obj)\r
+{\r
+       spritetabletype far *sprite;\r
+\r
+       sprite=&spritetable[obj->baseshape+obj->curframe-STARTSPRITES];\r
+\r
+       if (obj->sprflags&sf_vertflip)\r
+       {\r
+               return((sprite->yl-(sprite->height<<G_P_SHIFT) + sprite->yh+(1<<G_P_SHIFT)) >> 1);\r
+       }\r
+       else\r
+               return((sprite->yh-sprite->yl+(1<<G_P_SHIFT)) >> 1);\r
+}\r
+\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// ObjWidth\r
+//\r
+unsigned ObjWidth(objtype *obj)\r
+{\r
+       spritetabletype far *sprite;\r
+\r
+       sprite=&spritetable[obj->baseshape+obj->curframe-STARTSPRITES];\r
+\r
+       return((sprite->xh-sprite->xl+(1<<G_P_SHIFT)) >> 1);\r
+}\r
+#endif\r
+\r
+#if 0\r
+//--------------------------------------------------------------------------\r
+// visible_on()\r
+//--------------------------------------------------------------------------\r
+boolean visible_on(objtype *obj)\r
+{\r
+       if (!(obj->flags & of_visible))\r
+       {\r
+               obj->needtoreact=true;\r
+               obj->flags |= of_visible;\r
+               return(true);\r
+       }\r
+\r
+       return(false);\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// visible_off()\r
+//--------------------------------------------------------------------------\r
+boolean visible_off(objtype *obj)\r
+{\r
+       if (obj->flags & of_visible)\r
+       {\r
+               obj->needtoreact=true;\r
+               obj->flags &= ~of_visible;\r
+               return(true);\r
+       }\r
+\r
+       return(false);\r
+}\r
+#endif\r
+\r
+\r
+/*\r
+===================\r
+=\r
+= FizzleFade\r
+=\r
+===================\r
+*/\r
+\r
+#define PIXPERFRAME     10000\r
+\r
+void FizzleFade (unsigned source, unsigned dest,\r
+       unsigned width,unsigned height, boolean abortable)\r
+{\r
+       unsigned        drawofs,pagedelta;\r
+       unsigned        char maskb[8] = {1,2,4,8,16,32,64,128};\r
+       unsigned        x,y,p,frame;\r
+       long            rndval;\r
+       ScanCode                         lastLastScan=LastScan=0;\r
+\r
+       width--;\r
+       height--;\r
+\r
+       pagedelta = dest-source;\r
+//     VW_SetScreen (dest,0);\r
+       rndval = 1;\r
+       y = 0;\r
+\r
+asm     mov     es,[screenseg]\r
+asm     mov     dx,SC_INDEX\r
+asm     mov     al,SC_MAPMASK\r
+asm     out     dx,al\r
+\r
+       TimeCount=frame=0;\r
+       do      // while (1)\r
+       {\r
+               if ((abortable) || (Flags & FL_QUICK))\r
+               {\r
+                       IN_ReadControl(0,&control);\r
+                       if (control.button0 || control.button1 || (lastLastScan != LastScan)\r
+                       || Keyboard[sc_Escape] || (Flags & FL_QUICK))\r
+                       {\r
+                               VW_ScreenToScreen (source,dest,(width+1)/8,height+1);\r
+                               goto exitfunc;\r
+                       }\r
+               }\r
+\r
+               for (p=0;p<PIXPERFRAME;p++)\r
+               {\r
+                       //\r
+                       // seperate random value into x/y pair\r
+                       //\r
+                       asm     mov     ax,[WORD PTR rndval]\r
+                       asm     mov     dx,[WORD PTR rndval+2]\r
+                       asm     mov     bx,ax\r
+                       asm     dec     bl\r
+                       asm     mov     [BYTE PTR y],bl                 // low 8 bits - 1 = y xoordinate\r
+                       asm     mov     bx,ax\r
+                       asm     mov     cx,dx\r
+                       asm     shr     cx,1\r
+                       asm     rcr     bx,1\r
+                       asm     shr     bx,1\r
+                       asm     shr     bx,1\r
+                       asm     shr     bx,1\r
+                       asm     shr     bx,1\r
+                       asm     shr     bx,1\r
+                       asm     shr     bx,1\r
+                       asm     shr     bx,1\r
+                       asm     mov     [x],bx                                  // next 9 bits = x xoordinate\r
+                       //\r
+                       // advance to next random element\r
+                       //\r
+                       asm     shr     dx,1\r
+                       asm     rcr     ax,1\r
+                       asm     jnc     noxor\r
+                       asm     xor     dx,0x0001\r
+                       asm     xor     ax,0x2000\r
+noxor:\r
+                       asm     mov     [WORD PTR rndval],ax\r
+                       asm     mov     [WORD PTR rndval+2],dx\r
+\r
+                       if (x>width || y>height)\r
+                               continue;\r
+                       drawofs = source+ylookup[y];\r
+\r
+                       asm     mov     cx,[x]\r
+                       asm     mov     si,cx\r
+                       asm     and     si,7\r
+                       asm     mov dx,GC_INDEX\r
+                       asm     mov al,GC_BITMASK\r
+                       asm     mov     ah,BYTE PTR [maskb+si]\r
+                       asm     out dx,ax\r
+\r
+                       asm     mov     si,[drawofs]\r
+                       asm     shr     cx,1\r
+                       asm     shr     cx,1\r
+                       asm     shr     cx,1\r
+                       asm     add     si,cx\r
+                       asm     mov     di,si\r
+                       asm     add     di,[pagedelta]\r
+\r
+                       asm     mov     dx,GC_INDEX\r
+                       asm     mov     al,GC_READMAP                   // leave GC_INDEX set to READMAP\r
+                       asm     out     dx,al\r
+\r
+                       asm     mov     dx,SC_INDEX+1\r
+                       asm     mov     al,1\r
+                       asm     out     dx,al\r
+                       asm     mov     dx,GC_INDEX+1\r
+                       asm     mov     al,0\r
+                       asm     out     dx,al\r
+\r
+                       asm     mov     bl,[es:si]\r
+                       asm     xchg [es:di],bl\r
+\r
+                       asm     mov     dx,SC_INDEX+1\r
+                       asm     mov     al,2\r
+                       asm     out     dx,al\r
+                       asm     mov     dx,GC_INDEX+1\r
+                       asm     mov     al,1\r
+                       asm     out     dx,al\r
+\r
+                       asm     mov     bl,[es:si]\r
+                       asm     xchg [es:di],bl\r
+\r
+                       asm     mov     dx,SC_INDEX+1\r
+                       asm     mov     al,4\r
+                       asm     out     dx,al\r
+                       asm     mov     dx,GC_INDEX+1\r
+                       asm     mov     al,2\r
+                       asm     out     dx,al\r
+\r
+                       asm     mov     bl,[es:si]\r
+                       asm     xchg [es:di],bl\r
+\r
+                       asm     mov     dx,SC_INDEX+1\r
+                       asm     mov     al,8\r
+                       asm     out     dx,al\r
+                       asm     mov     dx,GC_INDEX+1\r
+                       asm     mov     al,3\r
+                       asm     out     dx,al\r
+\r
+                       asm     mov     bl,[es:si]\r
+                       asm     xchg [es:di],bl\r
+\r
+                       if (rndval == 1)                // entire sequence has been completed\r
+                               goto exitfunc;\r
+               }\r
+//             frame++;\r
+//             while (TimeCount<frame);         // don't go too fast\r
+\r
+       } while (1);\r
+\r
+exitfunc:;\r
+       EGABITMASK(255);\r
+       EGAMAPMASK(15);\r
+       return;\r
+}\r
+\r
+#if 0\r
+//-------------------------------------------------------------------------\r
+// mprintf()\r
+//-------------------------------------------------------------------------\r
+void mprintf(char *msg, ...)\r
+{\r
+       static char x=0;\r
+       static char y=0;\r
+       static char far *video = MK_FP(0xb000,0x0000);\r
+       char buffer[100],*ptr;\r
+\r
+       va_list(ap);\r
+\r
+       va_start(ap,msg);\r
+\r
+       vsprintf(buffer,msg,ap);\r
+\r
+       ptr = buffer;\r
+       while (*ptr)\r
+       {\r
+               switch (*ptr)\r
+               {\r
+                       case '\n':\r
+                               if (y >= 23)\r
+                               {\r
+                                       video -= (x<<1);\r
+                                       _fmemcpy(MK_FP(0xb000,0x0000),MK_FP(0xb000,0x00a0),3840);\r
+                               }\r
+                               else\r
+                               {\r
+                                       y++;\r
+                                       video += ((80-x)<<1);\r
+                               }\r
+                               x=0;\r
+                       break;\r
+\r
+                       default:\r
+                               *video = *ptr;\r
+                               video[1] = 15;\r
+                               video += 2;\r
+                               x++;\r
+                       break;\r
+               }\r
+               ptr++;\r
+       }\r
+\r
+       va_end(ap);\r
+}\r
+#endif\r
+\r
+#if 0\r
+\r
+//--------------------------------------------------------------------------\r
+//                                                              FULL SCREEN REFRESH/ANIM MANAGERS\r
+//--------------------------------------------------------------------------\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//  InitLatchRefresh() -- Loads an ILBM (JAMPAK'd) into the Master Latch\r
+//                                                               to be used as the background refresh screen...\r
+//\r
+void InitLatchRefresh(char *filename)\r
+{\r
+       struct Shape RefreshShp;\r
+       short yofs;\r
+\r
+       VW_ClearVideo(0);\r
+\r
+       if (LoadShape(filename,&RefreshShp))\r
+               TrashProg("Can't load %s",filename);\r
+\r
+       VW_SetLineWidth(RefreshShp.BPR);\r
+\r
+       yofs = masterswap/SCREENWIDTH;\r
+       MoveGfxDst(0,yofs);                                                             // Handle title screen\r
+       UnpackEGAShapeToScreen(&RefreshShp,0,0);\r
+       FreeShape(&RefreshShp);\r
+\r
+       MoveScreen(0,0);\r
+       VW_InitDoubleBuffer();\r
+\r
+       RF_NewPosition(0,0);\r
+\r
+       RF_Refresh();\r
+\r
+       SetUpdateBlock(0,0,RefreshShp.bmHdr.w,RefreshShp.bmHdr.h,3);\r
+}\r
+\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// InitFullScreenAnim() -- Initialize ALL necessary functions for full screen\r
+//                                                                     animation types.\r
+//\r
+// filename - filename for background lbm.\r
+// SpawnAll - spawn function to call to spawn all inital objects..\r
+//\r
+void InitFullScreenAnim(char *filename, void (*SpawnAll)())\r
+{\r
+       unsigned old_flags;\r
+\r
+       old_flags = GE_SystemFlags.flags;\r
+       GE_SystemFlags.flags &= ~(GEsf_Tiles | GEsf_Panning | GEsf_RefreshVec);\r
+\r
+       CA_ClearMarks();\r
+\r
+       RFL_InitSpriteList();\r
+       InitObjArray();\r
+\r
+       if (SpawnAll)\r
+               SpawnAll();\r
+\r
+       CA_CacheMarks(NULL);\r
+\r
+       CalcInactivate();\r
+       CalcSprites();\r
+\r
+       InitLatchRefresh(filename);\r
+\r
+       RF_ForceRefresh();\r
+       RF_ForceRefresh();\r
+\r
+       GE_SystemFlags.flags = old_flags;\r
+}\r
+\r
+\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// DoFullScreenAnim()  -- a General "Do-Every-Thing" function that\r
+//                                                                     displays a full screen animation...\r
+//\r
+// filename - Filename of background lbm\r
+//     SpawnAll        - Function to call to spawn all inital objects (REQUIRED)\r
+//     CheckKey - Function to call every cycle - The function called must\r
+//                               return a value of greater than zero (0) to terminate the\r
+//                               animation cycle.                                                                        (REQUIRED)\r
+//     CleanUp - Function to call upon exiting the animation. (OPTIONAL)\r
+//\r
+void DoFullScreenAnim(char *filename, void (*SpawnAll)(), short (*CheckKey)(),void (*CleanUp)())\r
+{\r
+       unsigned old_flags;\r
+       boolean ExitAnim = false;\r
+\r
+       // Save off the current system flags....\r
+\r
+       old_flags = GE_SystemFlags.flags;\r
+       GE_SystemFlags.flags &= ~(GEsf_Tiles | GEsf_Panning);\r
+\r
+//     randomize();\r
+\r
+       // Init refresh latch screen, initalize all object, and setup video mode.\r
+\r
+       InitFullScreenAnim(filename,SpawnAll);\r
+\r
+       VW_FadeIn();\r
+\r
+       while (!ExitAnim)\r
+       {\r
+               CalcInactivate();\r
+               CalcSprites();\r
+               RF_Refresh();\r
+\r
+               ExitAnim = (boolean)CheckKey();\r
+       }\r
+\r
+//     RemoveBOBList(player);\r
+//     CalcInactivate();\r
+\r
+       if (CleanUp)\r
+               CleanUp();\r
+\r
+       // Restore old system flags...\r
+\r
+       GE_SystemFlags.flags = old_flags;\r
+}\r
+\r
+#endif\r
+\r
+//--------------------------------------------------------------------------\r
+// FindFile()\r
+//--------------------------------------------------------------------------\r
+boolean FindFile(char *filename,char *disktext,char disknum)\r
+{\r
+       extern boolean splitscreen;\r
+\r
+       char command[100];\r
+       char choices[]={sc_Escape,sc_Space,0},drive[2];\r
+       boolean fadeitout=false,rt_code=2;\r
+\r
+       if (!disktext)\r
+               disktext = GAMENAME;\r
+       drive[0] = getdisk() + 'A';\r
+       drive[1] = 0;\r
+       while (rt_code == 2)\r
+       {\r
+               if (Verify(filename))\r
+                       rt_code = true;\r
+               else\r
+               {\r
+                       if (ge_textmode)\r
+                       {\r
+                               clrscr();\r
+                               gotoxy(1,1);\r
+                               printf("\nInsert %s disk %d into drive %s.\n",disktext,disknum,drive);\r
+                               printf("Press SPACE to continue, ESC to abort.\n");\r
+                       }\r
+                       else\r
+                       {\r
+                               if (splitscreen)\r
+                               {\r
+                                       bufferofs = displayofs = screenloc[screenpage];\r
+                                       CenterWindow(38,5);\r
+                               }\r
+                               else\r
+                               {\r
+                                       bufferofs = displayofs = 0;\r
+                                       US_CenterWindow(38,5);\r
+                               }\r
+\r
+                               strcpy(command,"\nInsert ");\r
+                               strcat(command,disktext);\r
+                               strcat(command," disk");\r
+                               if (disknum != -1)\r
+                               {\r
+                                       strcat(command," ");\r
+                                       itoa(disknum,command+strlen(command),10);\r
+                               }\r
+                               strcat(command," into drive ");\r
+                               strcat(command,drive);\r
+                               strcat(command,".");\r
+                               US_CPrint(command);\r
+                               US_CPrint("Press SPACE to continue, ESC to abort.");\r
+                       }\r
+\r
+                       sound(300);\r
+                       VW_WaitVBL(20);\r
+                       nosound();\r
+\r
+                       if (!ge_textmode)\r
+                       {\r
+                               if (screenfaded)\r
+                               {\r
+                                       VW_FadeIn();\r
+                                       fadeitout=true;\r
+                               }\r
+                       }\r
+                       if (GetKeyChoice(choices,true) == sc_Escape)\r
+                               rt_code = false;\r
+               }\r
+       }\r
+\r
+       if (!ge_textmode)\r
+               if (fadeitout)\r
+                       VW_FadeOut();\r
+\r
+       return(rt_code);\r
+}\r
+\r
+#if 0\r
+//--------------------------------------------------------------------------\r
+// CacheAll()\r
+//--------------------------------------------------------------------------\r
+void CacheAV(char *title)\r
+{\r
+       if (Verify("EGAGRAPH."EXT))\r
+       {\r
+               CA_CacheMarks(title);\r
+               if (!FindFile("EGAGRAPH."EXT,AUDIO_DISK))\r
+                       TrashProg("Can't find graphics files.");\r
+\r
+// cache in audio\r
+\r
+               current_disk = AUDIO_DISK;\r
+       }\r
+       else\r
+       {\r
+\r
+// cache in audio\r
+\r
+               if (!FindFile("EGAGRAPH."EXT,VIDEO_DISK))\r
+                       TrashProg("Can't find audio files.");\r
+               CA_CacheMarks(title);\r
+\r
+               current_disk = VIDEO_DISK;\r
+       }\r
+}\r
+#endif\r
+\r
+#ifdef TEXT_PRESENTER\r
+//--------------------------------------------------------------------------\r
+//\r
+//                         TEXT PRESENTER CODE\r
+//\r
+//--------------------------------------------------------------------------\r
+\r
+typedef enum pi_stype {pis_pic2x,pis_latch_pic} pi_stype;\r
+\r
+\r
+typedef struct {                                               // 4 bytes\r
+       unsigned shapenum;\r
+       pi_stype shape_type;\r
+} pi_shape_info;\r
+\r
+#define pia_active     0x01\r
+\r
+typedef struct {                                               // 10 bytes\r
+       char baseshape;\r
+       char frame;\r
+       char maxframes;\r
+       short delay;\r
+       short maxdelay;\r
+       short x,y;\r
+} pi_anim_info;\r
+\r
+       #define PI_CASE_SENSITIVE\r
+\r
+       #define PI_RETURN_CHAR                  '\n'\r
+       #define PI_CONTROL_CHAR                 '^'\r
+\r
+       #define PI_CNVT_CODE(c1,c2)     ((c1)|(c2<<8))\r
+\r
+// shape table provides a way for the presenter to access and\r
+// display any shape.\r
+//\r
+pi_shape_info far pi_shape_table[] = {\r
+\r
+                                                                                       {BOLTOBJPIC,pis_pic2x},                         // 0\r
+                                                                                       {NUKEOBJPIC,pis_pic2x},\r
+                                                                                       {SKELETON_1PIC,pis_pic2x},\r
+                                                                                       {EYE_WALK1PIC,pis_pic2x},\r
+                                                                                       {ZOMB_WALK1PIC,pis_pic2x},\r
+\r
+                                                                                       {TIMEOBJ1PIC,pis_pic2x},                        // 5\r
+                                                                                       {POTIONOBJPIC,pis_pic2x},\r
+                                                                                       {RKEYOBJPIC,pis_pic2x},\r
+                                                                                       {YKEYOBJPIC,pis_pic2x},\r
+                                                                                       {GKEYOBJPIC,pis_pic2x},\r
+\r
+                                                                                       {BKEYOBJPIC,pis_pic2x},                         // 10\r
+                                                                                       {RGEM1PIC,pis_pic2x},\r
+                                                                                       {GGEM1PIC,pis_pic2x},\r
+                                                                                       {BGEM1PIC,pis_pic2x},\r
+                                                                                       {YGEM1PIC,pis_pic2x},\r
+\r
+                                                                                       {PGEM1PIC,pis_pic2x},                           // 15\r
+                                                                                       {CHESTOBJPIC,pis_pic2x},\r
+                                                                                       {PSHOT1PIC,pis_pic2x},\r
+                                                                                       {RED_DEMON1PIC,pis_pic2x},\r
+                                                                                       {MAGE1PIC,pis_pic2x},\r
+\r
+                                                                                       {BAT1PIC,pis_pic2x},                                    // 20\r
+                                                                                       {GREL1PIC,pis_pic2x},\r
+                                                                                       {GODESS_WALK1PIC,pis_pic2x},\r
+                                                                                       {ANT_WALK1PIC,pis_pic2x},\r
+                                                                                       {FATDEMON_WALK1PIC,pis_pic2x},\r
+\r
+                                                                                       {SUCCUBUS_WALK1PIC,pis_pic2x},  //25\r
+                                                                                       {TREE_WALK1PIC,pis_pic2x},\r
+                                                                                       {DRAGON_WALK1PIC,pis_pic2x},\r
+                                                                                       {BUNNY_LEFT1PIC,pis_pic2x},\r
+\r
+};\r
+\r
+// anim table holds info about each different animation.\r
+//\r
+pi_anim_info far pi_anim_table[] = {{0,0,3,0,10},              // 0            BOLT\r
+                                                                                               {1,0,3,0,10},     //       NUKE\r
+                                                                                               {2,0,4,0,10},     //       SKELETON\r
+                                                                                               {3,0,3,0,10},     //       EYE\r
+                                                                                               {4,0,3,0,10},     //       ZOMBIE\r
+\r
+                                                                                               {5,0,2,0,10},     // 5     FREEZE TIME\r
+                                                                                               {11,0,2,0,10},    //                    RED GEM\r
+                                                                                               {12,0,2,0,10},    //       GREEN GEM\r
+                                                                                               {13,0,2,0,10},    //       BLUE GEM\r
+                                                                                               {14,0,2,0,10},    //       YELLOW GEM\r
+\r
+                                                                                               {15,0,2,0,10},    // 10    PURPLE GEM\r
+                                                                                               {17,0,2,0,10},    //       PLAYER'S SHOT\r
+                                                                                               {18,0,3,0,10},    //       RED DEMON\r
+                                                                                               {19,0,2,0,10},    //       MAGE\r
+                                                                                               {20,0,4,0,10},    //       BAT\r
+\r
+                                                                                               {21,0,2,0,10},    // 15    GRELMINAR\r
+                                                                                               {22,0,3,0,10},    //       GODESS\r
+                                                                                               {23,0,3,0,10},    //       ANT\r
+                                                                                               {24,0,4,0,10},    //       FAT DEMON\r
+                                                                                               {25,0,4,0,10},    //       SUCCUBUS\r
+\r
+                                                                                               {26,0,2,0,10},    // 20    TREE\r
+                                                                                               {27,0,3,0,10},    //       DRAGON\r
+                                                                                               {28,0,2,0,10},    //       BUNNY\r
+};\r
+\r
+// anim list is created on the fly from the anim table...\r
+// this allows a single animation to be display in more than\r
+// one place...\r
+//\r
+pi_anim_info far pi_anim_list[PI_MAX_ANIMS];\r
+boolean pi_recursing=false;\r
+\r
+//--------------------------------------------------------------------------\r
+// Presenter() - DANGEROUS DAVE "Presenter()" is more up-to-date than this.\r
+//\r
+//\r
+// Active control codes:\r
+//\r
+//  ^CE                                - center text between 'left margin' and 'right margin'\r
+//  ^FCn                               - set font color\r
+//  ^LMnnn                     - set left margin (if 'nnn' == "fff" uses current x)\r
+//  ^RMnnn                     - set right margin (if 'nnn' == "fff" uses current x)\r
+//  ^EP                                - end of page (waits for up/down arrow)\r
+//  ^PXnnn                     - move x to coordinate 'n'\r
+//  ^PYnnn                     - move y to coordinate 'n'\r
+//  ^XX                                - exit presenter\r
+//  ^LJ                                - left justify  --\ ^RJ doesn't handle imbedded control\r
+//  ^RJ                                - right justify --/ codes properly. Use with caution.\r
+//  ^BGn                               - set background color\r
+//  ^ANnn                      - define animation\r
+//  ^SHnnn                     - display shape 'n' at current x,y\r
+//\r
+//\r
+// Future control codes:\r
+//\r
+//  ^OBnnn                     - activate object 'n'\r
+//  ^FL                                - flush to edges (whatever it's called)\r
+//\r
+//\r
+// Other info:\r
+//\r
+// All 'n' values are hex numbers (0 - f), case insensitive.\r
+// The number of N's listed is the number of digits REQUIRED by that control\r
+// code. (IE: ^LMnnn MUST have 3 values! --> 003, 1a2, 01f, etc...)\r
+//\r
+// If a line consists only of control codes, the cursor is NOT advanced\r
+// to the next line (the ending <CR><LF> is skipped). If just ONE non-control\r
+// code is added, the number "8" for example, then the "8" is displayed\r
+// and the cursor is advanced to the next line.\r
+//\r
+// ^CE must be on the same line as the text it should center!\r
+//\r
+//--------------------------------------------------------------------------\r
+void Presenter(PresenterInfo *pi)\r
+{\r
+#ifdef AMIGA\r
+       XBitMap **font = pi->font;\r
+\r
+       #define ch_width(ch) font[ch]->pad\r
+       char font_height = font[0]->Rows;\r
+#else\r
+       fontstruct _seg *font = (fontstruct _seg *)grsegs[STARTFONT];\r
+\r
+       #define MAX_PB 150\r
+       #define ch_width(ch) font->width[ch]\r
+       char font_height = font->height-1;              // "-1" squeezes font vertically\r
+       char pb[MAX_PB];\r
+       short length;\r
+#endif\r
+\r
+       enum {jm_left,jm_right,jm_flush};\r
+       char justify_mode = jm_left;\r
+       boolean centering=false;\r
+\r
+       short bgcolor = pi->bgcolor;\r
+       short xl=pi->xl,yl=pi->yl,xh=pi->xh,yh=pi->yh;\r
+       short cur_x = xl, cur_y = yl;\r
+       char far *first_ch = pi->script[0];\r
+\r
+       char far *scan_ch,temp;\r
+       short scan_x,PageNum=0,numanims=0;\r
+       boolean up_released=true,dn_released=true;\r
+       boolean presenting=true,start_of_line=true;\r
+\r
+// if set background is first thing in file, make sure initial screen\r
+// clear uses this color.\r
+//\r
+       if (!_fstrncmp(first_ch,"^BG",3))\r
+               bgcolor = PI_VALUE(first_ch+3,1);\r
+\r
+       if (!pi_recursing)\r
+       {\r
+               PurgeAllGfx();\r
+               CachePage(first_ch);\r
+       }\r
+       VW_Bar(xl,yl,xh-xl+1,yh-yl+1,bgcolor);\r
+\r
+       while (presenting)\r
+       {\r
+//\r
+// HANDLE WORD-WRAPPING TEXT\r
+//\r
+               if (*first_ch != PI_CONTROL_CHAR)\r
+               {\r
+                       start_of_line = false;\r
+\r
+       // Parse script until one of the following:\r
+       //\r
+       // 1) text extends beyond right margin\r
+       // 2) NULL termination is reached\r
+       // 3) PI_RETURN_CHAR is reached\r
+       // 4) PI_CONTROL_CHAR is reached\r
+       //\r
+                       scan_x = cur_x;\r
+                       scan_ch = first_ch;\r
+                       while ((scan_x+ch_width(*scan_ch) <= xh) && (*scan_ch) &&\r
+                                        (*scan_ch != PI_RETURN_CHAR) && (*scan_ch != PI_CONTROL_CHAR))\r
+                               scan_x += ch_width(*scan_ch++);\r
+\r
+       // if 'text extends beyond right margin', scan backwards for\r
+       // a SPACE\r
+       //\r
+                       if (scan_x+ch_width(*scan_ch) > xh)\r
+                       {\r
+                               short last_x = scan_x;\r
+                               char far *last_ch = scan_ch;\r
+\r
+                               while ((scan_ch != first_ch) && (*scan_ch != ' ') && (*scan_ch != PI_RETURN_CHAR))\r
+                                       scan_x -= ch_width(*scan_ch--);\r
+\r
+                               if (scan_ch == first_ch)\r
+                                       scan_ch = last_ch, scan_x = last_x;\r
+                       }\r
+\r
+       // print current line\r
+       //\r
+#ifdef AMIGA\r
+                       while (first_ch < scan_ch)\r
+                       {\r
+                               qBlit(font[*first_ch++],pi->dst,cur_x,cur_y);\r
+//                             qBlit(font[*first_ch],pi->dst,cur_x,cur_y);\r
+//                             cur_x += ch_width(*first_ch++);\r
+                       }\r
+#else\r
+                       temp = *scan_ch;\r
+                       *scan_ch = 0;\r
+\r
+                       if ((justify_mode == jm_right)&&(!centering))\r
+                       {\r
+                               unsigned width,height;\r
+\r
+                               VWL_MeasureString(first_ch,&width,&height,font);\r
+                               cur_x = xh-width;\r
+                               if (cur_x < xl)\r
+                                       cur_x = xl;\r
+                       }\r
+\r
+                       px = cur_x;\r
+                       py = cur_y;\r
+\r
+                       length = scan_ch-first_ch+1;            // USL_DrawString only works with\r
+                       if (length > MAX_PB)\r
+                               Quit("Presenter(): Print buffer string too long!");\r
+                       _fmemcpy(pb,first_ch,length);    // near pointers...\r
+\r
+                       if (*first_ch != '\n')\r
+                               USL_DrawString(pb);\r
+\r
+                       *scan_ch = temp;\r
+                       first_ch = scan_ch;\r
+#endif\r
+                       cur_x = scan_x;\r
+                       centering = false;\r
+\r
+       // skip SPACES / RETURNS at end of line\r
+       //\r
+                       if ((*first_ch==' ') || (*first_ch==PI_RETURN_CHAR))\r
+                               first_ch++;\r
+\r
+       // PI_CONTROL_CHARs don't advance to next character line\r
+       //\r
+                       if (*scan_ch != PI_CONTROL_CHAR)\r
+                       {\r
+                               cur_x = xl;\r
+                               cur_y += font_height;\r
+                       }\r
+               }\r
+               else\r
+//\r
+// HANDLE CONTROL CODES\r
+//\r
+               {\r
+                       PresenterInfo endmsg;\r
+                       pi_anim_info far *anim;\r
+                       pi_shape_info far *shape_info;\r
+                       unsigned shapenum;\r
+                       short length;\r
+                       char far *s;\r
+\r
+                       if (first_ch[-1] == '\n')\r
+                               start_of_line = true;\r
+\r
+                       first_ch++;\r
+#ifndef PI_CASE_SENSITIVE\r
+                       *first_ch=toupper(*first_ch);\r
+                       *(first_ch+1)=toupper(*(first_ch+1));\r
+#endif\r
+                       switch (*((unsigned far *)first_ch)++)\r
+                       {\r
+               // CENTER TEXT ------------------------------------------------------\r
+               //\r
+                               case PI_CNVT_CODE('C','E'):\r
+                                       length = 0;\r
+                                       s = first_ch;\r
+                                       while (*s && (*s != PI_RETURN_CHAR))\r
+                                       {\r
+                                               switch (*s)\r
+                                               {\r
+                                                       case PI_CONTROL_CHAR:\r
+                                                               s++;\r
+                                                               switch (*((unsigned *)s)++)\r
+                                                               {\r
+#ifndef AMIGA\r
+                                                                       case PI_CNVT_CODE('F','C'):\r
+                                                                       case PI_CNVT_CODE('B','G'):\r
+                                                                               s++;\r
+                                                                       break;\r
+#endif\r
+\r
+                                                                       case PI_CNVT_CODE('L','M'):\r
+                                                                       case PI_CNVT_CODE('R','M'):\r
+                                                                       case PI_CNVT_CODE('S','H'):\r
+                                                                       case PI_CNVT_CODE('P','X'):\r
+                                                                       case PI_CNVT_CODE('P','Y'):\r
+                                                                               s += 3;\r
+                                                                       break;\r
+\r
+                                                                       case PI_CNVT_CODE('L','J'):\r
+                                                                       case PI_CNVT_CODE('R','J'):\r
+                                                                               // No parameters to pass over!\r
+                                                                       break;\r
+                                                               }\r
+                                                       break;\r
+\r
+                                                       default:\r
+                                                               length += ch_width(*s++);\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                                       cur_x = ((xh-xl+1)-length)/2;\r
+                                       centering = true;\r
+                               break;\r
+\r
+               // DRAW SHAPE -------------------------------------------------------\r
+               //\r
+                               case PI_CNVT_CODE('S','H'):\r
+                                       shapenum = PI_VALUE(first_ch,3);\r
+                                       first_ch += 3;\r
+                                       shape_info = &pi_shape_table[shapenum];\r
+                                       switch (shape_info->shape_type)\r
+                                       {\r
+                                               short width;\r
+\r
+                                               case pis_pic2x:\r
+                                                       cur_x = ((cur_x+2) + 7) & 0xFFF8;\r
+                                                       width=BoxAroundPic(cur_x-2,cur_y-1,shape_info->shapenum,pi);\r
+                                                       VW_DrawPic2x(cur_x>>3,cur_y,shape_info->shapenum);\r
+                                                       cur_x += width;\r
+                                               break;\r
+                                       }\r
+                               break;\r
+\r
+               // INIT ANIMATION ---------------------------------------------------\r
+               //\r
+                               case PI_CNVT_CODE('A','N'):\r
+                                       shapenum = PI_VALUE(first_ch,2);\r
+                                       first_ch += 2;\r
+                                       _fmemcpy(&pi_anim_list[numanims],&pi_anim_table[shapenum],sizeof(pi_anim_info));\r
+                                       anim = &pi_anim_list[numanims++];\r
+                                       shape_info = &pi_shape_table[anim->baseshape+anim->frame];\r
+                                       switch (shape_info->shape_type)\r
+                                       {\r
+                                               short width;\r
+\r
+                                               case pis_pic2x:\r
+                                                       cur_x = ((cur_x+2) + 7) & 0xFFF8;\r
+                                                       width=BoxAroundPic(cur_x-2,cur_y-1,shape_info->shapenum,pi);\r
+                                                       VW_DrawPic2x(cur_x>>3,cur_y,shape_info->shapenum);\r
+                                                       anim->x = cur_x>>3;\r
+                                                       anim->y = cur_y;\r
+                                                       cur_x += width;\r
+                                               break;\r
+                                       }\r
+                               break;\r
+\r
+#ifndef AMIGA\r
+               // FONT COLOR -------------------------------------------------------\r
+               //\r
+                               case PI_CNVT_CODE('F','C'):\r
+                                       fontcolor = bgcolor ^ PI_VALUE(first_ch++,1);\r
+                               break;\r
+#endif\r
+\r
+               // BACKGROUND COLOR -------------------------------------------------\r
+               //\r
+                               case PI_CNVT_CODE('B','G'):\r
+                                       bgcolor = PI_VALUE(first_ch++,1);\r
+                               break;\r
+\r
+               // LEFT MARGIN ------------------------------------------------------\r
+               //\r
+                               case PI_CNVT_CODE('L','M'):\r
+                                       shapenum = PI_VALUE(first_ch,3);\r
+                                       first_ch += 3;\r
+                                       if (shapenum == 0xfff)\r
+                                               xl = cur_x;\r
+                                       else\r
+                                               xl = shapenum;\r
+                               break;\r
+\r
+               // RIGHT MARGIN -----------------------------------------------------\r
+               //\r
+                               case PI_CNVT_CODE('R','M'):\r
+                                       shapenum = PI_VALUE(first_ch,3);\r
+                                       first_ch += 3;\r
+                                       if (shapenum == 0xfff)\r
+                                               xh = cur_x;\r
+                                       else\r
+                                               xh = shapenum;\r
+                               break;\r
+\r
+               // SET X COORDINATE -------------------------------------------------\r
+               //\r
+                               case PI_CNVT_CODE('P','X'):\r
+                                       cur_x = PI_VALUE(first_ch,3);\r
+                                       first_ch += 3;\r
+                               break;\r
+\r
+               // SET Y COORDINATE -------------------------------------------------\r
+               //\r
+                               case PI_CNVT_CODE('P','Y'):\r
+                                       cur_y = PI_VALUE(first_ch,3);\r
+                                       first_ch += 3;\r
+                               break;\r
+\r
+               // LEFT JUSTIFY -----------------------------------------------------\r
+               //\r
+                               case PI_CNVT_CODE('L','J'):\r
+                                       justify_mode = jm_left;\r
+                               break;\r
+\r
+               // RIGHT JUSTIFY ----------------------------------------------------\r
+               //\r
+                               case PI_CNVT_CODE('R','J'):\r
+                                       justify_mode = jm_right;\r
+                               break;\r
+\r
+               // END OF PAGE ------------------------------------------------------\r
+               //\r
+                               case PI_CNVT_CODE('E','P'):\r
+                                       if (pi_recursing)\r
+                                               Quit("Presenter(): Can't use ^EP when recursing!");\r
+\r
+                                       endmsg.xl = cur_x;\r
+                                       endmsg.yl = yh-(font_height+2);\r
+                                       endmsg.xh = xh;\r
+                                       endmsg.yh = yh;\r
+                                       endmsg.bgcolor = bgcolor;\r
+                                       endmsg.ltcolor = pi->ltcolor;\r
+                                       endmsg.dkcolor = pi->dkcolor;\r
+                                       endmsg.script[0] = (char far *)"^CE^FC8-  ^FC0ESC ^FC8to return to play, or ^FC0ARROW KEYS ^FC8to page through more Help  -^XX";\r
+\r
+                                       pi_recursing = true;\r
+                                       Presenter(&endmsg);\r
+                                       pi_recursing = false;\r
+\r
+#ifndef AMIGA\r
+                                       if (screenfaded)\r
+                                               VW_FadeIn();\r
+                                       VW_ColorBorder(8 | 56);\r
+#endif\r
+\r
+                                       while (1)\r
+                                       {\r
+#ifndef AMIGA\r
+                                               long newtime;\r
+\r
+                                               VW_WaitVBL(1);\r
+                                               newtime = TimeCount;\r
+                                               realtics = tics = newtime-lasttimecount;\r
+                                               lasttimecount = newtime;\r
+#else\r
+                                               WaitVBL(1);\r
+                                               CALC_TICS;\r
+#endif\r
+                                               AnimatePage(numanims);\r
+                                               IN_ReadControl(0,&control);\r
+\r
+                                               if (control.button1 || Keyboard[1])\r
+                                               {\r
+                                                       presenting=false;\r
+                                                       break;\r
+                                               }\r
+                                               else\r
+                                               {\r
+                                                       if (ControlTypeUsed != ctrl_Keyboard)\r
+                                                               control.dir = dir_None;\r
+\r
+                                                       if (((control.dir == dir_North) || (control.dir == dir_West)) && (PageNum))\r
+                                                       {\r
+                                                               if (up_released)\r
+                                                               {\r
+                                                                       PageNum--;\r
+                                                                       up_released = false;\r
+                                                                       break;\r
+                                                               }\r
+                                                       }\r
+                                                       else\r
+                                                       {\r
+                                                               up_released = true;\r
+                                                               if (((control.dir == dir_South) || (control.dir == dir_East)) && (PageNum < pi->numpages-1))\r
+                                                               {\r
+                                                                       if (dn_released)\r
+                                                                       {\r
+                                                                               PageNum++;\r
+                                                                               dn_released = false;\r
+                                                                               break;\r
+                                                                       }\r
+                                                               }\r
+                                                               else\r
+                                                                       dn_released = true;\r
+                                                       }\r
+                                               }\r
+                                       }\r
+\r
+                                       cur_x = xl;\r
+                                       cur_y = yl;\r
+                                       if (cur_y+font_height > yh)\r
+                                               cur_y = yh-font_height;\r
+                                       first_ch = pi->script[PageNum];\r
+\r
+                                       numanims = 0;\r
+                                       PurgeAllGfx();\r
+                                       CachePage(first_ch);\r
+\r
+                                       VW_Bar(xl,yl,xh-xl+1,yh-yl+1,bgcolor);\r
+                               break;\r
+\r
+               // EXIT PRESENTER ---------------------------------------------------\r
+               //\r
+                               case PI_CNVT_CODE('X','X'):\r
+                                       presenting=false;\r
+                               break;\r
+                       }\r
+\r
+                       if ((first_ch[0] == ' ') && (first_ch[1] == '\n') && start_of_line)\r
+                               first_ch += 2;\r
+               }\r
+       }\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// ResetAnims()\r
+//--------------------------------------------------------------------------\r
+void ResetAnims()\r
+{\r
+       pi_anim_list[0].baseshape = -1;\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// AnimatePage()\r
+//--------------------------------------------------------------------------\r
+void AnimatePage(short numanims)\r
+{\r
+       pi_anim_info far *anim=pi_anim_list;\r
+       pi_shape_info far *shape_info;\r
+\r
+       while (numanims--)\r
+       {\r
+               anim->delay += tics;\r
+               if (anim->delay >= anim->maxdelay)\r
+               {\r
+                       anim->delay = 0;\r
+                       anim->frame++;\r
+                       if (anim->frame == anim->maxframes)\r
+                               anim->frame = 0;\r
+\r
+#if ANIM_USES_SHAPETABLE\r
+                       shape_info = &pi_shape_table[anim->baseshape+anim->frame];\r
+#else\r
+                       shape_info = &pi_shape_table[anim->baseshape];\r
+#endif\r
+                       switch (shape_info->shape_type)\r
+                       {\r
+                               case pis_pic2x:\r
+#if ANIM_USES_SHAPETABLE\r
+                                       VW_DrawPic2x(anim->x,anim->y,shape_info->shapenum);\r
+#else\r
+                                       VW_DrawPic2x(anim->x,anim->y,shape_info->shapenum+anim->frame);\r
+#endif\r
+                               break;\r
+                       }\r
+               }\r
+               anim++;\r
+       }\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// BoxAroundPic()\r
+//--------------------------------------------------------------------------\r
+short BoxAroundPic(short x1, short y1, unsigned picnum, PresenterInfo *pi)\r
+{\r
+       short x2,y2;\r
+\r
+       x2 = x1+(pictable[picnum-STARTPICS].width<<4)+2;\r
+       y2 = y1+(pictable[picnum-STARTPICS].height)+1;\r
+       VWB_Hlin(x1,x2,y1,pi->ltcolor);\r
+       VWB_Hlin(x1,x2,y2,pi->dkcolor);\r
+       VWB_Vlin(y1,y2,x1,pi->ltcolor);\r
+       VWB_Vlin(y1,y2,x1+1,pi->ltcolor);\r
+       VWB_Vlin(y1,y2,x2,pi->dkcolor);\r
+       VWB_Vlin(y1,y2,x2+1,pi->dkcolor);\r
+\r
+       return(x2-x1+1);\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// PurgeAllGfx()\r
+//--------------------------------------------------------------------------\r
+void PurgeAllGfx()\r
+{\r
+       ResetAnims();\r
+       FreeUpMemory();\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// CachePage()\r
+//--------------------------------------------------------------------------\r
+void CachePage(char far *script)\r
+{\r
+       pi_anim_info far *anim;\r
+       short loop;\r
+       unsigned shapenum;\r
+       boolean end_of_page=false;\r
+       short numanims=0;\r
+\r
+       while (!end_of_page)\r
+       {\r
+               switch (*script++)\r
+               {\r
+                       case PI_CONTROL_CHAR:\r
+#ifndef PI_CASE_SENSITIVE\r
+                               *script=toupper(*script);\r
+                               *(script+1)=toupper(*(script+1));\r
+#endif\r
+                               switch (*((unsigned far *)script)++)\r
+                               {\r
+                                       case PI_CNVT_CODE('S','H'):\r
+                                               shapenum = PI_VALUE(script,3);\r
+                                               script += 3;\r
+                                               CA_MarkGrChunk(pi_shape_table[shapenum].shapenum);\r
+                                       break;\r
+\r
+                                       case PI_CNVT_CODE('A','N'):\r
+                                               shapenum = PI_VALUE(script,2);\r
+                                               script += 2;\r
+\r
+                                               if (numanims++ == PI_MAX_ANIMS)\r
+                                                       Quit("CachePage(): Too many anims on one page.");\r
+\r
+                                               anim = &pi_anim_table[shapenum];\r
+#if ANIM_USES_SHAPETABLE\r
+                                               for (loop=anim->baseshape;loop < anim->baseshape+anim->maxframes; loop++)\r
+                                                       CA_MarkGrChunk(pi_shape_table[loop].shapenum);\r
+#else\r
+                                               shapenum = pi_shape_table[anim->baseshape].shapenum;\r
+                                               for (loop=0; loop<anim->maxframes; loop++)\r
+                                                       CA_MarkGrChunk(shapenum+loop);\r
+#endif\r
+                                       break;\r
+\r
+                                       case PI_CNVT_CODE('X','X'):\r
+                                       case PI_CNVT_CODE('E','P'):\r
+                                               end_of_page = true;\r
+                                       break;\r
+                               }\r
+                       break;\r
+               }\r
+       }\r
+\r
+       CA_CacheMarks(NULL);\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// PI_VALUE()\r
+//--------------------------------------------------------------------------\r
+unsigned PI_VALUE(char far *ptr,char num_nybbles)\r
+{\r
+       char ch,nybble,shift;\r
+       unsigned value=0;\r
+\r
+       for (nybble=0; nybble<num_nybbles; nybble++)\r
+       {\r
+               shift = 4*(num_nybbles-nybble-1);\r
+\r
+               ch = *ptr++;\r
+               if (isxdigit(ch))\r
+                       if (isalpha(ch))\r
+                               value |= (toupper(ch)-'A'+10)<<shift;\r
+                       else\r
+                               value |= (ch-'0')<<shift;\r
+       }\r
+\r
+       return(value);\r
+}\r
+\r
+//--------------------------------------------------------------------------\r
+// LoadPresenterScript()\r
+//--------------------------------------------------------------------------\r
+long LoadPresenterScript(char *filename,PresenterInfo *pi)\r
+{\r
+#pragma warn -pia\r
+       long size;\r
+\r
+       if (!(size=BLoad(filename,&pi->scriptstart)))\r
+               return(0);\r
+       pi->script[0] = MK_FP(pi->scriptstart,0);\r
+       pi->script[0][size-1] = 0;                                      // Last byte is trashed!\r
+       InitPresenterScript(pi);\r
+\r
+       return(size);\r
+#pragma warn +pia\r
+}\r
+\r
+//-------------------------------------------------------------------------\r
+// FreePresenterScript()\r
+//-------------------------------------------------------------------------\r
+void FreePresenterScript(PresenterInfo *pi)\r
+{\r
+       if (pi->script)\r
+               MM_FreePtr(&pi->scriptstart);\r
+}\r
+\r
+//-------------------------------------------------------------------------\r
+// InitPresenterScript()\r
+//-------------------------------------------------------------------------\r
+void InitPresenterScript(PresenterInfo *pi)\r
+{\r
+       char far *script = pi->script[0];\r
+\r
+       pi->numpages = 1;               // Assume at least 1 page\r
+       while (*script)\r
+       {\r
+               switch (*script++)\r
+               {\r
+                       case PI_CONTROL_CHAR:\r
+#ifndef PI_CASE_SENSITIVE\r
+                               *script=toupper(*script);\r
+                               *(script+1)=toupper(*(script+1));\r
+#endif\r
+                               switch (*((unsigned far *)script)++)\r
+                               {\r
+                                       case PI_CNVT_CODE('E','P'):\r
+                                               if (pi->numpages < PI_MAX_PAGES)\r
+                                                       pi->script[pi->numpages++] = script;\r
+                                               else\r
+                                                       TrashProg("GE ERROR: Too many Presenter() pages. --> %d",pi->numpages);\r
+                                       break;\r
+                               }\r
+                       break;\r
+\r
+                       case '\r':\r
+                               if (*script == '\n')\r
+                               {\r
+                                       *(script-1) = ' ';\r
+                                       script++;\r
+                               }\r
+                       break;\r
+               }\r
+       }\r
+\r
+       pi->numpages--; // Last page defined is not a real page.\r
+}\r
+#endif\r
+\r
+\r
+//-------------------------------------------------------------------------\r
+// AnimateWallList()\r
+//-------------------------------------------------------------------------\r
+void AnimateWallList(void)\r
+{\r
+       walltype *wall, *check;\r
+       unsigned i;\r
+       int tile,org_tile;\r
+\r
+       if (wall_anim_delay>0)\r
+       {\r
+               wall_anim_delay-=realtics;\r
+               return;\r
+       }\r
+\r
+       //\r
+       // Re-Init our counter...\r
+       //\r
+\r
+       wall_anim_delay = wall_anim_time;\r
+\r
+       //\r
+       // Clear all previous flags marking animation being DONE.\r
+       //\r
+\r
+       for (i=0;i<NUMFLOORS;i++)\r
+               TILE_FLAGS(i) &= ~tf_MARKED;\r
+\r
+\r
+       //\r
+       // Run though wall list updating only then needed animations\r
+       //\r
+\r
+       for (wall=&walls[1];wall<rightwall;wall++)\r
+       {\r
+               org_tile = tile = wall->color + wall_anim_pos[wall->color];\r
+\r
+               if (ANIM_FLAGS(tile))\r
+               {\r
+                       do\r
+                       {\r
+                               if (!(TILE_FLAGS(tile) & tf_MARKED))\r
+                               {\r
+                                       //\r
+                                       // update our offset table (0-NUMANIMS)\r
+                                       //\r
+\r
+                                       wall_anim_pos[tile] += (char signed)ANIM_FLAGS(tile+(char signed)wall_anim_pos[tile]);\r
+\r
+                                       //\r
+                                       // Mark tile as being already updated..\r
+                                       //\r
+\r
+                                       TILE_FLAGS(tile) |= tf_MARKED;\r
+                               }\r
+\r
+                               //\r
+                               // Check rest of tiles in this animation string...\r
+                               //\r
+\r
+                               tile += (char signed)ANIM_FLAGS(tile);\r
+\r
+                       } while (tile != org_tile);\r
+               }\r
+       }\r
+}\r
+\r
diff --git a/16/cawat/GELIB.H b/16/cawat/GELIB.H
new file mode 100644 (file)
index 0000000..eb25be6
--- /dev/null
@@ -0,0 +1,210 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#include "SL_FILE.h"\r
+\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// Defines\r
+//\r
+\r
+#define ANIM_USES_SHAPE_TABLE false\r
+#define PI_MAX_ANIMS 10\r
+#define PI_MAX_PAGES   40\r
+\r
+#define SAVEVER_DATA "0.01"\r
+#define FILENAME_LEN 15\r
+\r
+#define  GAMENAME              "CATACOMB ARMAGEDDON 3-D"\r
+#define        VERSION         "V1.02"\r
+#define  REVISION              "  rev 1 "\r
+\r
+//#define BOBLIST 1                          //SP - Undefine if not using BOBList\r
+\r
+#define AUDIO_DISK             (2)\r
+#define VIDEO_DISK             (1)\r
+#define LEVEL_DISK             (2)\r
+\r
+#define BIO_BUFFER_LEN (512)\r
+\r
+#define TrashProg Quit\r
+\r
+//   #define AMIGA\r
+\r
+\r
+typedef struct Sample {\r
+               char *filename;\r
+               memptr *data;\r
+} Sample;\r
+\r
+typedef enum {ged_none, ged_SoundSource,ged_SoundBlaster} AudioDeviceType;\r
+\r
+//typedef struct {\r
+//     memptr textptr;\r
+//     char far *pages[MAX_TEXT_PAGES];\r
+//     short totalpages;\r
+//} textinfo;\r
+\r
+typedef struct {\r
+       int handle;                     // handle of file\r
+       memptr buffer;          // pointer to buffer\r
+       word offset;            // offset into buffer\r
+       word status;            // read/write status\r
+} BufferedIO;\r
+\r
+typedef enum ANIMINFO {at_NONE,at_INIT,at_WAIT,at_ONCE,at_CYCLE,\r
+                                                         at_REBOUND,at_EXTRA,\r
+                                                         at_FWD,at_REV\r
+} ANIMINFO;\r
+\r
+struct BitMapHeader {\r
+       unsigned int    w,h,x,y;\r
+       unsigned char   d,trans,comp,pad;\r
+};\r
+\r
+struct BitMap {\r
+       unsigned int Width;\r
+       unsigned int Height;\r
+       unsigned int Depth;\r
+       unsigned int BytesPerRow;\r
+       char far *Planes[8];\r
+};\r
+\r
+struct Shape {\r
+       memptr Data;\r
+       long size;\r
+       unsigned int BPR;\r
+       struct BitMapHeader bmHdr;\r
+};\r
+\r
+#ifdef AMIGA\r
+typedef struct {\r
+       char *script[PI_MAX_PAGES];\r
+       XBitMap **shapes;\r
+       XBitMap **font;\r
+       short xl,yl,xh,yh;\r
+       struct BitMap *dst;\r
+       char numpages,bgcolor;\r
+} PresenterInfo;\r
+#else\r
+typedef struct {\r
+       char far *script[PI_MAX_PAGES];\r
+       memptr scriptstart;\r
+       short xl,yl,xh,yh;\r
+       char numpages,bgcolor,ltcolor,dkcolor;\r
+} PresenterInfo;\r
+#endif\r
+\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// Externs\r
+//\r
+\r
+extern char Filename[], ID[], VER[];\r
+extern boolean ge_textmode;\r
+extern short PPT_LeftEdge,PPT_RightEdge;\r
+//extern boolean ConserveMemory;\r
+extern BufferedIO lzwBIO;\r
+extern short wall_anim_delay,wall_anim_time;\r
+\r
+\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// Function prototypes\r
+//\r
+void WaitKeyVBL(short key, short vbls);\r
+void CalibrateJoystick(short joynum);\r
+void MoveScreen(short x, short y);\r
+void MoveGfxDst(short x, short y);\r
+void DoPiracy(void);\r
+void PrintPropText(char far *text);\r
+//void DisplayText(textinfo *textinfo);\r
+//long LoadTextFile(char *filename,textinfo *textinfo);\r
+//void FreeTextFile(textinfo *textinfo);\r
+//void InitTextFile(textinfo *textinfo);\r
+long Verify(char *filename);\r
+void GE_SaveGame(void);\r
+boolean GE_LoadGame(void);\r
+int GE_HardError(word errval,int ax,int bp,int si);\r
+\r
+#ifdef BOBLIST\r
+\r
+\r
+boolean UpdateBOBList(objtype *obj,struct Simple_Shape *Shape,shapeclass Class, short priority, spriteflags sprflags);\r
+boolean RemoveBOBShape(objtype *obj, shapeclass Class);\r
+void RemoveBOBList(objtype *obj);\r
+void InitBOBList(objtype *obj, struct BOB_Shape *BOB_Shape, short NumElements);\r
+void RefreshBOBList(objtype *obj);\r
+#endif\r
+\r
+\r
+unsigned long BLoad(char *SourceFile, memptr *DstPtr);\r
+void lzwDecompressFromRAM(byte far *SrcPtr, byte far *DstPtr, longword SrcLen);\r
+void lzwDecompressFromFile(BufferedIO *SrcPtr, byte far *DstPtr, longword SrcLen);\r
+byte readch(int handle);\r
+\r
+memptr InitBufferedIO(int handle, BufferedIO *bio);\r
+void FreeBufferedIO(BufferedIO *bio);\r
+byte bio_readch(BufferedIO *bio);\r
+void bio_fillbuffer(BufferedIO *bio);\r
+\r
+\r
+void SwapLong(long far *Var);\r
+void SwapWord(unsigned int far *Var);\r
+int LoadShape(char *Filename,struct Shape *SHP);\r
+void FreeShape(struct Shape *shape);\r
+int UnpackEGAShapeToScreen(struct Shape *SHP,int startx,int starty);\r
+char GetKeyChoice(char *choices,boolean clear);\r
+boolean AnimateObj(objtype *obj);\r
+void AdvanceAnimFWD(objtype *obj);\r
+void AdvanceAnimREV(objtype *obj);\r
+\r
+void LoadASArray(struct Sample *ASArray);\r
+void FreeASArray(struct Sample *ASArray);\r
+//void SelectDigiAudio(AudioDeviceType Device);\r
+void PlaySample(unsigned SampleNum);\r
+void GE_FreeAllDigiSounds(void);\r
+void GE_LoadAllDigiSounds(void);\r
+void DisplayGameList(short winx, short winy, short list_width, short list_height);\r
+void ReadGameList(void);\r
+void CheckStack(void);\r
+void CenterObj(objtype *obj, unsigned x, unsigned y);\r
+void cachein(short s,short e);\r
+void cacheout(short s,short e);\r
+void FizzleFade (unsigned source, unsigned dest,unsigned width,unsigned height, boolean abortable);\r
+void mprintf(char *msg, ...);\r
+boolean FindFile(char *filename,char *disktext,char disknum);\r
+void CacheAV(char *title);\r
+void BlackPalette(void);\r
+void ColoredPalette(void);\r
+void Presenter(PresenterInfo *pi);\r
+unsigned PI_VALUE(char far *ptr,char num_nybbles);\r
+long LoadPresenterScript(char *filename,PresenterInfo *pi);\r
+void FreePresenterScript(PresenterInfo *pi);\r
+void InitPresenterScript(PresenterInfo *pi);\r
+\r
+void AnimatePage(short numanims);\r
+short BoxAroundPic(short x1, short y1, unsigned picnum, PresenterInfo *pi);\r
+void PurgeAllGfx(void);\r
+void CachePage(char far *script);\r
+\r
+\r
+void AnimateWallList(void);\r
diff --git a/16/cawat/GFXE_ARM.EQU b/16/cawat/GFXE_ARM.EQU
new file mode 100644 (file)
index 0000000..f833b11
--- /dev/null
@@ -0,0 +1,571 @@
+;=====================================\r
+;\r
+; Graphics .EQU file for .ARM\r
+; IGRAB-ed on Thu Dec 02 13:59:00 1993\r
+;\r
+;=====================================\r
+\r
+FINALEPIC                      =       4\r
+STATUSPIC                      =       5\r
+FACE5PIC                       =       6\r
+FIRSTLATCHPIC                  =       7\r
+FACE1PIC                       =       8\r
+FACE2PIC                       =       9\r
+FACE3PIC                       =       10\r
+FACE4PIC                       =       11\r
+RADAR_TOPPIC                   =       12\r
+RADAR_BOTTOMPIC                        =       13\r
+RADAR_RGEMPIC                  =       14\r
+RADAR_GGEMPIC                  =       15\r
+RADAR_BGEMPIC                  =       16\r
+RADAR_YGEMPIC                  =       17\r
+RADAR_PGEMPIC                  =       18\r
+FIRSTGROUNDPIC                 =       19\r
+FIRSTSTRIPPIC                  =       20\r
+FIRSTSCALEPIC                  =       21\r
+SKELETON_1PIC                  =       22\r
+SKELETON_2PIC                  =       23\r
+SKELETON_3PIC                  =       24\r
+SKELETON_4PIC                  =       25\r
+SKELETON_ATTACK_1PIC           =       26\r
+SKELETON_ATTACK_2PIC           =       27\r
+SKELETON_ATTACK_3PIC           =       28\r
+SKELETON_OUCHPIC               =       29\r
+SKELETON_DEATH_1PIC            =       30\r
+SKELETON_DEATH_2PIC            =       31\r
+TOMB1PIC                       =       32\r
+TOMB2PIC                       =       33\r
+TOMB3PIC                       =       34\r
+OBJ_WARP1PIC                   =       35\r
+OBJ_WARP2PIC                   =       36\r
+OBJ_WARP3PIC                   =       37\r
+OBJ_WARP4PIC                   =       38\r
+EYE_WALK1PIC                   =       39\r
+EYE_WALK2PIC                   =       40\r
+EYE_WALK3PIC                   =       41\r
+EYE_OUCH1PIC                   =       42\r
+EYE_OUCH2PIC                   =       43\r
+EYE_DEATH1PIC                  =       44\r
+EYE_DEATH2PIC                  =       45\r
+EYE_DEATH3PIC                  =       46\r
+EYE_SCOWLPIC                   =       47\r
+EYE_SHOT1PIC                   =       48\r
+EYE_SHOT2PIC                   =       49\r
+ZOMB_APPEAR1PIC                        =       50\r
+ZOMB_APPEAR2PIC                        =       51\r
+ZOMB_APPEAR3PIC                        =       52\r
+ZOMB_APPEAR4PIC                        =       53\r
+ZOMB_WALK1PIC                  =       54\r
+ZOMB_WALK2PIC                  =       55\r
+ZOMB_WALK3PIC                  =       56\r
+ZOMB_OUCHPIC                   =       57\r
+ZOMB_ATTACKPIC                 =       58\r
+ZOMB_DIE1PIC                   =       59\r
+ZOMB_DIE2PIC                   =       60\r
+ZOMB_DIE3PIC                   =       61\r
+BOLTOBJPIC                     =       62\r
+BOLT2OBJPIC                    =       63\r
+BOLT3OBJPIC                    =       64\r
+NUKEOBJPIC                     =       65\r
+NUKE2OBJPIC                    =       66\r
+NUKE3OBJPIC                    =       67\r
+TIMEOBJ1PIC                    =       68\r
+TIMEOBJ2PIC                    =       69\r
+O_WATER_CHEST1PIC              =       70\r
+O_WATER_CHEST2PIC              =       71\r
+POTIONOBJPIC                   =       72\r
+RKEYOBJPIC                     =       73\r
+YKEYOBJPIC                     =       74\r
+GKEYOBJPIC                     =       75\r
+BKEYOBJPIC                     =       76\r
+RGEM1PIC                       =       77\r
+RGEM2PIC                       =       78\r
+GGEM1PIC                       =       79\r
+GGEM2PIC                       =       80\r
+BGEM1PIC                       =       81\r
+BGEM2PIC                       =       82\r
+YGEM1PIC                       =       83\r
+YGEM2PIC                       =       84\r
+PGEM1PIC                       =       85\r
+PGEM2PIC                       =       86\r
+CHESTOBJPIC                    =       87\r
+PSHOT1PIC                      =       88\r
+PSHOT2PIC                      =       89\r
+PSHOT_EXP1PIC                  =       90\r
+PSHOT_EXP2PIC                  =       91\r
+PSHOT_EXP3PIC                  =       92\r
+RED_DEMON1PIC                  =       93\r
+RED_DEMON2PIC                  =       94\r
+RED_DEMON3PIC                  =       95\r
+RED_DEMON4PIC                  =       96\r
+RED_DEMONATTACK1PIC            =       97\r
+RED_DEMONATTACK2PIC            =       98\r
+RED_DEMONATTACK3PIC            =       99\r
+RED_DEMONOUCHPIC               =       100\r
+RED_DEMONDIE1PIC               =       101\r
+RED_DEMONDIE2PIC               =       102\r
+RED_DEMONDIE3PIC               =       103\r
+MAGE1PIC                       =       104\r
+MAGE2PIC                       =       105\r
+MAGEOUCHPIC                    =       106\r
+MAGEATTACKPIC                  =       107\r
+MAGEDIE1PIC                    =       108\r
+MAGEDIE2PIC                    =       109\r
+BAT1PIC                                =       110\r
+BAT2PIC                                =       111\r
+BAT3PIC                                =       112\r
+BAT4PIC                                =       113\r
+BATDIE1PIC                     =       114\r
+BATDIE2PIC                     =       115\r
+GREL1PIC                       =       116\r
+GREL2PIC                       =       117\r
+GRELATTACKPIC                  =       118\r
+GRELHITPIC                     =       119\r
+GRELDIE1PIC                    =       120\r
+GRELDIE2PIC                    =       121\r
+GRELDIE3PIC                    =       122\r
+GRELDIE4PIC                    =       123\r
+GRELDIE5PIC                    =       124\r
+GRELDIE6PIC                    =       125\r
+SKULL_SHOTPIC                  =       126\r
+GODESS_WALK1PIC                        =       127\r
+GODESS_WALK2PIC                        =       128\r
+GODESS_WALK3PIC                        =       129\r
+GODESS_ATTACK1PIC              =       130\r
+GODESS_ATTACK2PIC              =       131\r
+GODESS_ATTACK3PIC              =       132\r
+GODESS_STATUEPIC               =       133\r
+GODESS_OUCHPIC                 =       134\r
+GODESS_DEATH1PIC               =       135\r
+GODESS_DEATH2PIC               =       136\r
+ANT_EGG1PIC                    =       137\r
+ANT_EGG2PIC                    =       138\r
+ANT_WALK1PIC                   =       139\r
+ANT_WALK2PIC                   =       140\r
+ANT_WALK3PIC                   =       141\r
+ANT_ATTACKPIC                  =       142\r
+ANT_DEATH1PIC                  =       143\r
+ANT_DEATH2PIC                  =       144\r
+ANT_DEATH3PIC                  =       145\r
+FATDEMON_WALK1PIC              =       146\r
+FATDEMON_WALK2PIC              =       147\r
+FATDEMON_WALK3PIC              =       148\r
+FATDEMON_WALK4PIC              =       149\r
+FATDEMON_ATTACK1PIC            =       150\r
+FATDEMON_ATTACK2PIC            =       151\r
+FATDEMON_OUCHPIC               =       152\r
+FATDEMON_BLOWUP1PIC            =       153\r
+FATDEMON_BLOWUP2PIC            =       154\r
+FATDEMON_BLOWUP3PIC            =       155\r
+FATDEMON_EXPLODEPIC            =       156\r
+FATDEMON_FEETPIC               =       157\r
+SUCCUBUS_WALK1PIC              =       158\r
+SUCCUBUS_WALK2PIC              =       159\r
+SUCCUBUS_WALK3PIC              =       160\r
+SUCCUBUS_WALK4PIC              =       161\r
+SUCCUBUS_ATTACK1PIC            =       162\r
+SUCCUBUS_ATTACK2PIC            =       163\r
+SUCCUBUS_OUCHPIC               =       164\r
+SUCCUBUS_DEATH1PIC             =       165\r
+SUCCUBUS_DEATH2PIC             =       166\r
+SUCCUBUS_SHOT1PIC              =       167\r
+TREE_IDLEPIC                   =       168\r
+TREE_AWAKENINGPIC              =       169\r
+TREE_WALK1PIC                  =       170\r
+TREE_WALK2PIC                  =       171\r
+TREE_ATTACK1PIC                        =       172\r
+TREE_ATTACK2PIC                        =       173\r
+TREE_ATTACK3PIC                        =       174\r
+TREE_DEATH1PIC                 =       175\r
+TREE_DEATH2PIC                 =       176\r
+TREE_DEATH3PIC                 =       177\r
+DRAGON_BUBBLES1PIC             =       178\r
+DRAGON_BUBBLES2PIC             =       179\r
+DRAGON_EYESPIC                 =       180\r
+DRAGON_RISE1PIC                        =       181\r
+DRAGON_RISE2PIC                        =       182\r
+DRAGON_WALK1PIC                        =       183\r
+DRAGON_WALK2PIC                        =       184\r
+DRAGON_WALK3PIC                        =       185\r
+DRAGON_WALK4PIC                        =       186\r
+DRAGON_ATTACK1PIC              =       187\r
+DRAGON_ATTACK2PIC              =       188\r
+DRAGON_ATTACK3PIC              =       189\r
+DRAGON_OUCHPIC                 =       190\r
+DRAGON_DEATH1PIC               =       191\r
+DRAGON_DEATH2PIC               =       192\r
+DRAGON_DEATH3PIC               =       193\r
+BUNNY_LEFT1PIC                 =       194\r
+BUNNY_LEFT2PIC                 =       195\r
+BUNNY_RIGHT1PIC                        =       196\r
+BUNNY_RIGHT2PIC                        =       197\r
+BUNNY_META1PIC                 =       198\r
+BUNNY_META2PIC                 =       199\r
+BUNNY_WALK1PIC                 =       200\r
+BUNNY_WALK2PIC                 =       201\r
+BUNNY_OUCHPIC                  =       202\r
+BUNNY_DEATH1PIC                        =       203\r
+BUNNY_DEATH2PIC                        =       204\r
+ARCH1PIC                       =       205\r
+ARCH2PIC                       =       206\r
+ARCH3PIC                       =       207\r
+ARCH4PIC                       =       208\r
+ARCH5PIC                       =       209\r
+ARCH6PIC                       =       210\r
+ARCH7PIC                       =       211\r
+ARCH8PIC                       =       212\r
+ARCH9PIC                       =       213\r
+ARCH10PIC                      =       214\r
+ARCH11PIC                      =       215\r
+ARCH12PIC                      =       216\r
+ARCH13PIC                      =       217\r
+ANT_HILLPIC                    =       218\r
+COLUMNPIC                      =       219\r
+SULPHUR_GAS_1PIC               =       220\r
+SULPHUR_GAS_2PIC               =       221\r
+SULPHUR_GAS_3PIC               =       222\r
+FIRE_POT_1PIC                  =       223\r
+FIRE_POT_2PIC                  =       224\r
+SKEL_HANGPIC                   =       225\r
+FORCE_FIELD_1PIC               =       226\r
+FORCE_FIELD_2PIC               =       227\r
+FORCE_FIELD_3PIC               =       228\r
+FORCE_FIELD_4PIC               =       229\r
+WFOUNTAINPIC                   =       230\r
+FIRSTWALLPIC                   =       231\r
+CRYSTAL_LIGHT_1PIC             =       232\r
+CRYSTAL_LIGHT_2PIC             =       233\r
+CRYSTAL_LIGHT_3PIC             =       234\r
+CRYSTAL_LIGHT_4PIC             =       235\r
+CRYSTAL_DARK_1PIC              =       236\r
+CRYSTAL_DARK_2PIC              =       237\r
+CRYSTAL_DARK_3PIC              =       238\r
+CRYSTAL_DARK_4PIC              =       239\r
+FIRE_WALL_1PIC                 =       240\r
+FIRE_WALL_2PIC                 =       241\r
+FIRE_WALL_3PIC                 =       242\r
+FIRE_WALL_4PIC                 =       243\r
+BRN_STONE_GATEPIC              =       244\r
+BRN_STONE_WALL_1PIC            =       245\r
+BRN_STONE_WALL_2PIC            =       246\r
+KUDZU_LIGHT_WALLPIC            =       247\r
+KUDZU_DARK_WALLPIC             =       248\r
+HEDGE_WALLPIC                  =       249\r
+HEDGE_EYESPIC                  =       250\r
+BRN_WINDOW_LIGHTPIC            =       251\r
+ALTAR_LEFTPIC                  =       252\r
+ALTAR_RIGHTPIC                 =       253\r
+GRAY_LIGHT_WALLPIC             =       254\r
+GRAY_DARK_WALLPIC              =       255\r
+GRAY_LIGHT_SIGNPIC             =       256\r
+GRAY_DARK_SIGNPIC              =       257\r
+MANICLE_LIGHT_BLOODYPIC                =       258\r
+MANICLE_DARK_BLOODYPIC         =       259\r
+LIGHT_CURTAIN_WINDOWPIC                =       260\r
+LIGHT_CURTAIN_WALLPIC          =       261\r
+DARK_CURTAIN_WINDOWPIC         =       262\r
+DARK_CURTAIN_WALLPIC           =       263\r
+BRN_LIGHT_SIGNPIC              =       264\r
+BRN_DARK_SIGNPIC               =       265\r
+LIGHT_STONE_WALLPIC            =       266\r
+DARK_STONE_WALLPIC             =       267\r
+BRN_FLAGSTONE_LIGHT_2PIC       =       268\r
+BRN_FLAGSTONE_DARK_2PIC                =       269\r
+RUST_METAL_LIGHTPIC            =       270\r
+RUST_METAL_DARKPIC             =       271\r
+GRAY_METAL_LIGHTPIC            =       272\r
+GRAY_METAL_DARKPIC             =       273\r
+WEAK_STONE_LIGHTPIC            =       274\r
+WEAK_STONE_DARKPIC             =       275\r
+WEAK_GRAY_RFGSTN_LIGHTPIC      =       276\r
+WEAK_GRAY_RFGSTN_DARKPIC       =       277\r
+WEAK_CRYSTAL_LIGHTPIC          =       278\r
+WEAK_CRYSTAL_DARKPIC           =       279\r
+RED_MUD_LIGHTPIC               =       280\r
+BRN_MUD_DARKPIC                        =       281\r
+RED_MUD_WEAK_LIGHTPIC          =       282\r
+BRN_MUD_WEAK_DARKPIC           =       283\r
+HORN_DOORPIC                   =       284\r
+CLOSED_DOOR_1PIC               =       285\r
+DOOR_2PIC                      =       286\r
+WATER_LIGHT_WEAK_1PIC          =       287\r
+WATER_LIGHT_WEAK_2PIC          =       288\r
+WATER_LIGHT_WEAK_3PIC          =       289\r
+WATER_DARK_WEAK_1PIC           =       290\r
+WATER_DARK_WEAK_2PIC           =       291\r
+WATER_DARK_WEAK_3PIC           =       292\r
+WATER_LIGHT_1PIC               =       293\r
+WATER_LIGHT_2PIC               =       294\r
+WATER_LIGHT_3PIC               =       295\r
+WATER_DARK_1PIC                        =       296\r
+WATER_DARK_2PIC                        =       297\r
+WATER_DARK_3PIC                        =       298\r
+TROLL_LIGHT_STONEPIC           =       299\r
+TROLL_DARK_STONEPIC            =       300\r
+TROLL_BLOODY_LT_STONEPIC       =       301\r
+TROLL_BLOODY_DK_STONEPIC       =       302\r
+LIGHT_BREATH_1PIC              =       303\r
+LIGHT_BREATH_2PIC              =       304\r
+LIGHT_BREATH_3PIC              =       305\r
+DARK_BREATH_1PIC               =       306\r
+DARK_BREATH_2PIC               =       307\r
+DARK_BREATH_3PIC               =       308\r
+EXP_WALL_1PIC                  =       309\r
+EXP_WALL_2PIC                  =       310\r
+EXP_WALL_3PIC                  =       311\r
+WATER_EXP_WALL_1PIC            =       312\r
+WATER_EXP_WALL_2PIC            =       313\r
+WATER_EXP_WALL_3PIC            =       314\r
+W_GEN_DOOR1PIC                 =       315\r
+W_GEN_DOOR2PIC                 =       316\r
+W_CRYSTAL_DOORPIC              =       317\r
+DMG_BRN_FSTN_LTPIC             =       318\r
+DMG_BRN_FSTN_DKPIC             =       319\r
+DMG_FIN_FSTN_LTPIC             =       320\r
+DMG_FIN_FSTN_DKPIC             =       321\r
+STEEL_DOOR1PIC                 =       322\r
+STEEL_DOOR2PIC                 =       323\r
+BRN_WINDOW_DARKPIC             =       324\r
+GRY_DOOR_LTPIC                 =       325\r
+GRY_DOOR_DKPIC                 =       326\r
+BRN_DOOR_LTPIC                 =       327\r
+BRN_DOOR_DKPIC                 =       328\r
+GRY_FGSTN_LTPIC                        =       329\r
+GRY_FGSTN_DKPIC                        =       330\r
+KUDZU_WEAK_LIGHTPIC            =       331\r
+KUDZU_WEAK_DARKPIC             =       332\r
+LT_SKEL1PIC                    =       333\r
+DK_SKEL1PIC                    =       334\r
+LT_SKEL2PIC                    =       335\r
+DK_SKEL2PIC                    =       336\r
+MANICLE_LIGHT_WALLPIC          =       337\r
+MANICLE_DARK_WALLPIC           =       338\r
+TAP_1PIC                       =       339\r
+TAP_2PIC                       =       340\r
+TAP_3PIC                       =       341\r
+TAP_4PIC                       =       342\r
+TAP_5PIC                       =       343\r
+FINALWALLPIC                   =       344\r
+WATER_DOOR1_PIC                        =       345\r
+WATER_DOOR2_PIC                        =       346\r
+LASTWALLPIC                    =       347\r
+\r
+HAND1PICM                      =       348\r
+\r
+NORTHICONSPR                   =       349\r
+\r
+LEVEL1TEXT                     =       640\r
+LEVEL2TEXT                     =       641\r
+LEVEL3TEXT                     =       642\r
+LEVEL4TEXT                     =       643\r
+LEVEL5TEXT                     =       644\r
+LEVEL6TEXT                     =       645\r
+LEVEL7TEXT                     =       646\r
+LEVEL8TEXT                     =       647\r
+LEVEL9TEXT                     =       648\r
+LEVEL10TEXT                    =       649\r
+LEVEL11TEXT                    =       650\r
+LEVEL12TEXT                    =       651\r
+LEVEL13TEXT                    =       652\r
+LEVEL14TEXT                    =       653\r
+LEVEL15TEXT                    =       654\r
+LEVEL16TEXT                    =       655\r
+LEVEL17TEXT                    =       656\r
+PIRACY                         =       657\r
+\r
+SKELDUDE_LUMP_START            =       22\r
+SKELDUDE_LUMP_END              =       31\r
+\r
+TOMBSTONES_LUMP_START          =       32\r
+TOMBSTONES_LUMP_END            =       34\r
+\r
+OBJ_WARP_LUMP_START            =       35\r
+OBJ_WARP_LUMP_END              =       38\r
+\r
+EYE_LUMP_START                 =       39\r
+EYE_LUMP_END                   =       49\r
+\r
+ZOMBIE_LUMP_START              =       50\r
+ZOMBIE_LUMP_END                        =       61\r
+\r
+BOLT_LUMP_START                        =       62\r
+BOLT_LUMP_END                  =       64\r
+\r
+NUKE_LUMP_START                        =       65\r
+NUKE_LUMP_END                  =       67\r
+\r
+TIME_LUMP_START                        =       68\r
+TIME_LUMP_END                  =       69\r
+\r
+O_WATER_CHEST_LUMP_START       =       70\r
+O_WATER_CHEST_LUMP_END         =       71\r
+\r
+POTION_LUMP_START              =       72\r
+POTION_LUMP_END                        =       72\r
+\r
+RKEY_LUMP_START                        =       73\r
+RKEY_LUMP_END                  =       73\r
+\r
+YKEY_LUMP_START                        =       74\r
+YKEY_LUMP_END                  =       74\r
+\r
+GKEY_LUMP_START                        =       75\r
+GKEY_LUMP_END                  =       75\r
+\r
+BKEY_LUMP_START                        =       76\r
+BKEY_LUMP_END                  =       76\r
+\r
+RGEM_LUMP_START                        =       77\r
+RGEM_LUMP_END                  =       78\r
+\r
+GGEM_LUMP_START                        =       79\r
+GGEM_LUMP_END                  =       80\r
+\r
+BGEM_LUMP_START                        =       81\r
+BGEM_LUMP_END                  =       82\r
+\r
+YGEM_LUMP_START                        =       83\r
+YGEM_LUMP_END                  =       84\r
+\r
+PGEM_LUMP_START                        =       85\r
+PGEM_LUMP_END                  =       86\r
+\r
+CHEST_LUMP_START               =       87\r
+CHEST_LUMP_END                 =       87\r
+\r
+PLAYER_LUMP_START              =       88\r
+PLAYER_LUMP_END                        =       92\r
+\r
+REDDEMON_LUMP_START            =       93\r
+REDDEMON_LUMP_END              =       103\r
+\r
+MAGE_LUMP_START                        =       104\r
+MAGE_LUMP_END                  =       109\r
+\r
+BAT_LUMP_START                 =       110\r
+BAT_LUMP_END                   =       115\r
+\r
+GREL_LUMP_START                        =       116\r
+GREL_LUMP_END                  =       126\r
+\r
+GODESS_LUMP_START              =       127\r
+GODESS_LUMP_END                        =       136\r
+\r
+ANT_LUMP_START                 =       137\r
+ANT_LUMP_END                   =       145\r
+\r
+FATDEMON_LUMP_START            =       146\r
+FATDEMON_LUMP_END              =       157\r
+\r
+SUCCUBUS_LUMP_START            =       158\r
+SUCCUBUS_LUMP_END              =       167\r
+\r
+TREE_LUMP_START                        =       168\r
+TREE_LUMP_END                  =       177\r
+\r
+DRAGON_LUMP_START              =       178\r
+DRAGON_LUMP_END                        =       193\r
+\r
+BUNNY_LUMP_START               =       194\r
+BUNNY_LUMP_END                 =       204\r
+\r
+ARCH1_LUMP_START               =       205\r
+ARCH1_LUMP_END                 =       205\r
+\r
+ARCH2_LUMP_START               =       206\r
+ARCH2_LUMP_END                 =       206\r
+\r
+ARCH3_LUMP_START               =       207\r
+ARCH3_LUMP_END                 =       207\r
+\r
+ARCH4_LUMP_START               =       208\r
+ARCH4_LUMP_END                 =       208\r
+\r
+ARCH5_LUMP_START               =       209\r
+ARCH5_LUMP_END                 =       209\r
+\r
+ARCH6_LUMP_START               =       210\r
+ARCH6_LUMP_END                 =       210\r
+\r
+ARCH7_LUMP_START               =       211\r
+ARCH7_LUMP_END                 =       211\r
+\r
+ARCH8_LUMP_START               =       212\r
+ARCH8_LUMP_END                 =       212\r
+\r
+ARCH9_LUMP_START               =       213\r
+ARCH9_LUMP_END                 =       213\r
+\r
+ARCH10_LUMP_START              =       214\r
+ARCH10_LUMP_END                        =       214\r
+\r
+ARCH11_LUMP_START              =       215\r
+ARCH11_LUMP_END                        =       215\r
+\r
+ARCH12_LUMP_START              =       216\r
+ARCH12_LUMP_END                        =       216\r
+\r
+ARCH13_LUMP_START              =       217\r
+ARCH13_LUMP_END                        =       217\r
+\r
+ANTHILL_LUMP_START             =       218\r
+ANTHILL_LUMP_END               =       218\r
+\r
+COLUMN_LUMP_START              =       219\r
+COLUMN_LUMP_END                        =       219\r
+\r
+SULPHURGAS_LUMP_START          =       220\r
+SULPHURGAS_LUMP_END            =       222\r
+\r
+FIREPOT_LUMP_START             =       223\r
+FIREPOT_LUMP_END               =       224\r
+\r
+SKELHANG_LUMP_START            =       225\r
+SKELHANG_LUMP_END              =       225\r
+\r
+FORCEFIELD_LUMP_START          =       226\r
+FORCEFIELD_LUMP_END            =       229\r
+\r
+FOUNTAIN_LUMP_START            =       230\r
+FOUNTAIN_LUMP_END              =       230\r
+\r
+\r
+;\r
+; Amount of each data item\r
+;\r
+NUMCHUNKS      =       658\r
+NUMFONT        =       1\r
+NUMFONTM       =       0\r
+NUMPICS        =       344\r
+NUMPICM        =       1\r
+NUMSPRITES     =       1\r
+NUMTILE8       =       108\r
+NUMTILE8M      =       36\r
+NUMTILE16      =       216\r
+NUMTILE16M     =       72\r
+NUMTILE32      =       0\r
+NUMTILE32M     =       0\r
+NUMEXTERN      =       18\r
+;\r
+; File offsets for data items\r
+;\r
+STRUCTPIC      =       0\r
+STRUCTPICM     =       1\r
+STRUCTSPRITE   =       2\r
+\r
+STARTFONT      =       3\r
+STARTFONTM     =       4\r
+STARTPICS      =       4\r
+STARTPICM      =       348\r
+STARTSPRITES   =       349\r
+STARTTILE8     =       350\r
+STARTTILE8M    =       351\r
+STARTTILE16    =       352\r
+STARTTILE16M   =       568\r
+STARTTILE32    =       640\r
+STARTTILE32M   =       640\r
+STARTEXTERN    =       640\r
+\r
+;\r
+; Thank you for using IGRAB!\r
+;\r
diff --git a/16/cawat/GFXE_ARM.H b/16/cawat/GFXE_ARM.H
new file mode 100644 (file)
index 0000000..cbeb40a
--- /dev/null
@@ -0,0 +1,647 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//////////////////////////////////////\r
+//\r
+// Graphics .H file for .ARM\r
+// IGRAB-ed on Thu Dec 02 13:58:59 1993\r
+//\r
+//////////////////////////////////////\r
+\r
+typedef enum {\r
+               FINALEPIC=4,\r
+               STATUSPIC,                   // 5\r
+               FACE5PIC,                    // 6\r
+               FIRSTLATCHPIC,               // 7\r
+               FACE1PIC,                    // 8\r
+               FACE2PIC,                    // 9\r
+               FACE3PIC,                    // 10\r
+               FACE4PIC,                    // 11\r
+               RADAR_TOPPIC,                // 12\r
+               RADAR_BOTTOMPIC,             // 13\r
+               RADAR_RGEMPIC,               // 14\r
+               RADAR_GGEMPIC,               // 15\r
+               RADAR_BGEMPIC,               // 16\r
+               RADAR_YGEMPIC,               // 17\r
+               RADAR_PGEMPIC,               // 18\r
+               FIRSTGROUNDPIC,              // 19\r
+               FIRSTSTRIPPIC,               // 20\r
+               FIRSTSCALEPIC,               // 21\r
+               // Lump Start\r
+               SKELETON_1PIC,               // 22\r
+               SKELETON_2PIC,               // 23\r
+               SKELETON_3PIC,               // 24\r
+               SKELETON_4PIC,               // 25\r
+               SKELETON_ATTACK_1PIC,        // 26\r
+               SKELETON_ATTACK_2PIC,        // 27\r
+               SKELETON_ATTACK_3PIC,        // 28\r
+               SKELETON_OUCHPIC,            // 29\r
+               SKELETON_DEATH_1PIC,         // 30\r
+               SKELETON_DEATH_2PIC,         // 31\r
+               // Lump Start\r
+               TOMB1PIC,                    // 32\r
+               TOMB2PIC,                    // 33\r
+               TOMB3PIC,                    // 34\r
+               // Lump Start\r
+               OBJ_WARP1PIC,                // 35\r
+               OBJ_WARP2PIC,                // 36\r
+               OBJ_WARP3PIC,                // 37\r
+               OBJ_WARP4PIC,                // 38\r
+               // Lump Start\r
+               EYE_WALK1PIC,                // 39\r
+               EYE_WALK2PIC,                // 40\r
+               EYE_WALK3PIC,                // 41\r
+               EYE_OUCH1PIC,                // 42\r
+               EYE_OUCH2PIC,                // 43\r
+               EYE_DEATH1PIC,               // 44\r
+               EYE_DEATH2PIC,               // 45\r
+               EYE_DEATH3PIC,               // 46\r
+               EYE_SCOWLPIC,                // 47\r
+               EYE_SHOT1PIC,                // 48\r
+               EYE_SHOT2PIC,                // 49\r
+               // Lump Start\r
+               ZOMB_APPEAR1PIC,             // 50\r
+               ZOMB_APPEAR2PIC,             // 51\r
+               ZOMB_APPEAR3PIC,             // 52\r
+               ZOMB_APPEAR4PIC,             // 53\r
+               ZOMB_WALK1PIC,               // 54\r
+               ZOMB_WALK2PIC,               // 55\r
+               ZOMB_WALK3PIC,               // 56\r
+               ZOMB_OUCHPIC,                // 57\r
+               ZOMB_ATTACKPIC,              // 58\r
+               ZOMB_DIE1PIC,                // 59\r
+               ZOMB_DIE2PIC,                // 60\r
+               ZOMB_DIE3PIC,                // 61\r
+               // Lump Start\r
+               BOLTOBJPIC,                  // 62\r
+               BOLT2OBJPIC,                 // 63\r
+               BOLT3OBJPIC,                 // 64\r
+               // Lump Start\r
+               NUKEOBJPIC,                  // 65\r
+               NUKE2OBJPIC,                 // 66\r
+               NUKE3OBJPIC,                 // 67\r
+               // Lump Start\r
+               TIMEOBJ1PIC,                 // 68\r
+               TIMEOBJ2PIC,                 // 69\r
+               // Lump Start\r
+               O_WATER_CHEST1PIC,           // 70\r
+               O_WATER_CHEST2PIC,           // 71\r
+               // Lump Start\r
+               POTIONOBJPIC,                // 72\r
+               // Lump Start\r
+               RKEYOBJPIC,                  // 73\r
+               // Lump Start\r
+               YKEYOBJPIC,                  // 74\r
+               // Lump Start\r
+               GKEYOBJPIC,                  // 75\r
+               // Lump Start\r
+               BKEYOBJPIC,                  // 76\r
+               // Lump Start\r
+               RGEM1PIC,                    // 77\r
+               RGEM2PIC,                    // 78\r
+               // Lump Start\r
+               GGEM1PIC,                    // 79\r
+               GGEM2PIC,                    // 80\r
+               // Lump Start\r
+               BGEM1PIC,                    // 81\r
+               BGEM2PIC,                    // 82\r
+               // Lump Start\r
+               YGEM1PIC,                    // 83\r
+               YGEM2PIC,                    // 84\r
+               // Lump Start\r
+               PGEM1PIC,                    // 85\r
+               PGEM2PIC,                    // 86\r
+               // Lump Start\r
+               CHESTOBJPIC,                 // 87\r
+               // Lump Start\r
+               PSHOT1PIC,                   // 88\r
+               PSHOT2PIC,                   // 89\r
+               PSHOT_EXP1PIC,               // 90\r
+               PSHOT_EXP2PIC,               // 91\r
+               PSHOT_EXP3PIC,               // 92\r
+               // Lump Start\r
+               RED_DEMON1PIC,               // 93\r
+               RED_DEMON2PIC,               // 94\r
+               RED_DEMON3PIC,               // 95\r
+               RED_DEMON4PIC,               // 96\r
+               RED_DEMONATTACK1PIC,         // 97\r
+               RED_DEMONATTACK2PIC,         // 98\r
+               RED_DEMONATTACK3PIC,         // 99\r
+               RED_DEMONOUCHPIC,            // 100\r
+               RED_DEMONDIE1PIC,            // 101\r
+               RED_DEMONDIE2PIC,            // 102\r
+               RED_DEMONDIE3PIC,            // 103\r
+               // Lump Start\r
+               MAGE1PIC,                    // 104\r
+               MAGE2PIC,                    // 105\r
+               MAGEOUCHPIC,                 // 106\r
+               MAGEATTACKPIC,               // 107\r
+               MAGEDIE1PIC,                 // 108\r
+               MAGEDIE2PIC,                 // 109\r
+               // Lump Start\r
+               BAT1PIC,                     // 110\r
+               BAT2PIC,                     // 111\r
+               BAT3PIC,                     // 112\r
+               BAT4PIC,                     // 113\r
+               BATDIE1PIC,                  // 114\r
+               BATDIE2PIC,                  // 115\r
+               // Lump Start\r
+               GREL1PIC,                    // 116\r
+               GREL2PIC,                    // 117\r
+               GRELATTACKPIC,               // 118\r
+               GRELHITPIC,                  // 119\r
+               GRELDIE1PIC,                 // 120\r
+               GRELDIE2PIC,                 // 121\r
+               GRELDIE3PIC,                 // 122\r
+               GRELDIE4PIC,                 // 123\r
+               GRELDIE5PIC,                 // 124\r
+               GRELDIE6PIC,                 // 125\r
+               SKULL_SHOTPIC,               // 126\r
+               // Lump Start\r
+               GODESS_WALK1PIC,             // 127\r
+               GODESS_WALK2PIC,             // 128\r
+               GODESS_WALK3PIC,             // 129\r
+               GODESS_ATTACK1PIC,           // 130\r
+               GODESS_ATTACK2PIC,           // 131\r
+               GODESS_ATTACK3PIC,           // 132\r
+               GODESS_STATUEPIC,            // 133\r
+               GODESS_OUCHPIC,              // 134\r
+               GODESS_DEATH1PIC,            // 135\r
+               GODESS_DEATH2PIC,            // 136\r
+               // Lump Start\r
+               ANT_EGG1PIC,                 // 137\r
+               ANT_EGG2PIC,                 // 138\r
+               ANT_WALK1PIC,                // 139\r
+               ANT_WALK2PIC,                // 140\r
+               ANT_WALK3PIC,                // 141\r
+               ANT_ATTACKPIC,               // 142\r
+               ANT_DEATH1PIC,               // 143\r
+               ANT_DEATH2PIC,               // 144\r
+               ANT_DEATH3PIC,               // 145\r
+               // Lump Start\r
+               FATDEMON_WALK1PIC,           // 146\r
+               FATDEMON_WALK2PIC,           // 147\r
+               FATDEMON_WALK3PIC,           // 148\r
+               FATDEMON_WALK4PIC,           // 149\r
+               FATDEMON_ATTACK1PIC,         // 150\r
+               FATDEMON_ATTACK2PIC,         // 151\r
+               FATDEMON_OUCHPIC,            // 152\r
+               FATDEMON_BLOWUP1PIC,         // 153\r
+               FATDEMON_BLOWUP2PIC,         // 154\r
+               FATDEMON_BLOWUP3PIC,         // 155\r
+               FATDEMON_EXPLODEPIC,         // 156\r
+               FATDEMON_FEETPIC,            // 157\r
+               // Lump Start\r
+               SUCCUBUS_WALK1PIC,           // 158\r
+               SUCCUBUS_WALK2PIC,           // 159\r
+               SUCCUBUS_WALK3PIC,           // 160\r
+               SUCCUBUS_WALK4PIC,           // 161\r
+               SUCCUBUS_ATTACK1PIC,         // 162\r
+               SUCCUBUS_ATTACK2PIC,         // 163\r
+               SUCCUBUS_OUCHPIC,            // 164\r
+               SUCCUBUS_DEATH1PIC,          // 165\r
+               SUCCUBUS_DEATH2PIC,          // 166\r
+               SUCCUBUS_SHOT1PIC,           // 167\r
+               // Lump Start\r
+               TREE_IDLEPIC,                // 168\r
+               TREE_AWAKENINGPIC,           // 169\r
+               TREE_WALK1PIC,               // 170\r
+               TREE_WALK2PIC,               // 171\r
+               TREE_ATTACK1PIC,             // 172\r
+               TREE_ATTACK2PIC,             // 173\r
+               TREE_ATTACK3PIC,             // 174\r
+               TREE_DEATH1PIC,              // 175\r
+               TREE_DEATH2PIC,              // 176\r
+               TREE_DEATH3PIC,              // 177\r
+               // Lump Start\r
+               DRAGON_BUBBLES1PIC,          // 178\r
+               DRAGON_BUBBLES2PIC,          // 179\r
+               DRAGON_EYESPIC,              // 180\r
+               DRAGON_RISE1PIC,             // 181\r
+               DRAGON_RISE2PIC,             // 182\r
+               DRAGON_WALK1PIC,             // 183\r
+               DRAGON_WALK2PIC,             // 184\r
+               DRAGON_WALK3PIC,             // 185\r
+               DRAGON_WALK4PIC,             // 186\r
+               DRAGON_ATTACK1PIC,           // 187\r
+               DRAGON_ATTACK2PIC,           // 188\r
+               DRAGON_ATTACK3PIC,           // 189\r
+               DRAGON_OUCHPIC,              // 190\r
+               DRAGON_DEATH1PIC,            // 191\r
+               DRAGON_DEATH2PIC,            // 192\r
+               DRAGON_DEATH3PIC,            // 193\r
+               // Lump Start\r
+               BUNNY_LEFT1PIC,              // 194\r
+               BUNNY_LEFT2PIC,              // 195\r
+               BUNNY_RIGHT1PIC,             // 196\r
+               BUNNY_RIGHT2PIC,             // 197\r
+               BUNNY_META1PIC,              // 198\r
+               BUNNY_META2PIC,              // 199\r
+               BUNNY_WALK1PIC,              // 200\r
+               BUNNY_WALK2PIC,              // 201\r
+               BUNNY_OUCHPIC,               // 202\r
+               BUNNY_DEATH1PIC,             // 203\r
+               BUNNY_DEATH2PIC,             // 204\r
+               // Lump Start\r
+               ARCH1PIC,                    // 205\r
+               // Lump Start\r
+               ARCH2PIC,                    // 206\r
+               // Lump Start\r
+               ARCH3PIC,                    // 207\r
+               // Lump Start\r
+               ARCH4PIC,                    // 208\r
+               // Lump Start\r
+               ARCH5PIC,                    // 209\r
+               // Lump Start\r
+               ARCH6PIC,                    // 210\r
+               // Lump Start\r
+               ARCH7PIC,                    // 211\r
+               // Lump Start\r
+               ARCH8PIC,                    // 212\r
+               // Lump Start\r
+               ARCH9PIC,                    // 213\r
+               // Lump Start\r
+               ARCH10PIC,                   // 214\r
+               // Lump Start\r
+               ARCH11PIC,                   // 215\r
+               // Lump Start\r
+               ARCH12PIC,                   // 216\r
+               // Lump Start\r
+               ARCH13PIC,                   // 217\r
+               // Lump Start\r
+               ANT_HILLPIC,                 // 218\r
+               // Lump Start\r
+               COLUMNPIC,                   // 219\r
+               // Lump Start\r
+               SULPHUR_GAS_1PIC,            // 220\r
+               SULPHUR_GAS_2PIC,            // 221\r
+               SULPHUR_GAS_3PIC,            // 222\r
+               // Lump Start\r
+               FIRE_POT_1PIC,               // 223\r
+               FIRE_POT_2PIC,               // 224\r
+               // Lump Start\r
+               SKEL_HANGPIC,                // 225\r
+               // Lump Start\r
+               FORCE_FIELD_1PIC,            // 226\r
+               FORCE_FIELD_2PIC,            // 227\r
+               FORCE_FIELD_3PIC,            // 228\r
+               FORCE_FIELD_4PIC,            // 229\r
+               // Lump Start\r
+               WFOUNTAINPIC,                // 230\r
+               FIRSTWALLPIC,                // 231\r
+               CRYSTAL_LIGHT_1PIC,          // 232\r
+               CRYSTAL_LIGHT_2PIC,          // 233\r
+               CRYSTAL_LIGHT_3PIC,          // 234\r
+               CRYSTAL_LIGHT_4PIC,          // 235\r
+               CRYSTAL_DARK_1PIC,           // 236\r
+               CRYSTAL_DARK_2PIC,           // 237\r
+               CRYSTAL_DARK_3PIC,           // 238\r
+               CRYSTAL_DARK_4PIC,           // 239\r
+               FIRE_WALL_1PIC,              // 240\r
+               FIRE_WALL_2PIC,              // 241\r
+               FIRE_WALL_3PIC,              // 242\r
+               FIRE_WALL_4PIC,              // 243\r
+               BRN_STONE_GATEPIC,           // 244\r
+               BRN_STONE_WALL_1PIC,         // 245\r
+               BRN_STONE_WALL_2PIC,         // 246\r
+               KUDZU_LIGHT_WALLPIC,         // 247\r
+               KUDZU_DARK_WALLPIC,          // 248\r
+               HEDGE_WALLPIC,               // 249\r
+               HEDGE_EYESPIC,               // 250\r
+               BRN_WINDOW_LIGHTPIC,         // 251\r
+               ALTAR_LEFTPIC,               // 252\r
+               ALTAR_RIGHTPIC,              // 253\r
+               GRAY_LIGHT_WALLPIC,          // 254\r
+               GRAY_DARK_WALLPIC,           // 255\r
+               GRAY_LIGHT_SIGNPIC,          // 256\r
+               GRAY_DARK_SIGNPIC,           // 257\r
+               MANICLE_LIGHT_BLOODYPIC,     // 258\r
+               MANICLE_DARK_BLOODYPIC,      // 259\r
+               LIGHT_CURTAIN_WINDOWPIC,     // 260\r
+               LIGHT_CURTAIN_WALLPIC,       // 261\r
+               DARK_CURTAIN_WINDOWPIC,      // 262\r
+               DARK_CURTAIN_WALLPIC,        // 263\r
+               BRN_LIGHT_SIGNPIC,           // 264\r
+               BRN_DARK_SIGNPIC,            // 265\r
+               LIGHT_STONE_WALLPIC,         // 266\r
+               DARK_STONE_WALLPIC,          // 267\r
+               BRN_FLAGSTONE_LIGHT_2PIC,    // 268\r
+               BRN_FLAGSTONE_DARK_2PIC,     // 269\r
+               RUST_METAL_LIGHTPIC,         // 270\r
+               RUST_METAL_DARKPIC,          // 271\r
+               GRAY_METAL_LIGHTPIC,         // 272\r
+               GRAY_METAL_DARKPIC,          // 273\r
+               WEAK_STONE_LIGHTPIC,         // 274\r
+               WEAK_STONE_DARKPIC,          // 275\r
+               WEAK_GRAY_RFGSTN_LIGHTPIC,   // 276\r
+               WEAK_GRAY_RFGSTN_DARKPIC,    // 277\r
+               WEAK_CRYSTAL_LIGHTPIC,       // 278\r
+               WEAK_CRYSTAL_DARKPIC,        // 279\r
+               RED_MUD_LIGHTPIC,            // 280\r
+               BRN_MUD_DARKPIC,             // 281\r
+               RED_MUD_WEAK_LIGHTPIC,       // 282\r
+               BRN_MUD_WEAK_DARKPIC,        // 283\r
+               HORN_DOORPIC,                // 284\r
+               CLOSED_DOOR_1PIC,            // 285\r
+               DOOR_2PIC,                   // 286\r
+               WATER_LIGHT_WEAK_1PIC,       // 287\r
+               WATER_LIGHT_WEAK_2PIC,       // 288\r
+               WATER_LIGHT_WEAK_3PIC,       // 289\r
+               WATER_DARK_WEAK_1PIC,        // 290\r
+               WATER_DARK_WEAK_2PIC,        // 291\r
+               WATER_DARK_WEAK_3PIC,        // 292\r
+               WATER_LIGHT_1PIC,            // 293\r
+               WATER_LIGHT_2PIC,            // 294\r
+               WATER_LIGHT_3PIC,            // 295\r
+               WATER_DARK_1PIC,             // 296\r
+               WATER_DARK_2PIC,             // 297\r
+               WATER_DARK_3PIC,             // 298\r
+               TROLL_LIGHT_STONEPIC,        // 299\r
+               TROLL_DARK_STONEPIC,         // 300\r
+               TROLL_BLOODY_LT_STONEPIC,    // 301\r
+               TROLL_BLOODY_DK_STONEPIC,    // 302\r
+               LIGHT_BREATH_1PIC,           // 303\r
+               LIGHT_BREATH_2PIC,           // 304\r
+               LIGHT_BREATH_3PIC,           // 305\r
+               DARK_BREATH_1PIC,            // 306\r
+               DARK_BREATH_2PIC,            // 307\r
+               DARK_BREATH_3PIC,            // 308\r
+               EXP_WALL_1PIC,               // 309\r
+               EXP_WALL_2PIC,               // 310\r
+               EXP_WALL_3PIC,               // 311\r
+               WATER_EXP_WALL_1PIC,         // 312\r
+               WATER_EXP_WALL_2PIC,         // 313\r
+               WATER_EXP_WALL_3PIC,         // 314\r
+               W_GEN_DOOR1PIC,              // 315\r
+               W_GEN_DOOR2PIC,              // 316\r
+               W_CRYSTAL_DOORPIC,           // 317\r
+               DMG_BRN_FSTN_LTPIC,          // 318\r
+               DMG_BRN_FSTN_DKPIC,          // 319\r
+               DMG_FIN_FSTN_LTPIC,          // 320\r
+               DMG_FIN_FSTN_DKPIC,          // 321\r
+               STEEL_DOOR1PIC,              // 322\r
+               STEEL_DOOR2PIC,              // 323\r
+               BRN_WINDOW_DARKPIC,          // 324\r
+               GRY_DOOR_LTPIC,              // 325\r
+               GRY_DOOR_DKPIC,              // 326\r
+               BRN_DOOR_LTPIC,              // 327\r
+               BRN_DOOR_DKPIC,              // 328\r
+               GRY_FGSTN_LTPIC,             // 329\r
+               GRY_FGSTN_DKPIC,             // 330\r
+               KUDZU_WEAK_LIGHTPIC,         // 331\r
+               KUDZU_WEAK_DARKPIC,          // 332\r
+               LT_SKEL1PIC,                 // 333\r
+               DK_SKEL1PIC,                 // 334\r
+               LT_SKEL2PIC,                 // 335\r
+               DK_SKEL2PIC,                 // 336\r
+               MANICLE_LIGHT_WALLPIC,       // 337\r
+               MANICLE_DARK_WALLPIC,        // 338\r
+               TAP_1PIC,                    // 339\r
+               TAP_2PIC,                    // 340\r
+               TAP_3PIC,                    // 341\r
+               TAP_4PIC,                    // 342\r
+               TAP_5PIC,                    // 343\r
+               FINALWALLPIC,                // 344\r
+               WATER_DOOR1_PIC,             // 345\r
+               WATER_DOOR2_PIC,             // 346\r
+               LASTWALLPIC,                 // 347\r
+\r
+               HAND1PICM=348,\r
+\r
+               NORTHICONSPR=349,\r
+\r
+               LEVEL1TEXT=640,\r
+               LEVEL2TEXT,                  // 641\r
+               LEVEL3TEXT,                  // 642\r
+               LEVEL4TEXT,                  // 643\r
+               LEVEL5TEXT,                  // 644\r
+               LEVEL6TEXT,                  // 645\r
+               LEVEL7TEXT,                  // 646\r
+               LEVEL8TEXT,                  // 647\r
+               LEVEL9TEXT,                  // 648\r
+               LEVEL10TEXT,                 // 649\r
+               LEVEL11TEXT,                 // 650\r
+               LEVEL12TEXT,                 // 651\r
+               LEVEL13TEXT,                 // 652\r
+               LEVEL14TEXT,                 // 653\r
+               LEVEL15TEXT,                 // 654\r
+               LEVEL16TEXT,                 // 655\r
+               LEVEL17TEXT,                 // 656\r
+               PIRACY,                      // 657\r
+               ENUMEND\r
+            } graphicnums;\r
+\r
+//\r
+// Data LUMPs\r
+//\r
+#define SKELDUDE_LUMP_START            22\r
+#define SKELDUDE_LUMP_END              31\r
+\r
+#define TOMBSTONES_LUMP_START          32\r
+#define TOMBSTONES_LUMP_END            34\r
+\r
+#define OBJ_WARP_LUMP_START            35\r
+#define OBJ_WARP_LUMP_END              38\r
+\r
+#define EYE_LUMP_START                 39\r
+#define EYE_LUMP_END                   49\r
+\r
+#define ZOMBIE_LUMP_START              50\r
+#define ZOMBIE_LUMP_END                        61\r
+\r
+#define BOLT_LUMP_START                        62\r
+#define BOLT_LUMP_END                  64\r
+\r
+#define NUKE_LUMP_START                        65\r
+#define NUKE_LUMP_END                  67\r
+\r
+#define TIME_LUMP_START                        68\r
+#define TIME_LUMP_END                  69\r
+\r
+#define O_WATER_CHEST_LUMP_START       70\r
+#define O_WATER_CHEST_LUMP_END         71\r
+\r
+#define POTION_LUMP_START              72\r
+#define POTION_LUMP_END                        72\r
+\r
+#define RKEY_LUMP_START                        73\r
+#define RKEY_LUMP_END                  73\r
+\r
+#define YKEY_LUMP_START                        74\r
+#define YKEY_LUMP_END                  74\r
+\r
+#define GKEY_LUMP_START                        75\r
+#define GKEY_LUMP_END                  75\r
+\r
+#define BKEY_LUMP_START                        76\r
+#define BKEY_LUMP_END                  76\r
+\r
+#define RGEM_LUMP_START                        77\r
+#define RGEM_LUMP_END                  78\r
+\r
+#define GGEM_LUMP_START                        79\r
+#define GGEM_LUMP_END                  80\r
+\r
+#define BGEM_LUMP_START                        81\r
+#define BGEM_LUMP_END                  82\r
+\r
+#define YGEM_LUMP_START                        83\r
+#define YGEM_LUMP_END                  84\r
+\r
+#define PGEM_LUMP_START                        85\r
+#define PGEM_LUMP_END                  86\r
+\r
+#define CHEST_LUMP_START               87\r
+#define CHEST_LUMP_END                 87\r
+\r
+#define PLAYER_LUMP_START              88\r
+#define PLAYER_LUMP_END                        92\r
+\r
+#define REDDEMON_LUMP_START            93\r
+#define REDDEMON_LUMP_END              103\r
+\r
+#define MAGE_LUMP_START                        104\r
+#define MAGE_LUMP_END                  109\r
+\r
+#define BAT_LUMP_START                 110\r
+#define BAT_LUMP_END                   115\r
+\r
+#define GREL_LUMP_START                        116\r
+#define GREL_LUMP_END                  126\r
+\r
+#define GODESS_LUMP_START              127\r
+#define GODESS_LUMP_END                        136\r
+\r
+#define ANT_LUMP_START                 137\r
+#define ANT_LUMP_END                   145\r
+\r
+#define FATDEMON_LUMP_START            146\r
+#define FATDEMON_LUMP_END              157\r
+\r
+#define SUCCUBUS_LUMP_START            158\r
+#define SUCCUBUS_LUMP_END              167\r
+\r
+#define TREE_LUMP_START                        168\r
+#define TREE_LUMP_END                  177\r
+\r
+#define DRAGON_LUMP_START              178\r
+#define DRAGON_LUMP_END                        193\r
+\r
+#define BUNNY_LUMP_START               194\r
+#define BUNNY_LUMP_END                 204\r
+\r
+#define ARCH1_LUMP_START               205\r
+#define ARCH1_LUMP_END                 205\r
+\r
+#define ARCH2_LUMP_START               206\r
+#define ARCH2_LUMP_END                 206\r
+\r
+#define ARCH3_LUMP_START               207\r
+#define ARCH3_LUMP_END                 207\r
+\r
+#define ARCH4_LUMP_START               208\r
+#define ARCH4_LUMP_END                 208\r
+\r
+#define ARCH5_LUMP_START               209\r
+#define ARCH5_LUMP_END                 209\r
+\r
+#define ARCH6_LUMP_START               210\r
+#define ARCH6_LUMP_END                 210\r
+\r
+#define ARCH7_LUMP_START               211\r
+#define ARCH7_LUMP_END                 211\r
+\r
+#define ARCH8_LUMP_START               212\r
+#define ARCH8_LUMP_END                 212\r
+\r
+#define ARCH9_LUMP_START               213\r
+#define ARCH9_LUMP_END                 213\r
+\r
+#define ARCH10_LUMP_START              214\r
+#define ARCH10_LUMP_END                        214\r
+\r
+#define ARCH11_LUMP_START              215\r
+#define ARCH11_LUMP_END                        215\r
+\r
+#define ARCH12_LUMP_START              216\r
+#define ARCH12_LUMP_END                        216\r
+\r
+#define ARCH13_LUMP_START              217\r
+#define ARCH13_LUMP_END                        217\r
+\r
+#define ANTHILL_LUMP_START             218\r
+#define ANTHILL_LUMP_END               218\r
+\r
+#define COLUMN_LUMP_START              219\r
+#define COLUMN_LUMP_END                        219\r
+\r
+#define SULPHURGAS_LUMP_START          220\r
+#define SULPHURGAS_LUMP_END            222\r
+\r
+#define FIREPOT_LUMP_START             223\r
+#define FIREPOT_LUMP_END               224\r
+\r
+#define SKELHANG_LUMP_START            225\r
+#define SKELHANG_LUMP_END              225\r
+\r
+#define FORCEFIELD_LUMP_START          226\r
+#define FORCEFIELD_LUMP_END            229\r
+\r
+#define FOUNTAIN_LUMP_START            230\r
+#define FOUNTAIN_LUMP_END              230\r
+\r
+\r
+//\r
+// Amount of each data item\r
+//\r
+#define NUMCHUNKS    658\r
+#define NUMFONT      1\r
+#define NUMFONTM     0\r
+#define NUMPICS      344\r
+#define NUMPICM      1\r
+#define NUMSPRITES   1\r
+#define NUMTILE8     108\r
+#define NUMTILE8M    36\r
+#define NUMTILE16    216\r
+#define NUMTILE16M   72\r
+#define NUMTILE32    0\r
+#define NUMTILE32M   0\r
+#define NUMEXTERNS   18\r
+//\r
+// File offsets for data items\r
+//\r
+#define STRUCTPIC    0\r
+#define STRUCTPICM   1\r
+#define STRUCTSPRITE 2\r
+\r
+#define STARTFONT    3\r
+#define STARTFONTM   4\r
+#define STARTPICS    4\r
+#define STARTPICM    348\r
+#define STARTSPRITES 349\r
+#define STARTTILE8   350\r
+#define STARTTILE8M  351\r
+#define STARTTILE16  352\r
+#define STARTTILE16M 568\r
+#define STARTTILE32  640\r
+#define STARTTILE32M 640\r
+#define STARTEXTERNS 640\r
+\r
+//\r
+// Thank you for using IGRAB!\r
+//\r
diff --git a/16/cawat/ID_ASM.EQU b/16/cawat/ID_ASM.EQU
new file mode 100644 (file)
index 0000000..01fe696
--- /dev/null
@@ -0,0 +1,114 @@
+;\r
+; Equates for all .ASM files\r
+;\r
+\r
+;----------------------------------------------------------------------------\r
+\r
+INCLUDE        "GFXE_ARM.EQU"\r
+\r
+;----------------------------------------------------------------------------\r
+\r
+CGAGR          =       1\r
+EGAGR          =       2\r
+VGAGR          =       3\r
+\r
+GRMODE         =       EGAGR\r
+PROFILE                =       0                       ; 1=keep stats on tile drawing\r
+\r
+SC_INDEX       =       03C4h\r
+SC_RESET       =       0\r
+SC_CLOCK       =       1\r
+SC_MAPMASK     =       2\r
+SC_CHARMAP     =       3\r
+SC_MEMMODE     =       4\r
+\r
+CRTC_INDEX     =       03D4h\r
+CRTC_H_TOTAL   =       0\r
+CRTC_H_DISPEND =       1\r
+CRTC_H_BLANK   =       2\r
+CRTC_H_ENDBLANK        =       3\r
+CRTC_H_RETRACE =       4\r
+CRTC_H_ENDRETRACE =    5\r
+CRTC_V_TOTAL   =       6\r
+CRTC_OVERFLOW  =       7\r
+CRTC_ROWSCAN   =       8\r
+CRTC_MAXSCANLINE =     9\r
+CRTC_CURSORSTART =     10\r
+CRTC_CURSOREND =       11\r
+CRTC_STARTHIGH =       12\r
+CRTC_STARTLOW  =       13\r
+CRTC_CURSORHIGH        =       14\r
+CRTC_CURSORLOW =       15\r
+CRTC_V_RETRACE =       16\r
+CRTC_V_ENDRETRACE =    17\r
+CRTC_V_DISPEND =       18\r
+CRTC_OFFSET    =       19\r
+CRTC_UNDERLINE =       20\r
+CRTC_V_BLANK   =       21\r
+CRTC_V_ENDBLANK        =       22\r
+CRTC_MODE      =       23\r
+CRTC_LINECOMPARE =     24\r
+\r
+\r
+GC_INDEX       =       03CEh\r
+GC_SETRESET    =       0\r
+GC_ENABLESETRESET =    1\r
+GC_COLORCOMPARE        =       2\r
+GC_DATAROTATE  =       3\r
+GC_READMAP     =       4\r
+GC_MODE                =       5\r
+GC_MISCELLANEOUS =     6\r
+GC_COLORDONTCARE =     7\r
+GC_BITMASK     =       8\r
+\r
+ATR_INDEX      =       03c0h\r
+ATR_MODE       =       16\r
+ATR_OVERSCAN   =       17\r
+ATR_COLORPLANEENABLE = 18\r
+ATR_PELPAN     =       19\r
+ATR_COLORSELECT        =       20\r
+\r
+STATUS_REGISTER_1     =        03dah\r
+\r
+\r
+MACRO  WORDOUT\r
+       out     dx,ax\r
+ENDM\r
+\r
+if 0\r
+\r
+MACRO  WORDOUT\r
+       out     dx,al\r
+       inc     dx\r
+       xchg    al,ah\r
+       out     dx,al\r
+       dec     dx\r
+       xchg    al,ah\r
+ENDM\r
+\r
+endif\r
+\r
+UPDATEWIDE     =       22\r
+UPDATEHIGH     =       13              ; hack for catacombs\r
+\r
+;\r
+; tile info offsets from segment tinf\r
+;\r
+\r
+SPEED          =       402\r
+ANIM           =       (SPEED+NUMTILE16)\r
+\r
+NORTHWALL      =       (ANIM+NUMTILE16)\r
+EASTWALL       =       (NORTHWALL+NUMTILE16M)\r
+SOUTHWALL   =  (EASTWALL+NUMTILE16M)\r
+WESTWALL    =  (SOUTHWALL+NUMTILE16M)\r
+MANIM       =  (WESTWALL+NUMTILE16M)\r
+INTILE      =  (MANIM+NUMTILE16M)\r
+MSPEED      =  (INTILE+NUMTILE16M)\r
+\r
+IFE GRMODE-EGAGR\r
+SCREENWIDTH    =       40\r
+ENDIF\r
+IFE GRMODE-CGAGR\r
+SCREENWIDTH    =       128\r
+ENDIF\r
diff --git a/16/cawat/ID_CA.C b/16/cawat/ID_CA.C
new file mode 100644 (file)
index 0000000..31cf7bf
--- /dev/null
@@ -0,0 +1,2190 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_CA.C\r
+\r
+/*\r
+=============================================================================\r
+\r
+Id Software Caching Manager\r
+---------------------------\r
+\r
+Must be started BEFORE the memory manager, because it needs to get the headers\r
+loaded into the data segment\r
+\r
+=============================================================================\r
+*/\r
+\r
+#include "LIB_HEAD.H"\r
+#pragma hdrstop\r
+//#include "ID_STRS.H"\r
+\r
+#pragma warn -pro\r
+#pragma warn -use\r
+\r
+#define THREEBYTEGRSTARTS\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+typedef struct\r
+{\r
+  unsigned bit0,bit1;  // 0-255 is a character, > is a pointer to a node\r
+} huffnode;\r
+\r
+\r
+typedef struct\r
+{\r
+       unsigned        RLEWtag;\r
+       long            headeroffsets[100];\r
+       byte            tileinfo[];\r
+} mapfiletype;\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+byte           _seg    *tinf;\r
+int                    mapon;\r
+\r
+unsigned       _seg    *mapsegs[3];\r
+maptype                _seg    *mapheaderseg[NUMMAPS];\r
+byte           _seg    *audiosegs[NUMSNDCHUNKS];\r
+void           _seg    *grsegs[NUMCHUNKS];\r
+\r
+byte           far     grneeded[NUMCHUNKS];\r
+byte           ca_levelbit,ca_levelnum;\r
+\r
+int                    profilehandle,debughandle;\r
+\r
+void   (*drawcachebox)         (char *title, unsigned numcache);\r
+void   (*updatecachebox)       (void);\r
+void   (*finishcachebox)       (void);\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+extern long    far     CGAhead;\r
+extern long    far     EGAhead;\r
+extern byte    CGAdict;\r
+extern byte    EGAdict;\r
+extern byte    far     maphead;\r
+extern byte    mapdict;\r
+extern byte    far     audiohead;\r
+extern byte    audiodict;\r
+\r
+\r
+long           _seg *grstarts; // array of offsets in egagraph, -1 for sparse\r
+long           _seg *audiostarts;      // array of offsets in audio / audiot\r
+\r
+#ifdef GRHEADERLINKED\r
+huffnode       *grhuffman;\r
+#else\r
+huffnode       grhuffman[255];\r
+#endif\r
+\r
+#ifdef AUDIOHEADERLINKED\r
+huffnode       *audiohuffman;\r
+#else\r
+huffnode       audiohuffman[255];\r
+#endif\r
+\r
+\r
+int                    grhandle;               // handle to EGAGRAPH\r
+int                    maphandle;              // handle to MAPTEMP / GAMEMAPS\r
+int                    audiohandle;    // handle to AUDIOT / AUDIO\r
+\r
+long           chunkcomplen,chunkexplen;\r
+\r
+SDMode         oldsoundmode;\r
+\r
+\r
+\r
+void   CAL_DialogDraw (char *title,unsigned numcache);\r
+void   CAL_DialogUpdate (void);\r
+void   CAL_DialogFinish (void);\r
+void   CAL_CarmackExpand (unsigned far *source, unsigned far *dest,\r
+               unsigned length);\r
+\r
+\r
+#ifdef THREEBYTEGRSTARTS\r
+#define FILEPOSSIZE    3\r
+//#define      GRFILEPOS(c) (*(long far *)(((byte far *)grstarts)+(c)*3)&0xffffff)\r
+long GRFILEPOS(int c)\r
+{\r
+       long value;\r
+       int     offset;\r
+\r
+       offset = c*3;\r
+\r
+       value = *(long far *)(((byte far *)grstarts)+offset);\r
+\r
+       value &= 0x00ffffffl;\r
+\r
+       if (value == 0xffffffl)\r
+               value = -1;\r
+\r
+       return value;\r
+};\r
+#else\r
+#define FILEPOSSIZE    4\r
+#define        GRFILEPOS(c) (grstarts[c])\r
+#endif\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                          LOW LEVEL ROUTINES\r
+\r
+=============================================================================\r
+*/\r
+\r
+/*\r
+============================\r
+=\r
+= CA_OpenDebug / CA_CloseDebug\r
+=\r
+= Opens a binary file with the handle "debughandle"\r
+=\r
+============================\r
+*/\r
+\r
+void CA_OpenDebug (void)\r
+{\r
+       unlink ("DEBUG.TXT");\r
+       debughandle = open("DEBUG.TXT", O_CREAT | O_WRONLY | O_TEXT);\r
+}\r
+\r
+void CA_CloseDebug (void)\r
+{\r
+       close (debughandle);\r
+}\r
+\r
+\r
+\r
+/*\r
+============================\r
+=\r
+= CAL_GetGrChunkLength\r
+=\r
+= Gets the length of an explicit length chunk (not tiles)\r
+= The file pointer is positioned so the compressed data can be read in next.\r
+=\r
+============================\r
+*/\r
+\r
+void CAL_GetGrChunkLength (int chunk)\r
+{\r
+       lseek(grhandle,GRFILEPOS(chunk),SEEK_SET);\r
+       read(grhandle,&chunkexplen,sizeof(chunkexplen));\r
+       chunkcomplen = GRFILEPOS(chunk+1)-GRFILEPOS(chunk)-4;\r
+}\r
+\r
+\r
+/*\r
+==========================\r
+=\r
+= CA_FarRead\r
+=\r
+= Read from a file to a far pointer\r
+=\r
+==========================\r
+*/\r
+\r
+boolean CA_FarRead (int handle, byte far *dest, long length)\r
+{\r
+       if (length>0xffffl)\r
+               Quit ("CA_FarRead doesn't support 64K reads yet!");\r
+\r
+asm            push    ds\r
+asm            mov     bx,[handle]\r
+asm            mov     cx,[WORD PTR length]\r
+asm            mov     dx,[WORD PTR dest]\r
+asm            mov     ds,[WORD PTR dest+2]\r
+asm            mov     ah,0x3f                         // READ w/handle\r
+asm            int     21h\r
+asm            pop     ds\r
+asm            jnc     good\r
+       errno = _AX;\r
+       return  false;\r
+good:\r
+asm            cmp     ax,[WORD PTR length]\r
+asm            je      done\r
+       errno = EINVFMT;                        // user manager knows this is bad read\r
+       return  false;\r
+done:\r
+       return  true;\r
+}\r
+\r
+\r
+/*\r
+==========================\r
+=\r
+= CA_SegWrite\r
+=\r
+= Write from a file to a far pointer\r
+=\r
+==========================\r
+*/\r
+\r
+boolean CA_FarWrite (int handle, byte far *source, long length)\r
+{\r
+       if (length>0xffffl)\r
+               Quit ("CA_FarWrite doesn't support 64K reads yet!");\r
+\r
+asm            push    ds\r
+asm            mov     bx,[handle]\r
+asm            mov     cx,[WORD PTR length]\r
+asm            mov     dx,[WORD PTR source]\r
+asm            mov     ds,[WORD PTR source+2]\r
+asm            mov     ah,0x40                 // WRITE w/handle\r
+asm            int     21h\r
+asm            pop     ds\r
+asm            jnc     good\r
+       errno = _AX;\r
+       return  false;\r
+good:\r
+asm            cmp     ax,[WORD PTR length]\r
+asm            je      done\r
+       errno = ENOMEM;                         // user manager knows this is bad write\r
+       return  false;\r
+\r
+done:\r
+       return  true;\r
+}\r
+\r
+\r
+/*\r
+==========================\r
+=\r
+= CA_ReadFile\r
+=\r
+= Reads a file into an allready allocated buffer\r
+=\r
+==========================\r
+*/\r
+\r
+boolean CA_ReadFile (char *filename, memptr *ptr)\r
+{\r
+       int handle;\r
+       long size;\r
+\r
+       if ((handle = open(filename,O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+               return false;\r
+\r
+       size = filelength (handle);\r
+       if (!CA_FarRead (handle,*ptr,size))\r
+       {\r
+               close (handle);\r
+               return false;\r
+       }\r
+       close (handle);\r
+       return true;\r
+}\r
+\r
+\r
+\r
+/*\r
+==========================\r
+=\r
+= CA_LoadFile\r
+=\r
+= Allocate space for and load a file\r
+=\r
+==========================\r
+*/\r
+\r
+boolean CA_LoadFile (char *filename, memptr *ptr)\r
+{\r
+       int handle;\r
+       long size;\r
+\r
+       if ((handle = open(filename,O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+               return false;\r
+\r
+       size = filelength (handle);\r
+       MM_GetPtr (ptr,size);\r
+       if (!CA_FarRead (handle,*ptr,size))\r
+       {\r
+               close (handle);\r
+               return false;\r
+       }\r
+       close (handle);\r
+       return true;\r
+}\r
+\r
+/*\r
+============================================================================\r
+\r
+               COMPRESSION routines, see JHUFF.C for more\r
+\r
+============================================================================\r
+*/\r
+\r
+\r
+\r
+/*\r
+===============\r
+=\r
+= CAL_OptimizeNodes\r
+=\r
+= Goes through a huffman table and changes the 256-511 node numbers to the\r
+= actular address of the node.  Must be called before CAL_HuffExpand\r
+=\r
+===============\r
+*/\r
+\r
+void CAL_OptimizeNodes (huffnode *table)\r
+{\r
+  huffnode *node;\r
+  int i;\r
+\r
+  node = table;\r
+\r
+  for (i=0;i<255;i++)\r
+  {\r
+       if (node->bit0 >= 256)\r
+         node->bit0 = (unsigned)(table+(node->bit0-256));\r
+       if (node->bit1 >= 256)\r
+         node->bit1 = (unsigned)(table+(node->bit1-256));\r
+       node++;\r
+  }\r
+}\r
+\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_HuffExpand\r
+=\r
+= Length is the length of the EXPANDED data\r
+=\r
+======================\r
+*/\r
+\r
+void CAL_HuffExpand (byte huge *source, byte huge *dest,\r
+  long length,huffnode *hufftable)\r
+{\r
+//  unsigned bit,byte,node,code;\r
+  unsigned sourceseg,sourceoff,destseg,destoff,endoff;\r
+  huffnode *headptr;\r
+//  huffnode *nodeon;\r
+\r
+  headptr = hufftable+254;     // head node is allways node 254\r
+\r
+  source++;    // normalize\r
+  source--;\r
+  dest++;\r
+  dest--;\r
+\r
+  sourceseg = FP_SEG(source);\r
+  sourceoff = FP_OFF(source);\r
+  destseg = FP_SEG(dest);\r
+  destoff = FP_OFF(dest);\r
+  endoff = destoff+length;\r
+\r
+//\r
+// ds:si source\r
+// es:di dest\r
+// ss:bx node pointer\r
+//\r
+\r
+       if (length <0xfff0)\r
+       {\r
+\r
+//--------------------------\r
+// expand less than 64k of data\r
+//--------------------------\r
+\r
+asm mov        bx,[headptr]\r
+\r
+asm    mov     si,[sourceoff]\r
+asm    mov     di,[destoff]\r
+asm    mov     es,[destseg]\r
+asm    mov     ds,[sourceseg]\r
+asm    mov     ax,[endoff]\r
+\r
+asm    mov     ch,[si]                         // load first byte\r
+asm    inc     si\r
+asm    mov     cl,1\r
+\r
+expandshort:\r
+asm    test    ch,cl                   // bit set?\r
+asm    jnz     bit1short\r
+asm    mov     dx,[ss:bx]                      // take bit0 path from node\r
+asm    shl     cl,1                            // advance to next bit position\r
+asm    jc      newbyteshort\r
+asm    jnc     sourceupshort\r
+\r
+bit1short:\r
+asm    mov     dx,[ss:bx+2]            // take bit1 path\r
+asm    shl     cl,1                            // advance to next bit position\r
+asm    jnc     sourceupshort\r
+\r
+newbyteshort:\r
+asm    mov     ch,[si]                         // load next byte\r
+asm    inc     si\r
+asm    mov     cl,1                            // back to first bit\r
+\r
+sourceupshort:\r
+asm    or      dh,dh                           // if dx<256 its a byte, else move node\r
+asm    jz      storebyteshort\r
+asm    mov     bx,dx                           // next node = (huffnode *)code\r
+asm    jmp     expandshort\r
+\r
+storebyteshort:\r
+asm    mov     [es:di],dl\r
+asm    inc     di                                      // write a decopmpressed byte out\r
+asm    mov     bx,[headptr]            // back to the head node for next bit\r
+\r
+asm    cmp     di,ax                           // done?\r
+asm    jne     expandshort\r
+       }\r
+       else\r
+       {\r
+\r
+//--------------------------\r
+// expand more than 64k of data\r
+//--------------------------\r
+\r
+  length--;\r
+\r
+asm mov        bx,[headptr]\r
+asm    mov     cl,1\r
+\r
+asm    mov     si,[sourceoff]\r
+asm    mov     di,[destoff]\r
+asm    mov     es,[destseg]\r
+asm    mov     ds,[sourceseg]\r
+\r
+asm    lodsb                   // load first byte\r
+\r
+expand:\r
+asm    test    al,cl           // bit set?\r
+asm    jnz     bit1\r
+asm    mov     dx,[ss:bx]      // take bit0 path from node\r
+asm    jmp     gotcode\r
+bit1:\r
+asm    mov     dx,[ss:bx+2]    // take bit1 path\r
+\r
+gotcode:\r
+asm    shl     cl,1            // advance to next bit position\r
+asm    jnc     sourceup\r
+asm    lodsb\r
+asm    cmp     si,0x10         // normalize ds:si\r
+asm    jb      sinorm\r
+asm    mov     cx,ds\r
+asm    inc     cx\r
+asm    mov     ds,cx\r
+asm    xor     si,si\r
+sinorm:\r
+asm    mov     cl,1            // back to first bit\r
+\r
+sourceup:\r
+asm    or      dh,dh           // if dx<256 its a byte, else move node\r
+asm    jz      storebyte\r
+asm    mov     bx,dx           // next node = (huffnode *)code\r
+asm    jmp     expand\r
+\r
+storebyte:\r
+asm    mov     [es:di],dl\r
+asm    inc     di              // write a decopmpressed byte out\r
+asm    mov     bx,[headptr]    // back to the head node for next bit\r
+\r
+asm    cmp     di,0x10         // normalize es:di\r
+asm    jb      dinorm\r
+asm    mov     dx,es\r
+asm    inc     dx\r
+asm    mov     es,dx\r
+asm    xor     di,di\r
+dinorm:\r
+\r
+asm    sub     [WORD PTR ss:length],1\r
+asm    jnc     expand\r
+asm    dec     [WORD PTR ss:length+2]\r
+asm    jns     expand          // when length = ffff ffff, done\r
+\r
+       }\r
+\r
+asm    mov     ax,ss\r
+asm    mov     ds,ax\r
+\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_CarmackExpand\r
+=\r
+= Length is the length of the EXPANDED data\r
+=\r
+======================\r
+*/\r
+\r
+#define NEARTAG        0xa7\r
+#define FARTAG 0xa8\r
+\r
+void CAL_CarmackExpand (unsigned far *source, unsigned far *dest, unsigned length)\r
+{\r
+       unsigned        ch,chhigh,count,offset;\r
+       unsigned        far *copyptr, far *inptr, far *outptr;\r
+\r
+       length/=2;\r
+\r
+       inptr = source;\r
+       outptr = dest;\r
+\r
+       while (length)\r
+       {\r
+               ch = *inptr++;\r
+               chhigh = ch>>8;\r
+               if (chhigh == NEARTAG)\r
+               {\r
+                       count = ch&0xff;\r
+                       if (!count)\r
+                       {                               // have to insert a word containing the tag byte\r
+                               ch |= *((unsigned char far *)inptr)++;\r
+                               *outptr++ = ch;\r
+                               length--;\r
+                       }\r
+                       else\r
+                       {\r
+                               offset = *((unsigned char far *)inptr)++;\r
+                               copyptr = outptr - offset;\r
+                               length -= count;\r
+                               while (count--)\r
+                                       *outptr++ = *copyptr++;\r
+                       }\r
+               }\r
+               else if (chhigh == FARTAG)\r
+               {\r
+                       count = ch&0xff;\r
+                       if (!count)\r
+                       {                               // have to insert a word containing the tag byte\r
+                               ch |= *((unsigned char far *)inptr)++;\r
+                               *outptr++ = ch;\r
+                               length --;\r
+                       }\r
+                       else\r
+                       {\r
+                               offset = *inptr++;\r
+                               copyptr = dest + offset;\r
+                               length -= count;\r
+                               while (count--)\r
+                                       *outptr++ = *copyptr++;\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       *outptr++ = ch;\r
+                       length --;\r
+               }\r
+       }\r
+}\r
+\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CA_RLEWcompress\r
+=\r
+======================\r
+*/\r
+\r
+long CA_RLEWCompress (unsigned huge *source, long length, unsigned huge *dest,\r
+  unsigned rlewtag)\r
+{\r
+  long complength;\r
+  unsigned value,count,i;\r
+  unsigned huge *start,huge *end;\r
+\r
+  start = dest;\r
+\r
+  end = source + (length+1)/2;\r
+\r
+//\r
+// compress it\r
+//\r
+  do\r
+  {\r
+       count = 1;\r
+       value = *source++;\r
+       while (*source == value && source<end)\r
+       {\r
+         count++;\r
+         source++;\r
+       }\r
+       if (count>3 || value == rlewtag)\r
+       {\r
+    //\r
+    // send a tag / count / value string\r
+    //\r
+      *dest++ = rlewtag;\r
+      *dest++ = count;\r
+      *dest++ = value;\r
+    }\r
+    else\r
+    {\r
+    //\r
+    // send word without compressing\r
+    //\r
+      for (i=1;i<=count;i++)\r
+       *dest++ = value;\r
+       }\r
+\r
+  } while (source<end);\r
+\r
+  complength = 2*(dest-start);\r
+  return complength;\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CA_RLEWexpand\r
+= length is EXPANDED length\r
+=\r
+======================\r
+*/\r
+\r
+void CA_RLEWexpand (unsigned huge *source, unsigned huge *dest,long length,\r
+  unsigned rlewtag)\r
+{\r
+//  unsigned value,count,i;\r
+  unsigned huge *end;\r
+  unsigned sourceseg,sourceoff,destseg,destoff,endseg,endoff;\r
+\r
+\r
+//\r
+// expand it\r
+//\r
+#if 0\r
+  do\r
+  {\r
+       value = *source++;\r
+       if (value != rlewtag)\r
+       //\r
+       // uncompressed\r
+       //\r
+         *dest++=value;\r
+       else\r
+       {\r
+       //\r
+       // compressed string\r
+       //\r
+         count = *source++;\r
+         value = *source++;\r
+         for (i=1;i<=count;i++)\r
+       *dest++ = value;\r
+       }\r
+  } while (dest<end);\r
+#endif\r
+\r
+  end = dest + (length)/2;\r
+  sourceseg = FP_SEG(source);\r
+  sourceoff = FP_OFF(source);\r
+  destseg = FP_SEG(dest);\r
+  destoff = FP_OFF(dest);\r
+  endseg = FP_SEG(end);\r
+  endoff = FP_OFF(end);\r
+\r
+\r
+//\r
+// ax = source value\r
+// bx = tag value\r
+// cx = repeat counts\r
+// dx = scratch\r
+//\r
+// NOTE: A repeat count that produces 0xfff0 bytes can blow this!\r
+//\r
+\r
+asm    mov     bx,rlewtag\r
+asm    mov     si,sourceoff\r
+asm    mov     di,destoff\r
+asm    mov     es,destseg\r
+asm    mov     ds,sourceseg\r
+\r
+expand:\r
+asm    lodsw\r
+asm    cmp     ax,bx\r
+asm    je      repeat\r
+asm    stosw\r
+asm    jmp     next\r
+\r
+repeat:\r
+asm    lodsw\r
+asm    mov     cx,ax           // repeat count\r
+asm    lodsw                   // repeat value\r
+asm    rep stosw\r
+\r
+next:\r
+\r
+asm    cmp     si,0x10         // normalize ds:si\r
+asm    jb      sinorm\r
+asm    mov     ax,si\r
+asm    shr     ax,1\r
+asm    shr     ax,1\r
+asm    shr     ax,1\r
+asm    shr     ax,1\r
+asm    mov     dx,ds\r
+asm    add     dx,ax\r
+asm    mov     ds,dx\r
+asm    and     si,0xf\r
+sinorm:\r
+asm    cmp     di,0x10         // normalize es:di\r
+asm    jb      dinorm\r
+asm    mov     ax,di\r
+asm    shr     ax,1\r
+asm    shr     ax,1\r
+asm    shr     ax,1\r
+asm    shr     ax,1\r
+asm    mov     dx,es\r
+asm    add     dx,ax\r
+asm    mov     es,dx\r
+asm    and     di,0xf\r
+dinorm:\r
+\r
+asm    cmp     di,ss:endoff\r
+asm    jne     expand\r
+asm    mov     ax,es\r
+asm    cmp     ax,ss:endseg\r
+asm    jb      expand\r
+\r
+asm    mov     ax,ss\r
+asm    mov     ds,ax\r
+\r
+}\r
+\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                        CACHE MANAGER ROUTINES\r
+\r
+=============================================================================\r
+*/\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_SetupGrFile\r
+=\r
+======================\r
+*/\r
+\r
+void CAL_SetupGrFile (void)\r
+{\r
+       int handle;\r
+       memptr compseg;\r
+\r
+#ifdef GRHEADERLINKED\r
+\r
+#if GRMODE == EGAGR\r
+       grhuffman = (huffnode *)&EGAdict;\r
+       grstarts = (long _seg *)FP_SEG(&EGAhead);\r
+#endif\r
+#if GRMODE == CGAGR\r
+       grhuffman = (huffnode *)&CGAdict;\r
+       grstarts = (long _seg *)FP_SEG(&CGAhead);\r
+#endif\r
+\r
+       CAL_OptimizeNodes (grhuffman);\r
+\r
+#else\r
+\r
+//\r
+// load ???dict.ext (huffman dictionary for graphics files)\r
+//\r
+\r
+       if ((handle = open(GREXT"DICT."EXT,\r
+                O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+               Quit ("Can't open "GREXT"DICT."EXT"!");\r
+\r
+       read(handle, &grhuffman, sizeof(grhuffman));\r
+       close(handle);\r
+       CAL_OptimizeNodes (grhuffman);\r
+//\r
+// load the data offsets from ???head.ext\r
+//\r
+       MM_GetPtr (&(memptr)grstarts,(NUMCHUNKS+1)*FILEPOSSIZE);\r
+\r
+       if ((handle = open(GREXT"HEAD."EXT,\r
+                O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+               Quit ("Can't open "GREXT"HEAD."EXT"!");\r
+\r
+       CA_FarRead(handle, (memptr)grstarts, (NUMCHUNKS+1)*FILEPOSSIZE);\r
+\r
+       close(handle);\r
+\r
+\r
+#endif\r
+\r
+//\r
+// Open the graphics file, leaving it open until the game is finished\r
+//\r
+       grhandle = open(GREXT"GRAPH."EXT, O_RDONLY | O_BINARY);\r
+       if (grhandle == -1)\r
+               Quit ("Cannot open "GREXT"GRAPH."EXT"!");\r
+\r
+\r
+//\r
+// load the pic and sprite headers into the arrays in the data segment\r
+//\r
+#if NUMPICS>0\r
+       MM_GetPtr(&(memptr)pictable,NUMPICS*sizeof(pictabletype));\r
+       CAL_GetGrChunkLength(STRUCTPIC);                // position file pointer\r
+       MM_GetPtr(&compseg,chunkcomplen);\r
+       CA_FarRead (grhandle,compseg,chunkcomplen);\r
+       CAL_HuffExpand (compseg, (byte huge *)pictable,NUMPICS*sizeof(pictabletype),grhuffman);\r
+       MM_FreePtr(&compseg);\r
+#endif\r
+\r
+#if NUMPICM>0\r
+       MM_GetPtr(&(memptr)picmtable,NUMPICM*sizeof(pictabletype));\r
+       CAL_GetGrChunkLength(STRUCTPICM);               // position file pointer\r
+       MM_GetPtr(&compseg,chunkcomplen);\r
+       CA_FarRead (grhandle,compseg,chunkcomplen);\r
+       CAL_HuffExpand (compseg, (byte huge *)picmtable,NUMPICS*sizeof(pictabletype),grhuffman);\r
+       MM_FreePtr(&compseg);\r
+#endif\r
+\r
+#if NUMSPRITES>0\r
+       MM_GetPtr(&(memptr)spritetable,NUMSPRITES*sizeof(spritetabletype));\r
+       CAL_GetGrChunkLength(STRUCTSPRITE);     // position file pointer\r
+       MM_GetPtr(&compseg,chunkcomplen);\r
+       CA_FarRead (grhandle,compseg,chunkcomplen);\r
+       CAL_HuffExpand (compseg, (byte huge *)spritetable,NUMSPRITES*sizeof(spritetabletype),grhuffman);\r
+       MM_FreePtr(&compseg);\r
+#endif\r
+\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_SetupMapFile\r
+=\r
+======================\r
+*/\r
+\r
+void CAL_SetupMapFile (void)\r
+{\r
+       int handle;\r
+       long length;\r
+\r
+//\r
+// load maphead.ext (offsets and tileinfo for map file)\r
+//\r
+#ifndef MAPHEADERLINKED\r
+       if ((handle = open("MAPHEAD."EXT,\r
+                O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+               Quit ("Can't open MAPHEAD."EXT"!");\r
+       length = filelength(handle);\r
+       MM_GetPtr (&(memptr)tinf,length);\r
+       CA_FarRead(handle, tinf, length);\r
+       close(handle);\r
+#else\r
+\r
+       tinf = (byte _seg *)FP_SEG(&maphead);\r
+\r
+#endif\r
+\r
+//\r
+// open the data file\r
+//\r
+#ifdef MAPHEADERLINKED\r
+       if ((maphandle = open("GAMEMAPS."EXT,\r
+                O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+               Quit ("Can't open GAMEMAPS."EXT"!");\r
+#else\r
+       if ((maphandle = open("MAPTEMP."EXT,\r
+                O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+               Quit ("Can't open MAPTEMP."EXT"!");\r
+#endif\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_SetupAudioFile\r
+=\r
+======================\r
+*/\r
+\r
+void CAL_SetupAudioFile (void)\r
+{\r
+       int handle;\r
+       long length;\r
+\r
+//\r
+// load maphead.ext (offsets and tileinfo for map file)\r
+//\r
+#ifndef AUDIOHEADERLINKED\r
+       if ((handle = open("AUDIOHED."EXT,\r
+                O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+               Quit ("Can't open AUDIOHED."EXT"!");\r
+       length = filelength(handle);\r
+       MM_GetPtr (&(memptr)audiostarts,length);\r
+       CA_FarRead(handle, (byte far *)audiostarts, length);\r
+       close(handle);\r
+#else\r
+       audiohuffman = (huffnode *)&audiodict;\r
+       CAL_OptimizeNodes (audiohuffman);\r
+       audiostarts = (long _seg *)FP_SEG(&audiohead);\r
+#endif\r
+\r
+//\r
+// open the data file\r
+//\r
+#ifndef AUDIOHEADERLINKED\r
+       if ((audiohandle = open("AUDIOT."EXT,\r
+                O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+               Quit ("Can't open AUDIOT."EXT"!");\r
+#else\r
+       if ((audiohandle = open("AUDIO."EXT,\r
+                O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+               Quit ("Can't open AUDIO."EXT"!");\r
+#endif\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CA_Startup\r
+=\r
+= Open all files and load in headers\r
+=\r
+======================\r
+*/\r
+\r
+void CA_Startup (void)\r
+{\r
+#ifdef PROFILE\r
+       unlink ("PROFILE.TXT");\r
+       profilehandle = open("PROFILE.TXT", O_CREAT | O_WRONLY | O_TEXT);\r
+#endif\r
+\r
+// MDM begin - (GAMERS EDGE)\r
+//\r
+       if (!FindFile("AUDIO."EXT,NULL,2))\r
+               Quit("CA_Startup(): Can't find audio files.");\r
+//\r
+// MDM end\r
+\r
+#ifndef NOAUDIO\r
+       CAL_SetupAudioFile ();\r
+#endif\r
+\r
+// MDM begin - (GAMERS EDGE)\r
+//\r
+       if (!FindFile("GAMEMAPS."EXT,NULL,1))\r
+               Quit("CA_Startup(): Can't find level files.");\r
+//\r
+// MDM end\r
+\r
+#ifndef NOMAPS\r
+       CAL_SetupMapFile ();\r
+#endif\r
+\r
+// MDM begin - (GAMERS EDGE)\r
+//\r
+       if (!FindFile("EGAGRAPH."EXT,NULL,2))\r
+               Quit("CA_Startup(): Can't find graphics files.");\r
+//\r
+// MDM end\r
+\r
+#ifndef NOGRAPHICS\r
+       CAL_SetupGrFile ();\r
+#endif\r
+\r
+       mapon = -1;\r
+       ca_levelbit = 1;\r
+       ca_levelnum = 0;\r
+\r
+       drawcachebox    = CAL_DialogDraw;\r
+       updatecachebox  = CAL_DialogUpdate;\r
+       finishcachebox  = CAL_DialogFinish;\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CA_Shutdown\r
+=\r
+= Closes all files\r
+=\r
+======================\r
+*/\r
+\r
+void CA_Shutdown (void)\r
+{\r
+#ifdef PROFILE\r
+       close (profilehandle);\r
+#endif\r
+\r
+       close (maphandle);\r
+       close (grhandle);\r
+       close (audiohandle);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CA_CacheAudioChunk\r
+=\r
+======================\r
+*/\r
+\r
+void CA_CacheAudioChunk (int chunk)\r
+{\r
+       long    pos,compressed;\r
+#ifdef AUDIOHEADERLINKED\r
+       long    expanded;\r
+       memptr  bigbufferseg;\r
+       byte    far *source;\r
+#endif\r
+\r
+       if (audiosegs[chunk])\r
+       {\r
+               MM_SetPurge (&(memptr)audiosegs[chunk],0);\r
+               return;                                                 // allready in memory\r
+       }\r
+\r
+// MDM begin - (GAMERS EDGE)\r
+//\r
+       if (!FindFile("AUDIO."EXT,NULL,2))\r
+               Quit("CA_CacheAudioChunk(): Can't find audio files.");\r
+//\r
+// MDM end\r
+\r
+//\r
+// load the chunk into a buffer, either the miscbuffer if it fits, or allocate\r
+// a larger buffer\r
+//\r
+       pos = audiostarts[chunk];\r
+       compressed = audiostarts[chunk+1]-pos;\r
+\r
+       lseek(audiohandle,pos,SEEK_SET);\r
+\r
+#ifndef AUDIOHEADERLINKED\r
+\r
+       MM_GetPtr (&(memptr)audiosegs[chunk],compressed);\r
+       if (mmerror)\r
+               return;\r
+\r
+       CA_FarRead(audiohandle,audiosegs[chunk],compressed);\r
+\r
+#else\r
+\r
+       if (compressed<=BUFFERSIZE)\r
+       {\r
+               CA_FarRead(audiohandle,bufferseg,compressed);\r
+               source = bufferseg;\r
+       }\r
+       else\r
+       {\r
+               MM_GetPtr(&bigbufferseg,compressed);\r
+               if (mmerror)\r
+                       return;\r
+               MM_SetLock (&bigbufferseg,true);\r
+               CA_FarRead(audiohandle,bigbufferseg,compressed);\r
+               source = bigbufferseg;\r
+       }\r
+\r
+       expanded = *(long far *)source;\r
+       source += 4;                    // skip over length\r
+       MM_GetPtr (&(memptr)audiosegs[chunk],expanded);\r
+       if (mmerror)\r
+               goto done;\r
+       CAL_HuffExpand (source,audiosegs[chunk],expanded,audiohuffman);\r
+\r
+done:\r
+       if (compressed>BUFFERSIZE)\r
+               MM_FreePtr(&bigbufferseg);\r
+#endif\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CA_LoadAllSounds\r
+=\r
+= Purges all sounds, then loads all new ones (mode switch)\r
+=\r
+======================\r
+*/\r
+\r
+void CA_LoadAllSounds (void)\r
+{\r
+       unsigned        start,i;\r
+\r
+       switch (oldsoundmode)\r
+       {\r
+       case sdm_Off:\r
+               goto cachein;\r
+       case sdm_PC:\r
+               start = STARTPCSOUNDS;\r
+               break;\r
+       case sdm_AdLib:\r
+               start = STARTADLIBSOUNDS;\r
+               break;\r
+       }\r
+\r
+       for (i=0;i<NUMSOUNDS;i++,start++)\r
+               if (audiosegs[start])\r
+                       MM_SetPurge (&(memptr)audiosegs[start],3);              // make purgable\r
+\r
+cachein:\r
+\r
+       switch (SoundMode)\r
+       {\r
+       case sdm_Off:\r
+               return;\r
+       case sdm_PC:\r
+               start = STARTPCSOUNDS;\r
+               break;\r
+       case sdm_AdLib:\r
+               start = STARTADLIBSOUNDS;\r
+               break;\r
+       }\r
+\r
+       for (i=0;i<NUMSOUNDS;i++,start++)\r
+               CA_CacheAudioChunk (start);\r
+\r
+       oldsoundmode = SoundMode;\r
+}\r
+\r
+//===========================================================================\r
+\r
+#if GRMODE == EGAGR\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_ShiftSprite\r
+=\r
+= Make a shifted (one byte wider) copy of a sprite into another area\r
+=\r
+======================\r
+*/\r
+\r
+unsigned       static  sheight,swidth;\r
+boolean static dothemask;\r
+\r
+void CAL_ShiftSprite (unsigned segment,unsigned source,unsigned dest,\r
+       unsigned width, unsigned height, unsigned pixshift, boolean domask)\r
+{\r
+\r
+       sheight = height;               // because we are going to reassign bp\r
+       swidth = width;\r
+       dothemask = domask;\r
+\r
+asm    mov     ax,[segment]\r
+asm    mov     ds,ax           // source and dest are in same segment, and all local\r
+\r
+asm    mov     bx,[source]\r
+asm    mov     di,[dest]\r
+\r
+asm    mov     bp,[pixshift]\r
+asm    shl     bp,1\r
+asm    mov     bp,WORD PTR [shifttabletable+bp]        // bp holds pointer to shift table\r
+\r
+asm    cmp     [ss:dothemask],0\r
+asm    je              skipmask\r
+\r
+//\r
+// table shift the mask\r
+//\r
+asm    mov     dx,[ss:sheight]\r
+\r
+domaskrow:\r
+\r
+asm    mov     BYTE PTR [di],255       // 0xff first byte\r
+asm    mov     cx,ss:[swidth]\r
+\r
+domaskbyte:\r
+\r
+asm    mov     al,[bx]                         // source\r
+asm    not     al\r
+asm    inc     bx                                      // next source byte\r
+asm    xor     ah,ah\r
+asm    shl     ax,1\r
+asm    mov     si,ax\r
+asm    mov     ax,[bp+si]                      // table shift into two bytes\r
+asm    not     ax\r
+asm    and     [di],al                         // and with first byte\r
+asm    inc     di\r
+asm    mov     [di],ah                         // replace next byte\r
+\r
+asm    loop    domaskbyte\r
+\r
+asm    inc     di                                      // the last shifted byte has 1s in it\r
+asm    dec     dx\r
+asm    jnz     domaskrow\r
+\r
+skipmask:\r
+\r
+//\r
+// table shift the data\r
+//\r
+asm    mov     dx,ss:[sheight]\r
+asm    shl     dx,1\r
+asm    shl     dx,1                            // four planes of data\r
+\r
+dodatarow:\r
+\r
+asm    mov     BYTE PTR [di],0         // 0 first byte\r
+asm    mov     cx,ss:[swidth]\r
+\r
+dodatabyte:\r
+\r
+asm    mov     al,[bx]                         // source\r
+asm    inc     bx                                      // next source byte\r
+asm    xor     ah,ah\r
+asm    shl     ax,1\r
+asm    mov     si,ax\r
+asm    mov     ax,[bp+si]                      // table shift into two bytes\r
+asm    or      [di],al                         // or with first byte\r
+asm    inc     di\r
+asm    mov     [di],ah                         // replace next byte\r
+\r
+asm    loop    dodatabyte\r
+\r
+asm    inc     di                                      // the last shifted byte has 0s in it\r
+asm    dec     dx\r
+asm    jnz     dodatarow\r
+\r
+//\r
+// done\r
+//\r
+\r
+asm    mov     ax,ss                           // restore data segment\r
+asm    mov     ds,ax\r
+\r
+}\r
+\r
+#endif\r
+\r
+//===========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_CacheSprite\r
+=\r
+= Generate shifts and set up sprite structure for a given sprite\r
+=\r
+======================\r
+*/\r
+\r
+void CAL_CacheSprite (int chunk, byte far *compressed)\r
+{\r
+       int i;\r
+       unsigned shiftstarts[5];\r
+       unsigned smallplane,bigplane,expanded;\r
+       spritetabletype far *spr;\r
+       spritetype _seg *dest;\r
+\r
+#if GRMODE == CGAGR\r
+//\r
+// CGA has no pel panning, so shifts are never needed\r
+//\r
+       spr = &spritetable[chunk-STARTSPRITES];\r
+       smallplane = spr->width*spr->height;\r
+       MM_GetPtr (&grsegs[chunk],smallplane*2+MAXSHIFTS*6);\r
+       if (mmerror)\r
+               return;\r
+       dest = (spritetype _seg *)grsegs[chunk];\r
+       dest->sourceoffset[0] = MAXSHIFTS*6;    // start data after 3 unsigned tables\r
+       dest->planesize[0] = smallplane;\r
+       dest->width[0] = spr->width;\r
+\r
+//\r
+// expand the unshifted shape\r
+//\r
+       CAL_HuffExpand (compressed, &dest->data[0],smallplane*2,grhuffman);\r
+\r
+#endif\r
+\r
+\r
+#if GRMODE == EGAGR\r
+\r
+//\r
+// calculate sizes\r
+//\r
+       spr = &spritetable[chunk-STARTSPRITES];\r
+       smallplane = spr->width*spr->height;\r
+       bigplane = (spr->width+1)*spr->height;\r
+\r
+       shiftstarts[0] = MAXSHIFTS*6;   // start data after 3 unsigned tables\r
+       shiftstarts[1] = shiftstarts[0] + smallplane*5; // 5 planes in a sprite\r
+       shiftstarts[2] = shiftstarts[1] + bigplane*5;\r
+       shiftstarts[3] = shiftstarts[2] + bigplane*5;\r
+       shiftstarts[4] = shiftstarts[3] + bigplane*5;   // nothing ever put here\r
+\r
+       expanded = shiftstarts[spr->shifts];\r
+       MM_GetPtr (&grsegs[chunk],expanded);\r
+       if (mmerror)\r
+               return;\r
+       dest = (spritetype _seg *)grsegs[chunk];\r
+\r
+//\r
+// expand the unshifted shape\r
+//\r
+       CAL_HuffExpand (compressed, &dest->data[0],smallplane*5,grhuffman);\r
+\r
+//\r
+// make the shifts!\r
+//\r
+       switch (spr->shifts)\r
+       {\r
+       case    1:\r
+               for (i=0;i<4;i++)\r
+               {\r
+                       dest->sourceoffset[i] = shiftstarts[0];\r
+                       dest->planesize[i] = smallplane;\r
+                       dest->width[i] = spr->width;\r
+               }\r
+               break;\r
+\r
+       case    2:\r
+               for (i=0;i<2;i++)\r
+               {\r
+                       dest->sourceoffset[i] = shiftstarts[0];\r
+                       dest->planesize[i] = smallplane;\r
+                       dest->width[i] = spr->width;\r
+               }\r
+               for (i=2;i<4;i++)\r
+               {\r
+                       dest->sourceoffset[i] = shiftstarts[1];\r
+                       dest->planesize[i] = bigplane;\r
+                       dest->width[i] = spr->width+1;\r
+               }\r
+               CAL_ShiftSprite ((unsigned)grsegs[chunk],dest->sourceoffset[0],\r
+                       dest->sourceoffset[2],spr->width,spr->height,4,true);\r
+               break;\r
+\r
+       case    4:\r
+               dest->sourceoffset[0] = shiftstarts[0];\r
+               dest->planesize[0] = smallplane;\r
+               dest->width[0] = spr->width;\r
+\r
+               dest->sourceoffset[1] = shiftstarts[1];\r
+               dest->planesize[1] = bigplane;\r
+               dest->width[1] = spr->width+1;\r
+               CAL_ShiftSprite ((unsigned)grsegs[chunk],dest->sourceoffset[0],\r
+                       dest->sourceoffset[1],spr->width,spr->height,2,true);\r
+\r
+               dest->sourceoffset[2] = shiftstarts[2];\r
+               dest->planesize[2] = bigplane;\r
+               dest->width[2] = spr->width+1;\r
+               CAL_ShiftSprite ((unsigned)grsegs[chunk],dest->sourceoffset[0],\r
+                       dest->sourceoffset[2],spr->width,spr->height,4,true);\r
+\r
+               dest->sourceoffset[3] = shiftstarts[3];\r
+               dest->planesize[3] = bigplane;\r
+               dest->width[3] = spr->width+1;\r
+               CAL_ShiftSprite ((unsigned)grsegs[chunk],dest->sourceoffset[0],\r
+                       dest->sourceoffset[3],spr->width,spr->height,6,true);\r
+\r
+               break;\r
+\r
+       default:\r
+               Quit ("CAL_CacheSprite: Bad shifts number!");\r
+       }\r
+\r
+#endif\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_ExpandGrChunk\r
+=\r
+= Does whatever is needed with a pointer to a compressed chunk\r
+=\r
+======================\r
+*/\r
+\r
+void CAL_ExpandGrChunk (int chunk, byte far *source)\r
+{\r
+       long    expanded;\r
+\r
+\r
+       if (chunk >= STARTTILE8 && chunk < STARTEXTERNS)\r
+       {\r
+       //\r
+       // expanded sizes of tile8/16/32 are implicit\r
+       //\r
+\r
+#if GRMODE == EGAGR\r
+#define BLOCK          32\r
+#define MASKBLOCK      40\r
+#endif\r
+\r
+#if GRMODE == CGAGR\r
+#define BLOCK          16\r
+#define MASKBLOCK      32\r
+#endif\r
+\r
+               if (chunk<STARTTILE8M)                  // tile 8s are all in one chunk!\r
+                       expanded = BLOCK*NUMTILE8;\r
+               else if (chunk<STARTTILE16)\r
+                       expanded = MASKBLOCK*NUMTILE8M;\r
+               else if (chunk<STARTTILE16M)    // all other tiles are one/chunk\r
+                       expanded = BLOCK*4;\r
+               else if (chunk<STARTTILE32)\r
+                       expanded = MASKBLOCK*4;\r
+               else if (chunk<STARTTILE32M)\r
+                       expanded = BLOCK*16;\r
+               else\r
+                       expanded = MASKBLOCK*16;\r
+       }\r
+       else\r
+       {\r
+       //\r
+       // everything else has an explicit size longword\r
+       //\r
+               expanded = *(long far *)source;\r
+               source += 4;                    // skip over length\r
+       }\r
+\r
+//\r
+// allocate final space, decompress it, and free bigbuffer\r
+// Sprites need to have shifts made and various other junk\r
+//\r
+       if (chunk>=STARTSPRITES && chunk< STARTTILE8)\r
+               CAL_CacheSprite(chunk,source);\r
+       else\r
+       {\r
+               MM_GetPtr (&grsegs[chunk],expanded);\r
+               if (mmerror)\r
+                       return;\r
+               CAL_HuffExpand (source,grsegs[chunk],expanded,grhuffman);\r
+       }\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_ReadGrChunk\r
+=\r
+= Gets a chunk off disk, optimizing reads to general buffer\r
+=\r
+======================\r
+*/\r
+\r
+void CAL_ReadGrChunk (int chunk)\r
+{\r
+       long    pos,compressed;\r
+       memptr  bigbufferseg;\r
+       byte    far *source;\r
+       int             next;\r
+\r
+//\r
+// load the chunk into a buffer, either the miscbuffer if it fits, or allocate\r
+// a larger buffer\r
+//\r
+       pos = GRFILEPOS(chunk);\r
+       if (pos<0)                                                      // $FFFFFFFF start is a sparse tile\r
+         return;\r
+\r
+       next = chunk +1;\r
+       while (GRFILEPOS(next) == -1)           // skip past any sparse tiles\r
+               next++;\r
+\r
+       compressed = GRFILEPOS(next)-pos;\r
+\r
+       lseek(grhandle,pos,SEEK_SET);\r
+\r
+       if (compressed<=BUFFERSIZE)\r
+       {\r
+               CA_FarRead(grhandle,bufferseg,compressed);\r
+               source = bufferseg;\r
+       }\r
+       else\r
+       {\r
+               MM_GetPtr(&bigbufferseg,compressed);\r
+               if (mmerror)\r
+                       return;\r
+               MM_SetLock (&bigbufferseg,true);\r
+               CA_FarRead(grhandle,bigbufferseg,compressed);\r
+               source = bigbufferseg;\r
+       }\r
+\r
+       CAL_ExpandGrChunk (chunk,source);\r
+\r
+       if (compressed>BUFFERSIZE)\r
+               MM_FreePtr(&bigbufferseg);\r
+}\r
+\r
+/*\r
+======================\r
+=\r
+= CA_CacheGrChunk\r
+=\r
+= Makes sure a given chunk is in memory, loadiing it if needed\r
+=\r
+======================\r
+*/\r
+\r
+void CA_CacheGrChunk (int chunk)\r
+{\r
+       long    pos,compressed;\r
+       memptr  bigbufferseg;\r
+       byte    far *source;\r
+       int             next;\r
+\r
+       grneeded[chunk] |= ca_levelbit;         // make sure it doesn't get removed\r
+       if (grsegs[chunk])\r
+       {\r
+               MM_SetPurge (&grsegs[chunk],0);\r
+               return;                                                 // allready in memory\r
+       }\r
+\r
+// MDM begin - (GAMERS EDGE)\r
+//\r
+       if (!FindFile("EGAGRAPH."EXT,NULL,2))\r
+               Quit("CA_CacheGrChunk(): Can't find graphics files.");\r
+//\r
+// MDM end\r
+\r
+//\r
+// load the chunk into a buffer, either the miscbuffer if it fits, or allocate\r
+// a larger buffer\r
+//\r
+       pos = GRFILEPOS(chunk);\r
+       if (pos<0)                                                      // $FFFFFFFF start is a sparse tile\r
+         return;\r
+\r
+       next = chunk +1;\r
+       while (GRFILEPOS(next) == -1)           // skip past any sparse tiles\r
+               next++;\r
+\r
+       compressed = GRFILEPOS(next)-pos;\r
+\r
+       lseek(grhandle,pos,SEEK_SET);\r
+\r
+       if (compressed<=BUFFERSIZE)\r
+       {\r
+               CA_FarRead(grhandle,bufferseg,compressed);\r
+               source = bufferseg;\r
+       }\r
+       else\r
+       {\r
+               MM_GetPtr(&bigbufferseg,compressed);\r
+               MM_SetLock (&bigbufferseg,true);\r
+               CA_FarRead(grhandle,bigbufferseg,compressed);\r
+               source = bigbufferseg;\r
+       }\r
+\r
+       CAL_ExpandGrChunk (chunk,source);\r
+\r
+       if (compressed>BUFFERSIZE)\r
+               MM_FreePtr(&bigbufferseg);\r
+}\r
+\r
+\r
+\r
+//==========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CA_CacheMap\r
+=\r
+======================\r
+*/\r
+\r
+void CA_CacheMap (int mapnum)\r
+{\r
+       long    pos,compressed;\r
+       int             plane;\r
+       memptr  *dest,bigbufferseg;\r
+       unsigned        size;\r
+       unsigned        far     *source;\r
+#ifdef MAPHEADERLINKED\r
+       memptr  buffer2seg;\r
+       long    expanded;\r
+#endif\r
+\r
+\r
+// MDM begin - (GAMERS EDGE)\r
+//\r
+       if (!FindFile("GAMEMAPS."EXT,NULL,1))\r
+               Quit("CA_CacheMap(): Can't find level files.");\r
+//\r
+// MDM end\r
+\r
+\r
+//\r
+// free up memory from last map\r
+//\r
+       if (mapon>-1 && mapheaderseg[mapon])\r
+               MM_SetPurge (&(memptr)mapheaderseg[mapon],3);\r
+       for (plane=0;plane<MAPPLANES;plane++)\r
+               if (mapsegs[plane])\r
+                       MM_FreePtr (&(memptr)mapsegs[plane]);\r
+\r
+       mapon = mapnum;\r
+\r
+\r
+//\r
+// load map header\r
+// The header will be cached if it is still around\r
+//\r
+       if (!mapheaderseg[mapnum])\r
+       {\r
+               pos = ((mapfiletype     _seg *)tinf)->headeroffsets[mapnum];\r
+               if (pos<0)                                              // $FFFFFFFF start is a sparse map\r
+                 Quit ("CA_CacheMap: Tried to load a non existent map!");\r
+\r
+               MM_GetPtr(&(memptr)mapheaderseg[mapnum],sizeof(maptype));\r
+               lseek(maphandle,pos,SEEK_SET);\r
+               CA_FarRead (maphandle,(memptr)mapheaderseg[mapnum],sizeof(maptype));\r
+       }\r
+       else\r
+               MM_SetPurge (&(memptr)mapheaderseg[mapnum],0);\r
+\r
+//\r
+// load the planes in\r
+// If a plane's pointer still exists it will be overwritten (levels are\r
+// allways reloaded, never cached)\r
+//\r
+\r
+       size = mapheaderseg[mapnum]->width * mapheaderseg[mapnum]->height * 2;\r
+\r
+       for (plane = 0; plane<MAPPLANES; plane++)\r
+       {\r
+               pos = mapheaderseg[mapnum]->planestart[plane];\r
+               compressed = mapheaderseg[mapnum]->planelength[plane];\r
+\r
+               if (!compressed)\r
+                       continue;               // the plane is not used in this game\r
+\r
+               dest = &(memptr)mapsegs[plane];\r
+               MM_GetPtr(dest,size);\r
+\r
+               lseek(maphandle,pos,SEEK_SET);\r
+               if (compressed<=BUFFERSIZE)\r
+                       source = bufferseg;\r
+               else\r
+               {\r
+                       MM_GetPtr(&bigbufferseg,compressed);\r
+                       MM_SetLock (&bigbufferseg,true);\r
+                       source = bigbufferseg;\r
+               }\r
+\r
+               CA_FarRead(maphandle,(byte far *)source,compressed);\r
+#ifdef MAPHEADERLINKED\r
+               //\r
+               // unhuffman, then unRLEW\r
+               // The huffman'd chunk has a two byte expanded length first\r
+               // The resulting RLEW chunk also does, even though it's not really\r
+               // needed\r
+               //\r
+               expanded = *source;\r
+               source++;\r
+               MM_GetPtr (&buffer2seg,expanded);\r
+               CAL_CarmackExpand (source, (unsigned far *)buffer2seg,expanded);\r
+               CA_RLEWexpand (((unsigned far *)buffer2seg)+1,*dest,size,\r
+               ((mapfiletype _seg *)tinf)->RLEWtag);\r
+               MM_FreePtr (&buffer2seg);\r
+\r
+#else\r
+               //\r
+               // unRLEW, skipping expanded length\r
+               //\r
+               CA_RLEWexpand (source+1, *dest,size,\r
+               ((mapfiletype _seg *)tinf)->RLEWtag);\r
+#endif\r
+\r
+               if (compressed>BUFFERSIZE)\r
+                       MM_FreePtr(&bigbufferseg);\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CA_UpLevel\r
+=\r
+= Goes up a bit level in the needed lists and clears it out.\r
+= Everything is made purgable\r
+=\r
+======================\r
+*/\r
+\r
+void CA_UpLevel (void)\r
+{\r
+       if (ca_levelnum==7)\r
+               Quit ("CA_UpLevel: Up past level 7!");\r
+\r
+       ca_levelbit<<=1;\r
+       ca_levelnum++;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CA_DownLevel\r
+=\r
+= Goes down a bit level in the needed lists and recaches\r
+= everything from the lower level\r
+=\r
+======================\r
+*/\r
+\r
+void CA_DownLevel (void)\r
+{\r
+       if (!ca_levelnum)\r
+               Quit ("CA_DownLevel: Down past level 0!");\r
+       ca_levelbit>>=1;\r
+       ca_levelnum--;\r
+       CA_CacheMarks(NULL);\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CA_ClearMarks\r
+=\r
+= Clears out all the marks at the current level\r
+=\r
+======================\r
+*/\r
+\r
+void CA_ClearMarks (void)\r
+{\r
+       int i;\r
+\r
+       for (i=0;i<NUMCHUNKS;i++)\r
+               grneeded[i]&=~ca_levelbit;\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CA_ClearAllMarks\r
+=\r
+= Clears out all the marks on all the levels\r
+=\r
+======================\r
+*/\r
+\r
+void CA_ClearAllMarks (void)\r
+{\r
+       _fmemset (grneeded,0,sizeof(grneeded));\r
+       ca_levelbit = 1;\r
+       ca_levelnum = 0;\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CA_FreeGraphics\r
+=\r
+======================\r
+*/\r
+\r
+void CA_FreeGraphics (void)\r
+{\r
+       int     i;\r
+\r
+       for (i=0;i<NUMCHUNKS;i++)\r
+               if (grsegs[i])\r
+                       MM_SetPurge (&(memptr)grsegs[i],3);\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CA_SetAllPurge\r
+=\r
+= Make everything possible purgable\r
+=\r
+======================\r
+*/\r
+\r
+void CA_SetAllPurge (void)\r
+{\r
+       int i;\r
+\r
+       CA_ClearMarks ();\r
+\r
+//\r
+// free cursor sprite and background save\r
+//\r
+       VW_FreeCursor ();\r
+\r
+//\r
+// free map headers and map planes\r
+//\r
+       for (i=0;i<NUMMAPS;i++)\r
+               if (mapheaderseg[i])\r
+                       MM_SetPurge (&(memptr)mapheaderseg[i],3);\r
+\r
+       for (i=0;i<3;i++)\r
+               if (mapsegs[i])\r
+                       MM_FreePtr (&(memptr)mapsegs[i]);\r
+\r
+//\r
+// free sounds\r
+//\r
+       for (i=0;i<NUMSNDCHUNKS;i++)\r
+               if (audiosegs[i])\r
+                       MM_SetPurge (&(memptr)audiosegs[i],3);\r
+\r
+//\r
+// free graphics\r
+//\r
+       CA_FreeGraphics ();\r
+}\r
+\r
+\r
+void CA_SetGrPurge (void)\r
+{\r
+       int i;\r
+\r
+//\r
+// free graphics\r
+//\r
+       for (i=0;i<NUMCHUNKS;i++)\r
+               if (grsegs[i])\r
+                       MM_SetPurge (&(memptr)grsegs[i],3);\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_DialogDraw\r
+=\r
+======================\r
+*/\r
+\r
+#define NUMBARS        (17l*8)\r
+#define BARSTEP        8\r
+\r
+unsigned       thx,thy,lastx;\r
+long           barx,barstep;\r
+\r
+void   CAL_DialogDraw (char *title,unsigned numcache)\r
+{\r
+       unsigned        homex,homey,x;\r
+\r
+       barstep = (NUMBARS<<16)/numcache;\r
+\r
+//\r
+// draw dialog window (masked tiles 12 - 20 are window borders)\r
+//\r
+       US_CenterWindow (20,8);\r
+       homex = PrintX;\r
+       homey = PrintY;\r
+\r
+       US_CPrint ("Loading");\r
+       fontcolor = F_SECONDCOLOR;\r
+       US_CPrint (title);\r
+       fontcolor = F_BLACK;\r
+\r
+//\r
+// draw thermometer bar\r
+//\r
+       thx = homex + 8;\r
+       thy = homey + 32;\r
+       VWB_DrawTile8(thx,thy,0);               // CAT3D numbers\r
+       VWB_DrawTile8(thx,thy+8,3);\r
+       VWB_DrawTile8(thx,thy+16,6);\r
+       VWB_DrawTile8(thx+17*8,thy,2);\r
+       VWB_DrawTile8(thx+17*8,thy+8,5);\r
+       VWB_DrawTile8(thx+17*8,thy+16,8);\r
+       for (x=thx+8;x<thx+17*8;x+=8)\r
+       {\r
+               VWB_DrawTile8(x,thy,1);\r
+               VWB_DrawTile8(x,thy+8,4);\r
+               VWB_DrawTile8(x,thy+16,7);\r
+       }\r
+\r
+       thx += 4;               // first line location\r
+       thy += 5;\r
+       barx = (long)thx<<16;\r
+       lastx = thx;\r
+\r
+       VW_UpdateScreen();\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_DialogUpdate\r
+=\r
+======================\r
+*/\r
+\r
+void   CAL_DialogUpdate (void)\r
+{\r
+       unsigned        x,xh;\r
+\r
+       barx+=barstep;\r
+       xh = barx>>16;\r
+       if (xh - lastx > BARSTEP)\r
+       {\r
+               for (x=lastx;x<=xh;x++)\r
+#if GRMODE == EGAGR\r
+                       VWB_Vlin (thy,thy+13,x,14);\r
+#endif\r
+#if GRMODE == CGAGR\r
+                       VWB_Vlin (thy,thy+13,x,SECONDCOLOR);\r
+#endif\r
+               lastx = xh;\r
+               VW_UpdateScreen();\r
+       }\r
+}\r
+\r
+/*\r
+======================\r
+=\r
+= CAL_DialogFinish\r
+=\r
+======================\r
+*/\r
+\r
+void   CAL_DialogFinish (void)\r
+{\r
+       unsigned        x,xh;\r
+\r
+       xh = thx + NUMBARS;\r
+       for (x=lastx;x<=xh;x++)\r
+#if GRMODE == EGAGR\r
+               VWB_Vlin (thy,thy+13,x,14);\r
+#endif\r
+#if GRMODE == CGAGR\r
+               VWB_Vlin (thy,thy+13,x,SECONDCOLOR);\r
+#endif\r
+       VW_UpdateScreen();\r
+\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= CA_CacheMarks\r
+=\r
+======================\r
+*/\r
+#define MAXEMPTYREAD   1024\r
+\r
+void CA_CacheMarks (char *title)\r
+{\r
+       boolean dialog;\r
+       int     i,next,numcache;\r
+       long    pos,endpos,nextpos,nextendpos,compressed;\r
+       long    bufferstart,bufferend;  // file position of general buffer\r
+       byte    far *source;\r
+       memptr  bigbufferseg;\r
+\r
+       dialog = (title!=NULL);\r
+\r
+       numcache = 0;\r
+//\r
+// go through and make everything not needed purgable\r
+//\r
+       for (i=0;i<NUMCHUNKS;i++)\r
+               if (grneeded[i]&ca_levelbit)\r
+               {\r
+                       if (grsegs[i])                                  // its allready in memory, make\r
+                               MM_SetPurge(&grsegs[i],0);      // sure it stays there!\r
+                       else\r
+                               numcache++;\r
+               }\r
+               else\r
+               {\r
+                       if (grsegs[i])                                  // not needed, so make it purgeable\r
+                               MM_SetPurge(&grsegs[i],3);\r
+               }\r
+\r
+       if (!numcache)                  // nothing to cache!\r
+               return;\r
+\r
+// MDM begin - (GAMERS EDGE)\r
+//\r
+       if (!FindFile("EGAGRAPH."EXT,NULL,2))\r
+               Quit("CA_CacheMarks(): Can't find graphics files.");\r
+//\r
+// MDM end\r
+\r
+       if (dialog)\r
+       {\r
+#ifdef PROFILE\r
+               write(profilehandle,title,strlen(title));\r
+               write(profilehandle,"\n",1);\r
+#endif\r
+               if (drawcachebox)\r
+                       drawcachebox(title,numcache);\r
+       }\r
+\r
+//\r
+// go through and load in anything still needed\r
+//\r
+       bufferstart = bufferend = 0;            // nothing good in buffer now\r
+\r
+       for (i=0;i<NUMCHUNKS;i++)\r
+               if ( (grneeded[i]&ca_levelbit) && !grsegs[i])\r
+               {\r
+//\r
+// update thermometer\r
+//\r
+                       if (dialog && updatecachebox)\r
+                               updatecachebox ();\r
+\r
+                       pos = GRFILEPOS(i);\r
+                       if (pos<0)\r
+                               continue;\r
+\r
+                       next = i +1;\r
+                       while (GRFILEPOS(next) == -1)           // skip past any sparse tiles\r
+                               next++;\r
+\r
+                       compressed = GRFILEPOS(next)-pos;\r
+                       endpos = pos+compressed;\r
+\r
+                       if (compressed<=BUFFERSIZE)\r
+                       {\r
+                               if (bufferstart<=pos\r
+                               && bufferend>= endpos)\r
+                               {\r
+                               // data is allready in buffer\r
+                                       source = (byte _seg *)bufferseg+(pos-bufferstart);\r
+                               }\r
+                               else\r
+                               {\r
+                               // load buffer with a new block from disk\r
+                               // try to get as many of the needed blocks in as possible\r
+                                       while ( next < NUMCHUNKS )\r
+                                       {\r
+                                               while (next < NUMCHUNKS &&\r
+                                               !(grneeded[next]&ca_levelbit && !grsegs[next]))\r
+                                                       next++;\r
+                                               if (next == NUMCHUNKS)\r
+                                                       continue;\r
+\r
+                                               nextpos = GRFILEPOS(next);\r
+                                               while (GRFILEPOS(++next) == -1) // skip past any sparse tiles\r
+                                                       ;\r
+                                               nextendpos = GRFILEPOS(next);\r
+                                               if (nextpos - endpos <= MAXEMPTYREAD\r
+                                               && nextendpos-pos <= BUFFERSIZE)\r
+                                                       endpos = nextendpos;\r
+                                               else\r
+                                                       next = NUMCHUNKS;                       // read pos to posend\r
+                                       }\r
+\r
+                                       lseek(grhandle,pos,SEEK_SET);\r
+                                       CA_FarRead(grhandle,bufferseg,endpos-pos);\r
+                                       bufferstart = pos;\r
+                                       bufferend = endpos;\r
+                                       source = bufferseg;\r
+                               }\r
+                       }\r
+                       else\r
+                       {\r
+                       // big chunk, allocate temporary buffer\r
+                               MM_GetPtr(&bigbufferseg,compressed);\r
+                               if (mmerror)\r
+                                       return;\r
+                               MM_SetLock (&bigbufferseg,true);\r
+                               lseek(grhandle,pos,SEEK_SET);\r
+                               CA_FarRead(grhandle,bigbufferseg,compressed);\r
+                               source = bigbufferseg;\r
+                       }\r
+\r
+                       CAL_ExpandGrChunk (i,source);\r
+                       if (mmerror)\r
+                               return;\r
+\r
+                       if (compressed>BUFFERSIZE)\r
+                               MM_FreePtr(&bigbufferseg);\r
+\r
+               }\r
+\r
+//\r
+// finish up any thermometer remnants\r
+//\r
+               if (dialog && finishcachebox)\r
+                       finishcachebox();\r
+}\r
+\r
diff --git a/16/cawat/ID_CA.H b/16/cawat/ID_CA.H
new file mode 100644 (file)
index 0000000..c2ff0ab
--- /dev/null
@@ -0,0 +1,123 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_CA.H
+
+/*#ifndef __TYPES__\r
+#include "ID_TYPES.H"\r
+#endif\r
+\r
+#ifndef __ID_MM__\r
+#include "ID_MM.H"\r
+#endif\r
+\r
+#ifndef __ID_GLOB__\r
+#include "ID_GLOB.H"\r
+#endif*/\r
+\r
+#define __ID_CA__\r
+\r
+//===========================================================================\r
+\r
+//#define NOMAPS\r
+//#define NOGRAPHICS\r
+//#define NOAUDIO\r
+\r
+#define MAPHEADERLINKED\r
+#define GRHEADERLINKED\r
+#define AUDIOHEADERLINKED\r
+\r
+#define NUMMAPS                39\r
+#define MAPPLANES              3\r
+\r
+//===========================================================================
+\r
+typedef        struct\r
+{\r
+       long            planestart[3];\r
+       unsigned        planelength[3];\r
+       unsigned        width,height;\r
+       char            name[16];\r
+} maptype;\r
+\r
+//===========================================================================\r
+\r
+extern byte            /*_1seg*/       *tinf;\r
+extern int                     mapon;\r
+\r
+extern unsigned        /*_1seg*/       *mapsegs[3];\r
+extern maptype         /*_1seg*/       *mapheaderseg[NUMMAPS];\r
+extern byte            /*_1seg*/       *audiosegs[NUMSNDCHUNKS];\r
+extern void            /*_1seg*/       *grsegs[NUMCHUNKS];\r
+\r
+extern byte            far     grneeded[NUMCHUNKS];\r
+extern byte            ca_levelbit,ca_levelnum;\r
+\r
+extern char            *titleptr[8];\r
+\r
+extern int                     profilehandle,debughandle;\r
+\r
+//\r
+// hooks for custom cache dialogs\r
+//\r
+extern void    (*drawcachebox)         (char *title, unsigned numcache);\r
+extern void    (*updatecachebox)       (void);\r
+extern void    (*finishcachebox)       (void);\r
+\r
+//===========================================================================\r
+\r
+// just for the score box reshifting\r
+\r
+void CAL_ShiftSprite (unsigned segment,unsigned source,unsigned dest,\r
+       unsigned width, unsigned height, unsigned pixshift, boolean domask);\r
+\r
+//===========================================================================\r
+\r
+void CA_OpenDebug (void);\r
+void CA_CloseDebug (void);\r
+boolean CA_FarRead (int handle, byte far *dest, long length);\r
+boolean CA_FarWrite (int handle, byte far *source, long length);\r
+boolean CA_ReadFile (char *filename, memptr *ptr);\r
+boolean CA_LoadFile (char *filename, memptr *ptr);\r
+\r
+long CA_RLEWCompress (unsigned huge *source, long length, unsigned huge *dest,\r
+  unsigned rlewtag);\r
+\r
+void CA_RLEWexpand (unsigned huge *source, unsigned huge *dest,long length,\r
+  unsigned rlewtag);\r
+\r
+void CA_Startup (void);\r
+void CA_Shutdown (void);\r
+\r
+void CA_CacheAudioChunk (int chunk);\r
+void CA_LoadAllSounds (void);\r
+\r
+void CA_UpLevel (void);\r
+void CA_DownLevel (void);\r
+\r
+void CA_SetAllPurge (void);\r
+\r
+void CA_ClearMarks (void);\r
+void CA_ClearAllMarks (void);\r
+\r
+#define CA_MarkGrChunk(chunk)  grneeded[chunk]|=ca_levelbit\r
+\r
+void CA_CacheGrChunk (int chunk);\r
+void CA_CacheMap (int mapnum);\r
+\r
+void CA_CacheMarks (char *title);\r
diff --git a/16/cawat/ID_HEADS.H b/16/cawat/ID_HEADS.H
new file mode 100644 (file)
index 0000000..88f86d3
--- /dev/null
@@ -0,0 +1,146 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_GLOB.H\r
+\r
+\r
+#include <ALLOC.H>\r
+#include <CTYPE.H>\r
+#include <DOS.H>\r
+#include <ERRNO.H>\r
+#include <FCNTL.H>\r
+#include <IO.H>\r
+#include <MEM.H>\r
+#include <PROCESS.H>\r
+#include <STDIO.H>\r
+#include <STDLIB.H>\r
+#include <STRING.H>\r
+#include <SYS\STAT.H>\r
+\r
+#define __ID_GLOB__\r
+\r
+//--------------------------------------------------------------------------\r
+\r
+#define        EXT     "ARM"\r
+\r
+extern char far introscn;\r
+\r
+#include "GFXE_ARM.H"\r
+#include "AUDIOARM.H"\r
+#include "MAPSARM.H"\r
+\r
+//--------------------------------------------------------------------------\r
+\r
+//\r
+// DEFINES THE TILE ATTRIBUTE CHECKING CONVENTION (macros).\r
+//\r
+// DEFINE CUSTOM BIT-FLAG NAMES...\r
+//\r
+\r
+\r
+#define tf_SOLID                                                       0x01\r
+#define tf_SPECIAL                                             0x02\r
+#define tf_EMBEDDED_KEY_COLOR                  0x04\r
+#define tf_INVISIBLE_WALL                      0x09\r
+#define tf_MARKED                      0x80\r
+\r
+#define ANIM_FLAGS(tile)       (tinf[ANIM+(tile)])\r
+#define TILE_FLAGS(tile)   (tinf[FLAGS+(tile)])\r
+\r
+#define GATE_KEY_COLOR(tile)           ((unsigned char)(TILE_FLAGS(tile)>>4))\r
+\r
+#define CAT3D\r
+\r
+#define        TEXTGR  0\r
+#define        CGAGR   1\r
+#define        EGAGR   2\r
+#define        VGAGR   3\r
+\r
+#define  EGA320GR      10                                      // MDM (GAMERS EDGE)\r
+#define  EGA640GR      11                                      // MDM (GAMERS EDGE)\r
+\r
+#define GRMODE EGAGR\r
+\r
+#if GRMODE == EGAGR\r
+#define GREXT  "EGA"\r
+#endif\r
+#if GRMODE == CGAGR\r
+#define GREXT  "CGA"\r
+#endif\r
+\r
+//#define PROFILE\r
+\r
+//\r
+//     ID Engine\r
+//     Types.h - Generic types, #defines, etc.\r
+//     v1.0d1\r
+//\r
+\r
+#ifndef        __TYPES__\r
+#define        __TYPES__\r
+\r
+typedef        enum    {false,true}    boolean;\r
+typedef        unsigned        char            byte;\r
+typedef        unsigned        int                     word;\r
+typedef        unsigned        long            longword;\r
+typedef        byte *                                  Ptr;\r
+\r
+typedef        struct\r
+               {\r
+                       int     x,y;\r
+               } Point;\r
+typedef        struct\r
+               {\r
+                       Point   ul,lr;\r
+               } Rect;\r
+\r
+#define        nil     ((void *)0)\r
+\r
+#endif\r
+\r
+#include "ID_MM.H"\r
+#include "ID_CA.H"\r
+#include "ID_VW.H"\r
+#include "ID_IN.H"\r
+#include "ID_SD.H"\r
+#include "ID_US.H"\r
+\r
+\r
+void   Quit (char *error, ...);                // defined in user program\r
+\r
+//\r
+// replacing refresh manager with custom routines\r
+//\r
+\r
+#define        PORTTILESWIDE           21      // all drawing takes place inside a\r
+#define        PORTTILESHIGH           14              // non displayed port of this size\r
+\r
+#define UPDATEWIDE                     (PORTTILESWIDE+1)\r
+#define UPDATEHIGH                     PORTTILESHIGH\r
+\r
+#define        MAXTICS                         6\r
+#define DEMOTICS                       3\r
+\r
+#define        UPDATETERMINATE 0x0301\r
+\r
+extern unsigned        mapwidth,mapheight,tics,realtics;\r
+extern boolean         compatability;\r
+\r
+extern byte            *updateptr;\r
+extern unsigned        uwidthtable[UPDATEHIGH];\r
+extern unsigned        blockstarts[UPDATEWIDE*UPDATEHIGH];\r
diff --git a/16/cawat/ID_IN.C b/16/cawat/ID_IN.C
new file mode 100644 (file)
index 0000000..284a6fe
--- /dev/null
@@ -0,0 +1,1284 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+//     ID Engine\r
+//     ID_IN.c - Input Manager\r
+//     v1.0d1\r
+//     By Jason Blochowiak\r
+//\r
+\r
+//\r
+//     This module handles dealing with the various input devices\r
+//\r
+//     Depends on: Memory Mgr (for demo recording), Sound Mgr (for timing stuff),\r
+//                             User Mgr (for command line parms)\r
+//\r
+//     Globals:\r
+//             LastScan - The keyboard scan code of the last key pressed\r
+//             LastASCII - The ASCII value of the last key pressed\r
+//     DEBUG - there are more globals\r
+//\r
+\r
+//#include "ID_HEADS.H"
+#include "id_in.h"\r
+//#pragma      hdrstop\r
+\r
+#define        KeyInt  9       // The keyboard ISR number\r
+\r
+// Stuff for the joystick\r
+#define        JoyScaleMax             32768\r
+#define        JoyScaleShift   8\r
+#define        MaxJoyValue             5000\r
+\r
+//     Global variables\r
+               boolean JoystickCalibrated=false;               // MDM (GAMERS EDGE) - added\r
+               ControlType ControlTypeUsed;                            // MDM (GAMERS EDGE) - added\r
+\r
+               boolean         Keyboard[NumCodes],\r
+                                       JoysPresent[MaxJoys],\r
+                                       MousePresent;\r
+               boolean         Paused;\r
+               char            LastASCII;\r
+               ScanCode        LastScan;\r
+               KeyboardDef     KbdDefs[MaxKbds] = {{0x1d,0x38,0x47,0x48,0x49,0x4b,0x4d,0x4f,0x50,0x51}};\r
+               JoystickDef     JoyDefs[MaxJoys];\r
+               ControlType     Controls[MaxPlayers];\r
+\r
+//             Demo            DemoMode = demo_Off;\r
+//             byte /*_1seg*/  *DemoBuffer;\r
+//             word            DemoOffset,DemoSize;\r
+\r
+//     Internal variables\r
+static boolean         IN_Started;\r
+static boolean         CapsLock;\r
+static ScanCode        CurCode,LastCode;\r
+static byte        far ASCIINames[] =          // Unshifted ASCII for scan codes\r
+                                       {\r
+//      0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F\r
+       0  ,27 ,'1','2','3','4','5','6','7','8','9','0','-','=',8  ,9  ,        // 0\r
+       'q','w','e','r','t','y','u','i','o','p','[',']',13 ,0  ,'a','s',        // 1\r
+       'd','f','g','h','j','k','l',';',39 ,'`',0  ,92 ,'z','x','c','v',        // 2\r
+       'b','n','m',',','.','/',0  ,'*',0  ,' ',0  ,0  ,0  ,0  ,0  ,0  ,        // 3\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,'7','8','9','-','4','5','6','+','1',        // 4\r
+       '2','3','0',127,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 5\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 6\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0           // 7\r
+                                       },\r
+                                       far ShiftNames[] =              // Shifted ASCII for scan codes\r
+                                       {\r
+//      0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F\r
+       0  ,27 ,'!','@','#','$','%','^','&','*','(',')','_','+',8  ,9  ,        // 0\r
+       'Q','W','E','R','T','Y','U','I','O','P','{','}',13 ,0  ,'A','S',        // 1\r
+       'D','F','G','H','J','K','L',':',34 ,'~',0  ,'|','Z','X','C','V',        // 2\r
+       'B','N','M','<','>','?',0  ,'*',0  ,' ',0  ,0  ,0  ,0  ,0  ,0  ,        // 3\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,'7','8','9','-','4','5','6','+','1',        // 4\r
+       '2','3','0',127,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 5\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 6\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0           // 7\r
+                                       },\r
+                                       far SpecialNames[] =    // ASCII for 0xe0 prefixed codes\r
+                                       {\r
+//      0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 0\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,13 ,0  ,0  ,0  ,        // 1\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 2\r
+       0  ,0  ,0  ,0  ,0  ,'/',0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 3\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 4\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 5\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,        // 6\r
+       0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0  ,0           // 7\r
+                                       },\r
+\r
+#if 0\r
+                                       *ScanNames[] =          // Scan code names with single chars\r
+                                       {\r
+       "?","?","1","2","3","4","5","6","7","8","9","0","-","+","?","?",\r
+       "Q","W","E","R","T","Y","U","I","O","P","[","]","|","?","A","S",\r
+       "D","F","G","H","J","K","L",";","\"","?","?","?","Z","X","C","V",\r
+       "B","N","M",",",".","/","?","?","?","?","?","?","?","?","?","?",\r
+       "?","?","?","?","?","?","?","?","\xf","?","-","\x15","5","\x11","+","?",\r
+       "\x13","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?",\r
+       "?","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?",\r
+       "?","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?"\r
+                                       },      // DEBUG - consolidate these\r
+#endif\r
+\r
+                                       far ExtScanCodes[] =    // Scan codes with >1 char names\r
+                                       {\r
+       1,0xe,0xf,0x1d,0x2a,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,\r
+       0x3f,0x40,0x41,0x42,0x43,0x44,0x57,0x59,0x46,0x1c,0x36,\r
+       0x37,0x38,0x47,0x49,0x4f,0x51,0x52,0x53,0x45,0x48,\r
+       0x50,0x4b,0x4d,0x00\r
+                                       };\r
+#if 0\r
+                                       *ExtScanNames[] =       // Names corresponding to ExtScanCodes\r
+                                       {\r
+       "Esc","BkSp","Tab","Ctrl","LShft","Space","CapsLk","F1","F2","F3","F4",\r
+       "F5","F6","F7","F8","F9","F10","F11","F12","ScrlLk","Enter","RShft",\r
+       "PrtSc","Alt","Home","PgUp","End","PgDn","Ins","Del","NumLk","Up",\r
+       "Down","Left","Right",""\r
+                                       };\r
+#endif\r
+static Direction       DirTable[] =            // Quick lookup for total direction\r
+                                       {\r
+                                               dir_NorthWest,  dir_North,      dir_NorthEast,\r
+                                               dir_West,               dir_None,       dir_East,\r
+                                               dir_SouthWest,  dir_South,      dir_SouthEast\r
+                                       };\r
+\r
+static void                    (*INL_KeyHook)(void);\r
+static void interrupt  (*OldKeyVect)(void);\r
+\r
+static char                    *ParmStrings[] = {"nojoys","nomouse",nil};\r
+\r
+//     Internal routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_KeyService() - Handles a keyboard interrupt (key up/down)\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void interrupt\r
+INL_KeyService(void)\r
+{\r
+static boolean special;\r
+               byte    k,c,\r
+                               temp;\r
+\r
+       k = inportb(0x60);      // Get the scan code\r
+\r
+       // Tell the XT keyboard controller to clear the key\r
+       outportb(0x61,(temp = inportb(0x61)) | 0x80);\r
+       outportb(0x61,temp);\r
+\r
+       if (k == 0xe0)          // Special key prefix\r
+               special = true;\r
+       else if (k == 0xe1)     // Handle Pause key\r
+               Paused = true;\r
+       else\r
+       {\r
+               if (k & 0x80)   // Break code\r
+               {\r
+                       k &= 0x7f;\r
+\r
+// DEBUG - handle special keys: ctl-alt-delete, print scrn\r
+\r
+                       Keyboard[k] = false;\r
+               }\r
+               else                    // Make code\r
+               {\r
+                       LastCode = CurCode;\r
+                       CurCode = LastScan = k;\r
+                       Keyboard[k] = true;\r
+\r
+                       if (special)\r
+                               c = SpecialNames[k];\r
+                       else\r
+                       {\r
+                               if (k == sc_CapsLock)\r
+                               {\r
+                                       CapsLock ^= true;\r
+                                       // DEBUG - make caps lock light work\r
+                               }\r
+\r
+                               if (Keyboard[sc_LShift] || Keyboard[sc_RShift]) // If shifted\r
+                               {\r
+                                       c = ShiftNames[k];\r
+                                       if ((c >= 'A') && (c <= 'Z') && CapsLock)\r
+                                               c += 'a' - 'A';\r
+                               }\r
+                               else\r
+                               {\r
+                                       c = ASCIINames[k];\r
+                                       if ((c >= 'a') && (c <= 'z') && CapsLock)\r
+                                               c -= 'a' - 'A';\r
+                               }\r
+                       }\r
+                       if (c)\r
+                               LastASCII = c;\r
+               }\r
+\r
+               special = false;\r
+       }\r
+\r
+       if (INL_KeyHook && !special)\r
+               INL_KeyHook();\r
+       outportb(0x20,0x20);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_GetMouseDelta() - Gets the amount that the mouse has moved from the\r
+//             mouse driver\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_GetMouseDelta(int *x,int *y)\r
+{\r
+       Mouse(MDelta);\r
+       *x = _CX;\r
+       *y = _DX;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_GetMouseButtons() - Gets the status of the mouse buttons from the\r
+//             mouse driver\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static word\r
+INL_GetMouseButtons(void)\r
+{\r
+       word    buttons;\r
+\r
+       Mouse(MButtons);\r
+       buttons = _BX;\r
+       return(buttons);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_GetJoyAbs() - Reads the absolute position of the specified joystick\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_GetJoyAbs(word joy,word *xp,word *yp)\r
+{\r
+       byte    xb,yb,\r
+                       xs,ys;\r
+       word    x,y;\r
+\r
+       x = y = 0;\r
+       xs = joy? 2 : 0;                // Find shift value for x axis\r
+       xb = 1 << xs;                   // Use shift value to get x bit mask\r
+       ys = joy? 3 : 1;                // Do the same for y axis\r
+       yb = 1 << ys;\r
+\r
+// Read the absolute joystick values\r
+asm            pushf                           // Save some registers\r
+asm            push    si\r
+asm            push    di\r
+asm            cli                                     // Make sure an interrupt doesn't screw the timings\r
+\r
+\r
+asm            mov             dx,0x201\r
+asm            in              al,dx\r
+asm            out             dx,al           // Clear the resistors\r
+\r
+asm            mov             ah,[xb]         // Get masks into registers\r
+asm            mov             ch,[yb]\r
+\r
+asm            xor             si,si           // Clear count registers\r
+asm            xor             di,di\r
+asm            xor             bh,bh           // Clear high byte of bx for later\r
+\r
+asm            push    bp                      // Don't mess up stack frame\r
+asm            mov             bp,MaxJoyValue\r
+\r
+loop:\r
+asm            in              al,dx           // Get bits indicating whether all are finished\r
+\r
+asm            dec             bp                      // Check bounding register\r
+asm            jz              done            // We have a silly value - abort\r
+\r
+asm            mov             bl,al           // Duplicate the bits\r
+asm            and             bl,ah           // Mask off useless bits (in [xb])\r
+asm            add             si,bx           // Possibly increment count register\r
+asm            mov             cl,bl           // Save for testing later\r
+\r
+asm            mov             bl,al\r
+asm            and             bl,ch           // [yb]\r
+asm            add             di,bx\r
+\r
+asm            add             cl,bl\r
+asm            jnz             loop            // If both bits were 0, drop out\r
+\r
+done:\r
+asm     pop            bp\r
+\r
+asm            mov             cl,[xs]         // Get the number of bits to shift\r
+asm            shr             si,cl           //  and shift the count that many times\r
+\r
+asm            mov             cl,[ys]\r
+asm            shr             di,cl\r
+\r
+asm            mov             [x],si          // Store the values into the variables\r
+asm            mov             [y],di\r
+\r
+asm            pop             di\r
+asm            pop             si\r
+asm            popf                            // Restore the registers\r
+\r
+       *xp = x;\r
+       *yp = y;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_GetJoyDelta() - Returns the relative movement of the specified\r
+//             joystick (from +/-127, scaled adaptively)\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_GetJoyDelta(word joy,int *dx,int *dy,boolean adaptive)\r
+{\r
+       word            x,y;\r
+       longword        time;\r
+       JoystickDef     *def;\r
+static longword        lasttime;\r
+\r
+       IN_GetJoyAbs(joy,&x,&y);\r
+       def = JoyDefs + joy;\r
+\r
+       if (x < def->threshMinX)\r
+       {\r
+               if (x < def->joyMinX)\r
+                       x = def->joyMinX;\r
+\r
+               x = -(x - def->threshMinX);\r
+               x *= def->joyMultXL;\r
+               x >>= JoyScaleShift;\r
+               *dx = (x > 127)? -127 : -x;\r
+       }\r
+       else if (x > def->threshMaxX)\r
+       {\r
+               if (x > def->joyMaxX)\r
+                       x = def->joyMaxX;\r
+\r
+               x = x - def->threshMaxX;\r
+               x *= def->joyMultXH;\r
+               x >>= JoyScaleShift;\r
+               *dx = (x > 127)? 127 : x;\r
+       }\r
+       else\r
+               *dx = 0;\r
+\r
+       if (y < def->threshMinY)\r
+       {\r
+               if (y < def->joyMinY)\r
+                       y = def->joyMinY;\r
+\r
+               y = -(y - def->threshMinY);\r
+               y *= def->joyMultYL;\r
+               y >>= JoyScaleShift;\r
+               *dy = (y > 127)? -127 : -y;\r
+       }\r
+       else if (y > def->threshMaxY)\r
+       {\r
+               if (y > def->joyMaxY)\r
+                       y = def->joyMaxY;\r
+\r
+               y = y - def->threshMaxY;\r
+               y *= def->joyMultYH;\r
+               y >>= JoyScaleShift;\r
+               *dy = (y > 127)? 127 : y;\r
+       }\r
+       else\r
+               *dy = 0;\r
+\r
+       if (adaptive)\r
+       {\r
+               time = (TimeCount - lasttime) / 2;\r
+               if (time)\r
+               {\r
+                       if (time > 8)\r
+                               time = 8;\r
+                       *dx *= time;\r
+                       *dy *= time;\r
+               }\r
+       }\r
+       lasttime = TimeCount;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_GetJoyButtons() - Returns the button status of the specified\r
+//             joystick\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static word\r
+INL_GetJoyButtons(word joy)\r
+{\r
+register       word    result;\r
+\r
+       result = inportb(0x201);        // Get all the joystick buttons\r
+       result >>= joy? 6 : 4;  // Shift into bits 0-1\r
+       result &= 3;                            // Mask off the useless bits\r
+       result ^= 3;\r
+       return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_GetJoyButtonsDB() - Returns the de-bounced button status of the\r
+//             specified joystick\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+word\r
+IN_GetJoyButtonsDB(word joy)\r
+{\r
+       longword        lasttime;\r
+       word            result1,result2;\r
+\r
+       do\r
+       {\r
+               result1 = INL_GetJoyButtons(joy);\r
+               lasttime = TimeCount;\r
+               while (TimeCount == lasttime)\r
+                       ;\r
+               result2 = INL_GetJoyButtons(joy);\r
+       } while (result1 != result2);\r
+       return(result1);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_StartKbd() - Sets up my keyboard stuff for use\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_StartKbd(void)\r
+{\r
+       INL_KeyHook = 0;        // Clear key hook\r
+\r
+       IN_ClearKeysDown();\r
+\r
+       OldKeyVect = getvect(KeyInt);\r
+       setvect(KeyInt,INL_KeyService);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_ShutKbd() - Restores keyboard control to the BIOS\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_ShutKbd(void)\r
+{\r
+       poke(0x40,0x17,peek(0x40,0x17) & 0xfaf0);       // Clear ctrl/alt/shift flags\r
+\r
+       setvect(KeyInt,OldKeyVect);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_StartMouse() - Detects and sets up the mouse\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+INL_StartMouse(void)\r
+{\r
+       if (getvect(MouseInt))\r
+       {\r
+               Mouse(MReset);\r
+               if (_AX == 0xffff)\r
+                       return(true);\r
+       }\r
+       return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_ShutMouse() - Cleans up after the mouse\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_ShutMouse(void)\r
+{\r
+}\r
+\r
+//\r
+//     INL_SetJoyScale() - Sets up scaling values for the specified joystick\r
+//\r
+static void\r
+INL_SetJoyScale(word joy)\r
+{\r
+       JoystickDef     *def;\r
+\r
+       def = &JoyDefs[joy];\r
+       def->joyMultXL = JoyScaleMax / (def->threshMinX - def->joyMinX);\r
+       def->joyMultXH = JoyScaleMax / (def->joyMaxX - def->threshMaxX);\r
+       def->joyMultYL = JoyScaleMax / (def->threshMinY - def->joyMinY);\r
+       def->joyMultYH = JoyScaleMax / (def->joyMaxY - def->threshMaxY);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_SetupJoy() - Sets up thresholding values and calls INL_SetJoyScale()\r
+//             to set up scaling values\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_SetupJoy(word joy,word minx,word maxx,word miny,word maxy)\r
+{\r
+       word            d,r;\r
+       JoystickDef     *def;\r
+\r
+       def = &JoyDefs[joy];\r
+\r
+       def->joyMinX = minx;\r
+       def->joyMaxX = maxx;\r
+       r = maxx - minx;\r
+       d = r / 5;\r
+       def->threshMinX = ((r / 2) - d) + minx;\r
+       def->threshMaxX = ((r / 2) + d) + minx;\r
+\r
+       def->joyMinY = miny;\r
+       def->joyMaxY = maxy;\r
+       r = maxy - miny;\r
+       d = r / 5;\r
+       def->threshMinY = ((r / 2) - d) + miny;\r
+       def->threshMaxY = ((r / 2) + d) + miny;\r
+\r
+       INL_SetJoyScale(joy);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_StartJoy() - Detects & auto-configures the specified joystick\r
+//                                     The auto-config assumes the joystick is centered\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+INL_StartJoy(word joy)\r
+{\r
+       word            x,y;\r
+\r
+       IN_GetJoyAbs(joy,&x,&y);\r
+\r
+       if\r
+       (\r
+               ((x == 0) || (x > MaxJoyValue - 10))\r
+       ||      ((y == 0) || (y > MaxJoyValue - 10))\r
+       )\r
+               return(false);\r
+       else\r
+       {\r
+               IN_SetupJoy(joy,0,x * 2,0,y * 2);\r
+               return(true);\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_ShutJoy() - Cleans up the joystick stuff\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_ShutJoy(word joy)\r
+{\r
+       JoysPresent[joy] = false;\r
+}\r
+\r
+//     Public routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_Startup() - Starts up the Input Mgr\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_Startup(void)\r
+{\r
+       boolean checkjoys,checkmouse;\r
+       word    i;\r
+\r
+       if (IN_Started)\r
+               return;\r
+\r
+       checkjoys = true;\r
+       checkmouse = true;\r
+       for (i = 1;i < _argc;i++)\r
+       {\r
+               switch (US_CheckParm(_argv[i],ParmStrings))\r
+               {\r
+               case 0:\r
+                       checkjoys = false;\r
+                       break;\r
+               case 1:\r
+                       checkmouse = false;\r
+                       break;\r
+               }\r
+       }\r
+\r
+       INL_StartKbd();\r
+       MousePresent = checkmouse? INL_StartMouse() : false;\r
+\r
+       for (i = 0;i < MaxJoys;i++)\r
+               JoysPresent[i] = checkjoys? INL_StartJoy(i) : false;\r
+\r
+       IN_Started = true;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_Default() - Sets up default conditions for the Input Mgr\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_Default(boolean gotit,ControlType in)\r
+{\r
+       if\r
+       (\r
+               (!gotit)\r
+       ||      ((in == ctrl_Joystick1) && !JoysPresent[0])\r
+       ||      ((in == ctrl_Joystick2) && !JoysPresent[1])\r
+       ||      ((in == ctrl_Mouse) && !MousePresent)\r
+       )\r
+               in = ctrl_Keyboard1;\r
+       IN_SetControlType(0,in);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_Shutdown() - Shuts down the Input Mgr\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_Shutdown(void)\r
+{\r
+       word    i;\r
+\r
+       if (!IN_Started)\r
+               return;\r
+\r
+       INL_ShutMouse();\r
+       for (i = 0;i < MaxJoys;i++)\r
+               INL_ShutJoy(i);\r
+       INL_ShutKbd();\r
+\r
+       IN_Started = false;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_SetKeyHook() - Sets the routine that gets called by INL_KeyService()\r
+//                     everytime a real make/break code gets hit\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_SetKeyHook(void (*hook)())\r
+{\r
+       INL_KeyHook = hook;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_ClearKeyDown() - Clears the keyboard array\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_ClearKeysDown(void)\r
+{\r
+       int     i;\r
+\r
+       LastScan = sc_None;\r
+       LastASCII = key_None;\r
+       for (i = 0;i < NumCodes;i++)\r
+               Keyboard[i] = false;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     INL_AdjustCursor() - Internal routine of common code from IN_ReadCursor()\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+INL_AdjustCursor(CursorInfo *info,word buttons,int dx,int dy)\r
+{\r
+       if (buttons & (1 << 0))\r
+               info->button0 = true;\r
+       if (buttons & (1 << 1))\r
+               info->button1 = true;\r
+\r
+       info->x += dx;\r
+       info->y += dy;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_ReadCursor() - Reads the input devices and fills in the cursor info\r
+//             struct\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_ReadCursor(CursorInfo *info)\r
+{\r
+       word    i,\r
+                       buttons;\r
+       int             dx,dy;\r
+\r
+       info->x = info->y = 0;\r
+       info->button0 = info->button1 = false;\r
+\r
+       if (MousePresent)\r
+       {\r
+               buttons = INL_GetMouseButtons();\r
+               INL_GetMouseDelta(&dx,&dy);\r
+               INL_AdjustCursor(info,buttons,dx,dy);\r
+       }\r
+\r
+       for (i = 0;i < MaxJoys;i++)\r
+       {\r
+               if (!JoysPresent[i])\r
+                       continue;\r
+\r
+               buttons = INL_GetJoyButtons(i);\r
+               INL_GetJoyDelta(i,&dx,&dy,true);\r
+               dx /= 64;\r
+               dy /= 64;\r
+               INL_AdjustCursor(info,buttons,dx,dy);\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_ReadControl() - Reads the device associated with the specified\r
+//             player and fills in the control info struct\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_ReadControl(int player,ControlInfo *info)\r
+{\r
+                       boolean         realdelta=false;                                // MDM (GAMERS EDGE)\r
+                       byte            dbyte;\r
+                       word            buttons;\r
+                       int                     dx,dy;\r
+                       Motion          mx,my;\r
+                       ControlType     type;\r
+register       KeyboardDef     *def;\r
+\r
+       dx = dy = 0;\r
+       mx = my = motion_None;\r
+       buttons = 0;\r
+\r
+#if 0\r
+       if (DemoMode == demo_Playback)\r
+       {\r
+               dbyte = DemoBuffer[DemoOffset + 1];\r
+               my = (dbyte & 3) - 1;\r
+               mx = ((dbyte >> 2) & 3) - 1;\r
+               buttons = (dbyte >> 4) & 3;\r
+\r
+               if (!(--DemoBuffer[DemoOffset]))\r
+               {\r
+                       DemoOffset += 2;\r
+                       if (DemoOffset >= DemoSize)\r
+                               DemoMode = demo_PlayDone;\r
+               }\r
+\r
+               realdelta = false;\r
+       }\r
+       else if (DemoMode == demo_PlayDone)\r
+               Quit("Demo playback exceeded");\r
+       else\r
+#endif\r
+       {\r
+                                                                                                                       // MDM begin (GAMERS EDGE) - added this block\r
+               ControlTypeUsed = ctrl_None;\r
+\r
+               // Handle mouse input...\r
+               //\r
+               if ((MousePresent) && (ControlTypeUsed == ctrl_None))\r
+               {\r
+                       INL_GetMouseDelta(&dx,&dy);\r
+                       buttons = INL_GetMouseButtons();\r
+                       realdelta = true;\r
+                       if (dx || dy || buttons)\r
+                               ControlTypeUsed = ctrl_Mouse;\r
+               }\r
+\r
+               // Handle joystick input...\r
+               //\r
+               if ((JoystickCalibrated) && (ControlTypeUsed == ctrl_None))\r
+               {\r
+                       type = ctrl_Joystick1;\r
+                       INL_GetJoyDelta(type - ctrl_Joystick,&dx,&dy,false);\r
+                       buttons = INL_GetJoyButtons(type - ctrl_Joystick);\r
+                       realdelta = true;\r
+                       if (dx || dy || buttons)\r
+                               ControlTypeUsed = ctrl_Joystick;\r
+               }\r
+\r
+               // Handle keyboard input...\r
+               //\r
+               if (ControlTypeUsed == ctrl_None)\r
+               {\r
+                       type = ctrl_Keyboard1;\r
+                       def = &KbdDefs[type - ctrl_Keyboard];\r
+\r
+                       if (Keyboard[def->upleft])\r
+                               mx = motion_Left,my = motion_Up;\r
+                       else if (Keyboard[def->upright])\r
+                               mx = motion_Right,my = motion_Up;\r
+                       else if (Keyboard[def->downleft])\r
+                               mx = motion_Left,my = motion_Down;\r
+                       else if (Keyboard[def->downright])\r
+                               mx = motion_Right,my = motion_Down;\r
+\r
+                       if (Keyboard[def->up])\r
+                               my = motion_Up;\r
+                       else if (Keyboard[def->down])\r
+                               my = motion_Down;\r
+\r
+                       if (Keyboard[def->left])\r
+                               mx = motion_Left;\r
+                       else if (Keyboard[def->right])\r
+                               mx = motion_Right;\r
+\r
+                       if (Keyboard[def->button0])\r
+                               buttons += 1 << 0;\r
+                       if (Keyboard[def->button1])\r
+                               buttons += 1 << 1;\r
+                       realdelta = false;\r
+                       if (mx || my || buttons)\r
+                               ControlTypeUsed = ctrl_Keyboard;\r
+               }                                                                                                       // MDM end (GAMERS EDGE)\r
+       }\r
+\r
+       if (realdelta)\r
+       {\r
+               mx = (dx < 0)? motion_Left : ((dx > 0)? motion_Right : motion_None);\r
+               my = (dy < 0)? motion_Up : ((dy > 0)? motion_Down : motion_None);\r
+       }\r
+       else\r
+       {\r
+               dx = mx * 127;\r
+               dy = my * 127;\r
+       }\r
+\r
+       info->x = dx;\r
+       info->xaxis = mx;\r
+       info->y = dy;\r
+       info->yaxis = my;\r
+       info->button0 = buttons & (1 << 0);\r
+       info->button1 = buttons & (1 << 1);\r
+       info->dir = DirTable[((my + 1) * 3) + (mx + 1)];\r
+\r
+#if 0\r
+       if (DemoMode == demo_Record)\r
+       {\r
+               // Pack the control info into a byte\r
+               dbyte = (buttons << 4) | ((mx + 1) << 2) | (my + 1);\r
+\r
+               if\r
+               (\r
+                       (DemoBuffer[DemoOffset + 1] == dbyte)\r
+               &&      (DemoBuffer[DemoOffset] < 255)\r
+               )\r
+                       (DemoBuffer[DemoOffset])++;\r
+               else\r
+               {\r
+                       if (DemoOffset || DemoBuffer[DemoOffset])\r
+                               DemoOffset += 2;\r
+\r
+                       if (DemoOffset >= DemoSize)\r
+                               Quit("Demo buffer overflow");\r
+\r
+                       DemoBuffer[DemoOffset] = 1;\r
+                       DemoBuffer[DemoOffset + 1] = dbyte;\r
+               }\r
+       }\r
+#endif\r
+}\r
+\r
+#if 0\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_ReadControl() - Reads the device associated with the specified\r
+//             player and fills in the control info struct\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_ReadControl(int player,ControlInfo *info)\r
+{\r
+                       boolean         realdelta;\r
+                       byte            dbyte;\r
+                       word            buttons;\r
+                       int                     dx,dy;\r
+                       Motion          mx,my;\r
+                       ControlType     type;\r
+register       KeyboardDef     *def;\r
+\r
+       dx = dy = 0;\r
+       mx = my = motion_None;\r
+       buttons = 0;\r
+\r
+#if 0\r
+       if (DemoMode == demo_Playback)\r
+       {\r
+               dbyte = DemoBuffer[DemoOffset + 1];\r
+               my = (dbyte & 3) - 1;\r
+               mx = ((dbyte >> 2) & 3) - 1;\r
+               buttons = (dbyte >> 4) & 3;\r
+\r
+               if (!(--DemoBuffer[DemoOffset]))\r
+               {\r
+                       DemoOffset += 2;\r
+                       if (DemoOffset >= DemoSize)\r
+                               DemoMode = demo_PlayDone;\r
+               }\r
+\r
+               realdelta = false;\r
+       }\r
+       else if (DemoMode == demo_PlayDone)\r
+               Quit("Demo playback exceeded");\r
+       else\r
+#endif\r
+       {\r
+               switch (type = Controls[player])\r
+               {\r
+               case ctrl_Keyboard1:\r
+               case ctrl_Keyboard2:\r
+                       def = &KbdDefs[type - ctrl_Keyboard];\r
+\r
+                       if (Keyboard[def->upleft])\r
+                               mx = motion_Left,my = motion_Up;\r
+                       else if (Keyboard[def->upright])\r
+                               mx = motion_Right,my = motion_Up;\r
+                       else if (Keyboard[def->downleft])\r
+                               mx = motion_Left,my = motion_Down;\r
+                       else if (Keyboard[def->downright])\r
+                               mx = motion_Right,my = motion_Down;\r
+\r
+                       if (Keyboard[def->up])\r
+                               my = motion_Up;\r
+                       else if (Keyboard[def->down])\r
+                               my = motion_Down;\r
+\r
+                       if (Keyboard[def->left])\r
+                               mx = motion_Left;\r
+                       else if (Keyboard[def->right])\r
+                               mx = motion_Right;\r
+\r
+                       if (Keyboard[def->button0])\r
+                               buttons += 1 << 0;\r
+                       if (Keyboard[def->button1])\r
+                               buttons += 1 << 1;\r
+                       realdelta = false;\r
+                       break;\r
+               case ctrl_Joystick1:\r
+               case ctrl_Joystick2:\r
+                       INL_GetJoyDelta(type - ctrl_Joystick,&dx,&dy,false);\r
+                       buttons = INL_GetJoyButtons(type - ctrl_Joystick);\r
+                       realdelta = true;\r
+                       break;\r
+               case ctrl_Mouse:\r
+                       INL_GetMouseDelta(&dx,&dy);\r
+                       buttons = INL_GetMouseButtons();\r
+                       realdelta = true;\r
+                       break;\r
+               }\r
+       }\r
+\r
+       if (realdelta)\r
+       {\r
+               mx = (dx < 0)? motion_Left : ((dx > 0)? motion_Right : motion_None);\r
+               my = (dy < 0)? motion_Up : ((dy > 0)? motion_Down : motion_None);\r
+       }\r
+       else\r
+       {\r
+               dx = mx * 127;\r
+               dy = my * 127;\r
+       }\r
+\r
+       info->x = dx;\r
+       info->xaxis = mx;\r
+       info->y = dy;\r
+       info->yaxis = my;\r
+       info->button0 = buttons & (1 << 0);\r
+       info->button1 = buttons & (1 << 1);\r
+       info->dir = DirTable[((my + 1) * 3) + (mx + 1)];\r
+\r
+#if 0\r
+       if (DemoMode == demo_Record)\r
+       {\r
+               // Pack the control info into a byte\r
+               dbyte = (buttons << 4) | ((mx + 1) << 2) | (my + 1);\r
+\r
+               if\r
+               (\r
+                       (DemoBuffer[DemoOffset + 1] == dbyte)\r
+               &&      (DemoBuffer[DemoOffset] < 255)\r
+               )\r
+                       (DemoBuffer[DemoOffset])++;\r
+               else\r
+               {\r
+                       if (DemoOffset || DemoBuffer[DemoOffset])\r
+                               DemoOffset += 2;\r
+\r
+                       if (DemoOffset >= DemoSize)\r
+                               Quit("Demo buffer overflow");\r
+\r
+                       DemoBuffer[DemoOffset] = 1;\r
+                       DemoBuffer[DemoOffset + 1] = dbyte;\r
+               }\r
+       }\r
+#endif\r
+}\r
+#endif\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_SetControlType() - Sets the control type to be used by the specified\r
+//             player\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_SetControlType(int player,ControlType type)\r
+{\r
+       // DEBUG - check that requested type is present?\r
+       Controls[player] = type;\r
+}\r
+\r
+#if 0\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_StartDemoRecord() - Starts the demo recording, using a buffer the\r
+//             size passed. Returns if the buffer allocation was successful\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+IN_StartDemoRecord(word bufsize)\r
+{\r
+       if (!bufsize)\r
+               return(false);\r
+\r
+       MM_GetPtr((memptr *)&DemoBuffer,bufsize);\r
+       DemoMode = demo_Record;\r
+       DemoSize = bufsize & ~1;\r
+       DemoOffset = 0;\r
+       DemoBuffer[0] = DemoBuffer[1] = 0;\r
+\r
+       return(true);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_StartDemoPlayback() - Plays back the demo pointed to of the given size\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_StartDemoPlayback(byte /*_1seg*/ *buffer,word bufsize)\r
+{\r
+       DemoBuffer = buffer;\r
+       DemoMode = demo_Playback;\r
+       DemoSize = bufsize & ~1;\r
+       DemoOffset = 0;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_StopDemo() - Turns off demo mode\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_StopDemo(void)\r
+{\r
+       if ((DemoMode == demo_Record) && DemoOffset)\r
+               DemoOffset += 2;\r
+\r
+       DemoMode = demo_Off;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_FreeDemoBuffer() - Frees the demo buffer, if it's been allocated\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_FreeDemoBuffer(void)\r
+{\r
+       if (DemoBuffer)\r
+               MM_FreePtr((memptr *)&DemoBuffer);\r
+}\r
+#endif\r
+\r
+\r
+#if 0\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_GetScanName() - Returns a string containing the name of the\r
+//             specified scan code\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+byte *\r
+IN_GetScanName(ScanCode scan)\r
+{\r
+       byte            **p;\r
+       ScanCode        far *s;\r
+\r
+       for (s = ExtScanCodes,p = ExtScanNames;*s;p++,s++)\r
+               if (*s == scan)\r
+                       return(*p);\r
+\r
+       return(ScanNames[scan]);\r
+}\r
+#endif\r
+\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_WaitForKey() - Waits for a scan code, then clears LastScan and\r
+//             returns the scan code\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+ScanCode\r
+IN_WaitForKey(void)\r
+{\r
+       ScanCode        result;\r
+\r
+       while (!(result = LastScan))\r
+               ;\r
+       LastScan = 0;\r
+       return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_WaitForASCII() - Waits for an ASCII char, then clears LastASCII and\r
+//             returns the ASCII value\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+char\r
+IN_WaitForASCII(void)\r
+{\r
+       char            result;\r
+\r
+       while (!(result = LastASCII))\r
+               ;\r
+       LastASCII = '\0';\r
+       return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_AckBack() - Waits for either an ASCII keypress or a button press\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_AckBack(void)\r
+{\r
+       word    i;\r
+\r
+       while (!LastScan)\r
+       {\r
+               if (MousePresent)\r
+               {\r
+                       if (INL_GetMouseButtons())\r
+                       {\r
+                               while (INL_GetMouseButtons())\r
+                                       ;\r
+                               return;\r
+                       }\r
+               }\r
+\r
+               for (i = 0;i < MaxJoys;i++)\r
+               {\r
+                       if (JoysPresent[i])\r
+                       {\r
+                               if (IN_GetJoyButtonsDB(i))\r
+                               {\r
+                                       while (IN_GetJoyButtonsDB(i))\r
+                                               ;\r
+                                       return;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       IN_ClearKey(LastScan);\r
+       LastScan = sc_None;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_Ack() - Clears user input & then calls IN_AckBack()\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+IN_Ack(void)\r
+{\r
+       word    i;\r
+\r
+       IN_ClearKey(LastScan);\r
+       LastScan = sc_None;\r
+\r
+       if (MousePresent)\r
+               while (INL_GetMouseButtons())\r
+                                       ;\r
+       for (i = 0;i < MaxJoys;i++)\r
+               if (JoysPresent[i])\r
+                       while (IN_GetJoyButtonsDB(i))\r
+                               ;\r
+\r
+       IN_AckBack();\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_IsUserInput() - Returns true if a key has been pressed or a button\r
+//             is down\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+IN_IsUserInput(void)\r
+{\r
+       boolean result;\r
+       word    i;\r
+\r
+       result = LastScan;\r
+\r
+       if (MousePresent)\r
+               if (INL_GetMouseButtons())\r
+                       result = true;\r
+\r
+       for (i = 0;i < MaxJoys;i++)\r
+               if (JoysPresent[i])\r
+                       if (INL_GetJoyButtons(i))\r
+                               result = true;\r
+\r
+       return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     IN_UserInput() - Waits for the specified delay time (in ticks) or the\r
+//             user pressing a key or a mouse button. If the clear flag is set, it\r
+//             then either clears the key or waits for the user to let the mouse\r
+//             button up.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+IN_UserInput(longword delay,boolean clear)\r
+{\r
+       longword        lasttime;\r
+\r
+       lasttime = TimeCount;\r
+       do\r
+       {\r
+               if (IN_IsUserInput())\r
+               {\r
+                       if (clear)\r
+                               IN_AckBack();\r
+                       return(true);\r
+               }\r
+       } while (TimeCount - lasttime < delay);\r
+       return(false);\r
+}\r
diff --git a/16/cawat/ID_IN.H b/16/cawat/ID_IN.H
new file mode 100644 (file)
index 0000000..f1f0ef8
--- /dev/null
@@ -0,0 +1,214 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+//     ID Engine\r
+//     ID_IN.h - Header file for Input Manager\r
+//     v1.0d1\r
+//     By Jason Blochowiak\r
+//\r
+
+#include "lib_head.h"
+\r
+#ifndef        __TYPES__\r
+#include "ID_Types.h"\r
+#endif\r
+\r
+#ifndef        __ID_IN__\r
+#define        __ID_IN__\r
+\r
+#ifdef __DEBUG__\r
+#define        __DEBUG_InputMgr__\r
+#endif\r
+\r
+#define        MaxPlayers      4\r
+#define        MaxKbds         2\r
+#define        MaxJoys         2\r
+#define        NumCodes        128\r
+\r
+typedef        byte            ScanCode;\r
+#define        sc_None                 0\r
+#define        sc_Bad                  0xff\r
+#define        sc_Return               0x1c\r
+#define        sc_Enter                sc_Return\r
+#define        sc_Escape               0x01\r
+#define        sc_Space                0x39\r
+#define        sc_BackSpace    0x0e\r
+#define        sc_Tab                  0x0f\r
+#define        sc_Alt                  0x38\r
+#define        sc_Control              0x1d\r
+#define        sc_CapsLock             0x3a\r
+#define        sc_LShift               0x2a\r
+#define        sc_RShift               0x36\r
+#define        sc_UpArrow              0x48\r
+#define        sc_DownArrow    0x50\r
+#define        sc_LeftArrow    0x4b\r
+#define        sc_RightArrow   0x4d\r
+#define        sc_Insert               0x52\r
+#define        sc_Delete               0x53\r
+#define        sc_Home                 0x47\r
+#define        sc_End                  0x4f\r
+#define        sc_PgUp                 0x49\r
+#define        sc_PgDn                 0x51\r
+#define        sc_F1                   0x3b\r
+#define        sc_F2                   0x3c\r
+#define        sc_F3                   0x3d\r
+#define        sc_F4                   0x3e\r
+#define        sc_F5                   0x3f\r
+#define        sc_F6                   0x40\r
+#define        sc_F7                   0x41\r
+#define        sc_F8                   0x42\r
+#define        sc_F9                   0x43\r
+#define        sc_F10                  0x44\r
+#define        sc_F11                  0x57\r
+#define        sc_F12                  0x59\r
+\r
+#define        sc_A                    0x1e\r
+#define        sc_B                    0x30\r
+#define        sc_C                    0x2e\r
+#define        sc_D                    0x20\r
+#define        sc_E                    0x12\r
+#define        sc_F                    0x21\r
+#define        sc_G                    0x22\r
+#define        sc_H                    0x23\r
+#define        sc_I                    0x17\r
+#define        sc_J                    0x24\r
+#define        sc_K                    0x25\r
+#define        sc_L                    0x26\r
+#define        sc_M                    0x32\r
+#define        sc_N                    0x31\r
+#define        sc_O                    0x18\r
+#define        sc_P                    0x19\r
+#define        sc_Q                    0x10\r
+#define        sc_R                    0x13\r
+#define        sc_S                    0x1f\r
+#define        sc_T                    0x14\r
+#define        sc_U                    0x16\r
+#define        sc_V                    0x2f\r
+#define        sc_W                    0x11\r
+#define        sc_X                    0x2d\r
+#define        sc_Y                    0x15\r
+#define        sc_Z                    0x2c\r
+\r
+#define        key_None                0\r
+#define        key_Return              0x0d\r
+#define        key_Enter               key_Return\r
+#define        key_Escape              0x1b\r
+#define        key_Space               0x20\r
+#define        key_BackSpace   0x08\r
+#define        key_Tab                 0x09\r
+#define        key_Delete              0x7f\r
+\r
+//     Stuff for the mouse\r
+#define        MReset          0\r
+#define        MButtons        3\r
+#define        MDelta          11\r
+\r
+#define        MouseInt        0x33\r
+#define        Mouse(x)        _AX = x,geninterrupt(MouseInt)\r
+\r
+typedef        enum            {\r
+                                               demo_Off,demo_Record,demo_Playback,demo_PlayDone\r
+                                       } Demo;\r
+typedef        enum            {\r
+                                               ctrl_None,                              // MDM (GAMERS EDGE) - added\r
+                                               ctrl_Keyboard,\r
+                                                       ctrl_Keyboard1 = ctrl_Keyboard,ctrl_Keyboard2,\r
+                                               ctrl_Joystick,\r
+                                                       ctrl_Joystick1 = ctrl_Joystick,ctrl_Joystick2,\r
+                                               ctrl_Mouse\r
+                                       } ControlType;\r
+typedef        enum            {\r
+                                               motion_Left = -1,motion_Up = -1,\r
+                                               motion_None = 0,\r
+                                               motion_Right = 1,motion_Down = 1\r
+                                       } Motion;\r
+typedef        enum            {\r
+                                               dir_North,dir_NorthEast,\r
+                                               dir_East,dir_SouthEast,\r
+                                               dir_South,dir_SouthWest,\r
+                                               dir_West,dir_NorthWest,\r
+                                               dir_None\r
+                                       } Direction;\r
+typedef        struct          {\r
+                                               boolean         button0,button1;\r
+                                               int                     x,y;\r
+                                               Motion          xaxis,yaxis;\r
+                                               Direction       dir;\r
+                                       } CursorInfo;\r
+typedef        CursorInfo      ControlInfo;\r
+typedef        struct          {\r
+                                               ScanCode        button0,button1,\r
+                                                                       upleft,         up,             upright,\r
+                                                                       left,                           right,\r
+                                                                       downleft,       down,   downright;\r
+                                       } KeyboardDef;\r
+typedef        struct          {\r
+                                               word            joyMinX,joyMinY,\r
+                                                                       threshMinX,threshMinY,\r
+                                                                       threshMaxX,threshMaxY,\r
+                                                                       joyMaxX,joyMaxY,\r
+                                                                       joyMultXL,joyMultYL,\r
+                                                                       joyMultXH,joyMultYH;\r
+                                       } JoystickDef;\r
+// Global variables\r
+extern boolean         Keyboard[],\r
+                                       MousePresent,\r
+                                       JoysPresent[];\r
+extern boolean         Paused;\r
+extern char            LastASCII;\r
+extern ScanCode        LastScan;\r
+extern KeyboardDef     KbdDefs[];\r
+extern JoystickDef     JoyDefs[];\r
+extern ControlType     Controls[MaxPlayers];\r
+\r
+extern boolean JoystickCalibrated;                             // MDM (GAMERS EDGE) - added\r
+extern ControlType ControlTypeUsed;                            // MDM (GAMERS EDGE) - added\r
+\r
+extern Demo            DemoMode;\r
+extern byte _seg       *DemoBuffer;\r
+extern word            DemoOffset,DemoSize;\r
+\r
+// Function prototypes\r
+#define        IN_KeyDown(code)        (Keyboard[(code)])\r
+#define        IN_ClearKey(code)       {Keyboard[code] = false;\\r
+                                                       if (code == LastScan) LastScan = sc_None;}\r
+\r
+// DEBUG - put names in prototypes\r
+extern void            IN_Startup(void),IN_Shutdown(void),\r
+                                       IN_Default(boolean gotit,ControlType in),\r
+                                       IN_SetKeyHook(void (*)()),\r
+                                       IN_ClearKeysDown(void),\r
+                                       IN_ReadCursor(CursorInfo *),\r
+                                       IN_ReadControl(int,ControlInfo *),\r
+                                       IN_SetControlType(int,ControlType),\r
+                                       IN_GetJoyAbs(word joy,word *xp,word *yp),\r
+                                       IN_SetupJoy(word joy,word minx,word maxx,\r
+                                                               word miny,word maxy),\r
+                                       IN_StartDemoPlayback(byte _seg *buffer,word bufsize),\r
+                                       IN_StopDemo(void),IN_FreeDemoBuffer(void),\r
+                                       IN_Ack(void),IN_AckBack(void);\r
+extern boolean         IN_UserInput(longword delay,boolean clear),\r
+                                       IN_IsUserInput(void),\r
+                                       IN_StartDemoRecord(word bufsize);\r
+extern byte            *IN_GetScanName(ScanCode);\r
+extern char            IN_WaitForASCII(void);\r
+extern ScanCode        IN_WaitForKey(void);\r
+extern word            IN_GetJoyButtonsDB(word joy);\r
+\r
+#endif\r
diff --git a/16/cawat/ID_MM.C b/16/cawat/ID_MM.C
new file mode 100644 (file)
index 0000000..1331a0c
--- /dev/null
@@ -0,0 +1,1150 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// NEWMM.C\r
+\r
+/*\r
+=============================================================================\r
+\r
+                       ID software memory manager\r
+                       --------------------------\r
+\r
+Primary coder: John Carmack\r
+\r
+RELIES ON\r
+---------\r
+Quit (char *error) function\r
+\r
+\r
+WORK TO DO\r
+----------\r
+MM_SizePtr to change the size of a given pointer\r
+\r
+Multiple purge levels utilized\r
+\r
+EMS / XMS unmanaged routines\r
+\r
+=============================================================================\r
+*/\r
+\r
+//#include "LIB_HEAD.H"
+#include "ID_MM.H"
+//#pragma hdrstop\r
+\r
+//#pragma warn -pro\r
+//#pragma warn -use\r
+\r
+\r
+#if 1          // 1 == Debug/Dev  ;  0 == Production/final\r
+\r
+#define OUT_OF_MEM_MSG "MM_GetPtr: Out of memory!\nYou were short :%ld bytes"\r
+\r
+#else\r
+\r
+\r
+#define OUT_OF_MEM_MSG "\npee\n"
+\r
+#endif\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                       LOCAL INFO\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define LOCKBIT                0x80    // if set in attributes, block cannot be moved\r
+#define PURGEBITS      3               // 0-3 level, 0= unpurgable, 3= purge first\r
+#define PURGEMASK      0xfffc\r
+#define BASEATTRIBUTES 0       // unlocked, non purgable\r
+\r
+#define MAXUMBS                10\r
+\r
+typedef struct mmblockstruct\r
+{\r
+       unsigned        start,length;\r
+       unsigned        attributes;\r
+       memptr          *useptr;        // pointer to the segment start\r
+       struct mmblockstruct far *next;\r
+} mmblocktype;\r
+\r
+\r
+//#define GETNEWBLOCK {if(!(mmnew=mmfree))Quit("MM_GETNEWBLOCK: No free blocks!");mmfree=mmfree->next;}\r
+\r
+#define GETNEWBLOCK {if(!mmfree)MML_ClearBlock();mmnew=mmfree;mmfree=mmfree->next;}\r
+\r
+#define FREEBLOCK(x) {*x->useptr=NULL;x->next=mmfree;mmfree=x;}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+mminfotype     mminfo;\r
+memptr         bufferseg;\r
+boolean                mmerror;\r
+\r
+void           (* beforesort) (void);\r
+void           (* aftersort) (void);\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+boolean                mmstarted;\r
+
+void huge      *hugeheap;\r
+void far       *farheap;\r
+void           *nearheap;\r
+\r
+mmblocktype    far mmblocks[MAXBLOCKS]\r
+                       ,far *mmhead,far *mmfree,far *mmrover,far *mmnew;\r
+\r
+boolean                bombonerror;\r
+\r
+unsigned       totalEMSpages,freeEMSpages,EMSpageframe,EMSpagesmapped,EMShandle;\r
+\r
+void           (* XMSaddr) (void);             // far pointer to XMS driver\r
+\r
+unsigned       numUMBs,UMBbase[MAXUMBS];\r
+\r
+//==========================================================================\r
+\r
+//\r
+// local prototypes\r
+//\r
+\r
+boolean                MML_CheckForEMS (void);\r
+void           MML_ShutdownEMS (void);\r
+void           MM_MapEMS (void);\r
+boolean        MML_CheckForXMS (void);\r
+void           MML_ShutdownXMS (void);\r
+void           MML_UseSpace (unsigned segstart, unsigned seglength);\r
+void           MML_ClearBlock (void);\r
+\r
+//==========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= MML_CheckForEMS\r
+=\r
+= Routine from p36 of Extending DOS\r
+=\r
+=======================\r
+*/\r
+\r
+boolean MML_CheckForEMS (void)\r
+{
+       boolean emmcfems;
+       char    emmname[] = "EMMXXXX0";
+
+       __asm {\r
+               mov     dx,OFFSET emmname[0]\r
+               mov     ax,0x3d00\r
+               int     0x21            // try to open EMMXXXX0 device\r
+               jc      error\r
+\r
+               mov     bx,ax\r
+               mov     ax,0x4400\r
+\r
+               int     0x21            // get device info\r
+               jc      error\r
+\r
+               and     dx,0x80\r
+               jz      error\r
+\r
+               mov     ax,0x4407\r
+\r
+               int     0x21            // get status\r
+               jc      error\r
+               or      al,al\r
+               jz      error\r
+\r
+               mov     ah,0x3e\r
+               int     0x21            // close handle\r
+               jc      error
+               //\r
+               // EMS is good\r
+               //
+               mov     emmcfems,1
+               jmp End
+               error:
+               //\r
+               // EMS is bad\r
+               //\r
+               mov     emmcfems,0
+               End:
+       }
+       return(emmcfems);\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MML_SetupEMS\r
+=\r
+=======================\r
+*/\r
+\r
+void MML_SetupEMS (void)\r
+{\r
+       char    str[80],str2[10];\r
+       unsigned        error;\r
+\r
+       totalEMSpages = freeEMSpages = EMSpageframe = EMSpagesmapped = 0;\r
+\r
+       __asm {\r
+               mov     ah,EMS_STATUS\r
+               int     EMS_INT                                         // make sure EMS hardware is present\r
+               or      ah,ah\r
+               jnz     error\r
+\r
+               mov     ah,EMS_VERSION\r
+               int     EMS_INT\r
+               or      ah,ah\r
+               jnz     error\r
+               cmp     al,0x32                                         // only work on ems 3.2 or greater\r
+               jb      error\r
+\r
+               mov     ah,EMS_GETFRAME\r
+               int     EMS_INT                                         // find the page frame address\r
+               or      ah,ah\r
+               jnz     error\r
+               mov     [EMSpageframe],bx\r
+\r
+               mov     ah,EMS_GETPAGES\r
+               int     EMS_INT                                         // find out how much EMS is there\r
+               or      ah,ah\r
+               jnz     error\r
+               mov     [totalEMSpages],dx\r
+               mov     [freeEMSpages],bx\r
+               or      bx,bx\r
+               jz      noEMS                                           // no EMS at all to allocate\r
+\r
+               cmp     bx,4\r
+               jle     getpages                                        // there is only 1,2,3,or 4 pages\r
+               mov     bx,4                                            // we can't use more than 4 pages\r
+       }\r
+\r
+getpages:\r
+asm {\r
+       mov     [EMSpagesmapped],bx\r
+       mov     ah,EMS_ALLOCPAGES                       // allocate up to 64k of EMS\r
+       int     EMS_INT\r
+       or      ah,ah\r
+       jnz     error\r
+       mov     [EMShandle],dx\r
+       }\r
+       return;\r
+\r
+error:\r
+       error = _AH;\r
+       strcpy (str,"MML_SetupEMS: EMS error 0x");\r
+       itoa(error,str2,16);\r
+       strcpy (str,str2);\r
+       Quit (str);\r
+\r
+noEMS:\r
+;\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MML_ShutdownEMS\r
+=\r
+=======================\r
+*/\r
+\r
+void MML_ShutdownEMS (void)\r
+{\r
+       if (!EMShandle)\r
+               return;\r
+\r
+               {\r
+       mov     ah,EMS_FREEPAGES\r
+       mov     dx,[EMShandle]\r
+       int     EMS_INT\r
+       or      ah,ah\r
+       jz      ok\r
+       }\r
+\r
+       Quit ("MML_ShutdownEMS: Error freeing EMS!");\r
+\r
+ok:\r
+;\r
+}\r
+\r
+/*\r
+====================\r
+=\r
+= MM_MapEMS\r
+=\r
+= Maps the 64k of EMS used by memory manager into the page frame\r
+= for general use.  This only needs to be called if you are keeping\r
+= other things in EMS.\r
+=\r
+====================\r
+*/\r
+\r
+void MM_MapEMS (void)\r
+{\r
+       char    str[80],str2[10];\r
+       unsigned        error;\r
+       int     i;\r
+\r
+       for (i=0;i<EMSpagesmapped;i++)\r
+       {\r
+                       {\r
+               mov     ah,EMS_MAPPAGE\r
+               mov     bx,[i]                  // logical page\r
+               mov     al,bl                   // physical page\r
+               mov     dx,[EMShandle]  // handle\r
+               int     EMS_INT\r
+               or      ah,ah\r
+               jnz     error\r
+               }\r
+       }\r
+\r
+       return;\r
+\r
+error:\r
+       error = _AH;\r
+       strcpy (str,"MM_MapEMS: EMS error 0x");\r
+       itoa(error,str2,16);\r
+       strcpy (str,str2);\r
+       Quit (str);\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= MML_CheckForXMS\r
+=\r
+= Check for XMM driver\r
+=\r
+=======================\r
+*/\r
+\r
+boolean MML_CheckForXMS (void)\r
+{\r
+       numUMBs = 0;\r
+\r
+asm {\r
+       mov     ax,0x4300\r
+       int     0x2f                            // query status of installed diver\r
+       cmp     al,0x80\r
+       je      good\r
+       }\r
+       return false;\r
+good:\r
+       return true;\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MML_SetupXMS\r
+=\r
+= Try to allocate all upper memory block\r
+=\r
+=======================\r
+*/\r
+\r
+void MML_SetupXMS (void)\r
+{\r
+       unsigned        base,size;\r
+\r
+               {\r
+       mov     ax,0x4310\r
+       int     0x2f\r
+       mov     [WORD PTR XMSaddr],bx\r
+       mov     [WORD PTR XMSaddr+2],es         // function pointer to XMS driver\r
+       }\r
+\r
+getmemory:\r
+               {\r
+       mov     ah,XMS_ALLOCUMB\r
+       mov     dx,0xffff                                       // try for largest block possible\r
+       call    [DWORD PTR XMSaddr]\r
+       or      ax,ax\r
+       jnz     gotone\r
+\r
+       cmp     bl,0xb0                                         // error: smaller UMB is available\r
+       jne     done;\r
+\r
+       mov     ah,XMS_ALLOCUMB\r
+       call    [DWORD PTR XMSaddr]             // DX holds largest available UMB\r
+       or      ax,ax\r
+       jz      done                                            // another error...\r
+       }\r
+\r
+gotone:\r
+               {\r
+       mov     [base],bx\r
+       mov     [size],dx\r
+       }\r
+       MML_UseSpace (base,size);\r
+       mminfo.XMSmem += size*16;\r
+       UMBbase[numUMBs] = base;\r
+       numUMBs++;\r
+       if (numUMBs < MAXUMBS)\r
+               goto getmemory;\r
+\r
+done:;\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MML_ShutdownXMS\r
+=\r
+======================\r
+*/\r
+\r
+void MML_ShutdownXMS (void)\r
+{\r
+       int     i;\r
+       unsigned        base;\r
+\r
+       for (i=0;i<numUMBs;i++)\r
+       {\r
+               base = UMBbase[i];\r
+\r
+               mov     ah,XMS_FREEUMB\r
+               mov     dx,[base]\r
+               call    [DWORD PTR XMSaddr]\r
+       }\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+======================\r
+=\r
+= MML_UseSpace\r
+=\r
+= Marks a range of paragraphs as usable by the memory manager\r
+= This is used to mark space for the near heap, far heap, ems page frame,\r
+= and upper memory blocks\r
+=\r
+======================\r
+*/\r
+\r
+void MML_UseSpace (unsigned segstart, unsigned seglength)\r
+{\r
+       mmblocktype far *scan,far *last;\r
+       unsigned        oldend;\r
+       long            extra;\r
+\r
+       scan = last = mmhead;\r
+       mmrover = mmhead;               // reset rover to start of memory\r
+\r
+//\r
+// search for the block that contains the range of segments\r
+//\r
+       while (scan->start+scan->length < segstart)\r
+       {\r
+               last = scan;\r
+               scan = scan->next;\r
+       }\r
+\r
+//\r
+// take the given range out of the block\r
+//\r
+       oldend = scan->start + scan->length;\r
+       extra = oldend - (segstart+seglength);\r
+       if (extra < 0)\r
+               Quit ("MML_UseSpace: Segment spans two blocks!");\r
+\r
+       if (segstart == scan->start)\r
+       {\r
+               last->next = scan->next;                        // unlink block\r
+               FREEBLOCK(scan);\r
+               scan = last;\r
+       }\r
+       else\r
+               scan->length = segstart-scan->start;    // shorten block\r
+\r
+       if (extra > 0)\r
+       {\r
+               GETNEWBLOCK;\r
+               mmnew->next = scan->next;\r
+               scan->next = mmnew;\r
+               mmnew->start = segstart+seglength;\r
+               mmnew->length = extra;\r
+               mmnew->attributes = LOCKBIT;\r
+       }\r
+\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= MML_ClearBlock\r
+=\r
+= We are out of blocks, so free a purgable block\r
+=\r
+====================\r
+*/\r
+\r
+void MML_ClearBlock (void)\r
+{\r
+       mmblocktype far *scan,far *last;\r
+\r
+       scan = mmhead->next;\r
+\r
+       while (scan)\r
+       {\r
+               if (!(scan->attributes&LOCKBIT) && (scan->attributes&PURGEBITS) )\r
+               {\r
+                       MM_FreePtr(scan->useptr);\r
+                       return;\r
+               }\r
+               scan = scan->next;\r
+       }\r
+\r
+       Quit ("MM_ClearBlock: No purgable blocks!");\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+/*\r
+===================\r
+=\r
+= MM_Startup\r
+=\r
+= Grabs all space from turbo with malloc/farmalloc\r
+= Allocates bufferseg misc buffer\r
+=\r
+===================\r
+*/\r
+\r
+static char *ParmStrings[] = {"noems","noxms",""};\r
+\r
+void MM_Startup (void)\r
+{\r
+       int i;\r
+       unsigned        long length;\r
+       void far        *start;\r
+       unsigned        segstart,seglength,endfree;\r
+\r
+       if (mmstarted)\r
+               MM_Shutdown ();\r
+\r
+\r
+       mmstarted = true;\r
+       bombonerror = true;\r
+//\r
+// set up the linked list (everything in the free list;\r
+//\r
+       mmhead = NULL;\r
+       mmfree = &mmblocks[0];\r
+       for (i=0;i<MAXBLOCKS-1;i++)\r
+               mmblocks[i].next = &mmblocks[i+1];\r
+       mmblocks[i].next = NULL;\r
+\r
+//\r
+// locked block of all memory until we punch out free space\r
+//\r
+       GETNEWBLOCK;\r
+       mmhead = mmnew;                         // this will allways be the first node\r
+       mmnew->start = 0;\r
+       mmnew->length = 0xffff;\r
+       mmnew->attributes = LOCKBIT;\r
+       mmnew->next = NULL;\r
+       mmrover = mmhead;\r
+\r
+\r
+//\r
+// get all available near conventional memory segments\r
+//\r
+       length=coreleft();\r
+       start = (void far *)(nearheap = malloc(length));\r
+\r
+       length -= 16-(FP_OFF(start)&15);\r
+       length -= SAVENEARHEAP;\r
+       seglength = length / 16;                        // now in paragraphs\r
+       segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;\r
+       MML_UseSpace (segstart,seglength);\r
+       mminfo.nearheap = length;\r
+\r
+//\r
+// get all available far conventional memory segments\r
+//\r
+       length=farcoreleft();\r
+       start = farheap = farmalloc(length);\r
+       length -= 16-(FP_OFF(start)&15);\r
+       length -= SAVEFARHEAP;\r
+       seglength = length / 16;                        // now in paragraphs\r
+       segstart = FP_SEG(start)+(FP_OFF(start)+15)/16;\r
+       MML_UseSpace (segstart,seglength);\r
+       mminfo.farheap = length;\r
+       mminfo.mainmem = mminfo.nearheap + mminfo.farheap;\r
+\r
+\r
+//\r
+// detect EMS and allocate up to 64K at page frame\r
+//\r
+       mminfo.EMSmem = 0;\r
+       for (i = 1;i < _argc;i++)\r
+       {\r
+               if ( US_CheckParm(_argv[i],ParmStrings) == 0)\r
+                       goto emsskip;                           // param NOEMS\r
+       }\r
+\r
+       if (MML_CheckForEMS())\r
+       {\r
+               MML_SetupEMS();                                 // allocate space\r
+               MML_UseSpace (EMSpageframe,EMSpagesmapped*0x400);\r
+               MM_MapEMS();                                    // map in used pages\r
+               mminfo.EMSmem = EMSpagesmapped*0x4000l;\r
+       }\r
+\r
+//\r
+// detect XMS and get upper memory blocks\r
+//\r
+emsskip:\r
+       mminfo.XMSmem = 0;\r
+       for (i = 1;i < _argc;i++)\r
+       {\r
+               if ( US_CheckParm(_argv[i],ParmStrings) == 0)\r
+                       goto xmsskip;                           // param NOXMS\r
+       }\r
+\r
+       if (MML_CheckForXMS())\r
+               MML_SetupXMS();                                 // allocate as many UMBs as possible\r
+\r
+//\r
+// allocate the misc buffer\r
+//\r
+xmsskip:\r
+       mmrover = mmhead;               // start looking for space after low block\r
+\r
+       MM_GetPtr (&bufferseg,BUFFERSIZE);\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= MM_Shutdown\r
+=\r
+= Frees all conventional, EMS, and XMS allocated\r
+=\r
+====================\r
+*/\r
+\r
+void MM_Shutdown (void)\r
+{\r
+  if (!mmstarted)\r
+       return;\r
+\r
+  farfree (farheap);\r
+  free (nearheap);\r
+  MML_ShutdownEMS ();\r
+  MML_ShutdownXMS ();\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= MM_GetPtr\r
+=\r
+= Allocates an unlocked, unpurgable block\r
+=\r
+====================\r
+*/\r
+\r
+void MM_GetPtr (memptr *baseptr,unsigned long size)\r
+{\r
+       mmblocktype far *scan,far *lastscan,far *endscan\r
+                               ,far *purge,far *next;\r
+       int                     search;\r
+       unsigned        needed,startseg;\r
+\r
+       needed = (size+15)/16;          // convert size from bytes to paragraphs\r
+\r
+       GETNEWBLOCK;                            // fill in start and next after a spot is found\r
+       mmnew->length = needed;\r
+       mmnew->useptr = baseptr;\r
+       mmnew->attributes = BASEATTRIBUTES;\r
+\r
+       for (search = 0; search<3; search++)\r
+       {\r
+       //\r
+       // first search:        try to allocate right after the rover, then on up\r
+       // second search:       search from the head pointer up to the rover\r
+       // third search:        compress memory, then scan from start\r
+               if (search == 1 && mmrover == mmhead)\r
+                       search++;\r
+\r
+               switch (search)\r
+               {\r
+               case 0:\r
+                       lastscan = mmrover;\r
+                       scan = mmrover->next;\r
+                       endscan = NULL;\r
+                       break;\r
+               case 1:\r
+                       lastscan = mmhead;\r
+                       scan = mmhead->next;\r
+                       endscan = mmrover;\r
+                       break;\r
+               case 2:\r
+                       MM_SortMem ();\r
+                       lastscan = mmhead;\r
+                       scan = mmhead->next;\r
+                       endscan = NULL;\r
+                       break;\r
+               }\r
+\r
+               startseg = lastscan->start + lastscan->length;\r
+\r
+               while (scan != endscan)\r
+               {\r
+                       if (scan->start - startseg >= needed)\r
+                       {\r
+                       //\r
+                       // got enough space between the end of lastscan and\r
+                       // the start of scan, so throw out anything in the middle\r
+                       // and allocate the new block\r
+                       //\r
+                               purge = lastscan->next;\r
+                               lastscan->next = mmnew;\r
+                               mmnew->start = *(unsigned *)baseptr = startseg;\r
+                               mmnew->next = scan;\r
+                               while ( purge != scan)\r
+                               {       // free the purgable block\r
+                                       next = purge->next;\r
+                                       FREEBLOCK(purge);\r
+                                       purge = next;           // purge another if not at scan\r
+                               }\r
+                               mmrover = mmnew;\r
+                               return; // good allocation!\r
+                       }\r
+\r
+                       //\r
+                       // if this block is purge level zero or locked, skip past it\r
+                       //\r
+                       if ( (scan->attributes & LOCKBIT)\r
+                               || !(scan->attributes & PURGEBITS) )\r
+                       {\r
+                               lastscan = scan;\r
+                               startseg = lastscan->start + lastscan->length;\r
+                       }\r
+\r
+\r
+                       scan=scan->next;                // look at next line\r
+               }\r
+       }\r
+\r
+       if (bombonerror)\r
+               Quit (OUT_OF_MEM_MSG,(size-mminfo.nearheap));\r
+       else\r
+               mmerror = true;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= MM_FreePtr\r
+=\r
+= Allocates an unlocked, unpurgable block\r
+=\r
+====================\r
+*/\r
+\r
+void MM_FreePtr (memptr *baseptr)\r
+{\r
+       mmblocktype far *scan,far *last;\r
+\r
+       last = mmhead;\r
+       scan = last->next;\r
+\r
+       if (baseptr == mmrover->useptr) // removed the last allocated block\r
+               mmrover = mmhead;\r
+\r
+       while (scan->useptr != baseptr && scan)\r
+       {\r
+               last = scan;\r
+               scan = scan->next;\r
+       }\r
+\r
+       if (!scan)\r
+               Quit ("MM_FreePtr: Block not found!");\r
+\r
+       last->next = scan->next;\r
+\r
+       FREEBLOCK(scan);\r
+}\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= MM_SetPurge\r
+=\r
+= Sets the purge level for a block (locked blocks cannot be made purgable)\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_SetPurge (memptr *baseptr, int purge)\r
+{\r
+       mmblocktype far *start;\r
+\r
+       start = mmrover;\r
+\r
+       do\r
+       {\r
+               if (mmrover->useptr == baseptr)\r
+                       break;\r
+\r
+               mmrover = mmrover->next;\r
+\r
+               if (!mmrover)\r
+                       mmrover = mmhead;\r
+               else if (mmrover == start)\r
+                       Quit ("MM_SetPurge: Block not found!");\r
+\r
+       } while (1);\r
+\r
+       mmrover->attributes &= ~PURGEBITS;\r
+       mmrover->attributes |= purge;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= MM_SetLock\r
+=\r
+= Locks / unlocks the block\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_SetLock (memptr *baseptr, boolean locked)\r
+{\r
+       mmblocktype far *start;\r
+\r
+       start = mmrover;\r
+\r
+       do\r
+       {\r
+               if (mmrover->useptr == baseptr)\r
+                       break;\r
+\r
+               mmrover = mmrover->next;\r
+\r
+               if (!mmrover)\r
+                       mmrover = mmhead;\r
+               else if (mmrover == start)\r
+                       Quit ("MM_SetLock: Block not found!");\r
+\r
+       } while (1);\r
+\r
+       mmrover->attributes &= ~LOCKBIT;\r
+       mmrover->attributes |= locked*LOCKBIT;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= MM_SortMem\r
+=\r
+= Throws out all purgable stuff and compresses movable blocks\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_SortMem (void)\r
+{\r
+       mmblocktype far *scan,far *last,far *next;\r
+       unsigned        start,length,source,dest,oldborder;\r
+       int                     playing;\r
+\r
+       //\r
+       // lock down a currently playing sound\r
+       //\r
+       playing = SD_SoundPlaying ();\r
+       if (playing)\r
+       {\r
+               switch (SoundMode)\r
+               {\r
+               case sdm_PC:\r
+                       playing += STARTPCSOUNDS;\r
+                       break;\r
+               case sdm_AdLib:\r
+                       playing += STARTADLIBSOUNDS;\r
+                       break;\r
+               }\r
+               MM_SetLock(&(memptr)audiosegs[playing],true);\r
+       }\r
+\r
+\r
+       SD_StopSound();\r
+//     oldborder = bordercolor;\r
+//     VW_ColorBorder (15);\r
+\r
+       if (beforesort)\r
+               beforesort();\r
+\r
+       scan = mmhead;\r
+\r
+       last = NULL;            // shut up compiler warning\r
+\r
+       while (scan)\r
+       {\r
+               if (scan->attributes & LOCKBIT)\r
+               {\r
+               //\r
+               // block is locked, so try to pile later blocks right after it\r
+               //\r
+                       start = scan->start + scan->length;\r
+               }\r
+               else\r
+               {\r
+                       if (scan->attributes & PURGEBITS)\r
+                       {\r
+                       //\r
+                       // throw out the purgable block\r
+                       //\r
+                               next = scan->next;\r
+                               FREEBLOCK(scan);\r
+                               last->next = next;\r
+                               scan = next;\r
+                               continue;\r
+                       }\r
+                       else\r
+                       {\r
+                       //\r
+                       // push the non purgable block on top of the last moved block\r
+                       //\r
+                               if (scan->start != start)\r
+                               {\r
+                                       length = scan->length;\r
+                                       source = scan->start;\r
+                                       dest = start;\r
+                                       while (length > 0xf00)\r
+                                       {\r
+                                               movedata(source,0,dest,0,0xf00*16);\r
+                                               length -= 0xf00;\r
+                                               source += 0xf00;\r
+                                               dest += 0xf00;\r
+                                       }\r
+                                       movedata(source,0,dest,0,length*16);\r
+\r
+                                       scan->start = start;\r
+                                       *(unsigned *)scan->useptr = start;\r
+                               }\r
+                               start = scan->start + scan->length;\r
+                       }\r
+               }\r
+\r
+               last = scan;\r
+               scan = scan->next;              // go to next block\r
+       }\r
+\r
+       mmrover = mmhead;\r
+\r
+       if (aftersort)\r
+               aftersort();\r
+\r
+//     VW_ColorBorder (oldborder);\r
+\r
+       if (playing)\r
+               MM_SetLock(&(memptr)audiosegs[playing],false);\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+#if 0\r
+/*\r
+=====================\r
+=\r
+= MM_ShowMemory\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_ShowMemory (void)\r
+{\r
+       mmblocktype far *scan;\r
+       unsigned color,temp;\r
+       long    end,owner;\r
+       char    scratch[80],str[10];\r
+\r
+       VW_SetDefaultColors();\r
+       VW_SetLineWidth(40);\r
+       temp = bufferofs;\r
+       bufferofs = 0;\r
+       VW_SetScreen (0,0);\r
+\r
+       scan = mmhead;\r
+\r
+       end = -1;\r
+\r
+//CA_OpenDebug ();\r
+\r
+       while (scan)\r
+       {\r
+               if (scan->attributes & PURGEBITS)\r
+                       color = 5;              // dark purple = purgable\r
+               else\r
+                       color = 9;              // medium blue = non purgable\r
+               if (scan->attributes & LOCKBIT)\r
+                       color = 12;             // red = locked\r
+               if (scan->start<=end)\r
+                       Quit ("MM_ShowMemory: Memory block order currupted!");\r
+               end = scan->start+scan->length-1;\r
+               VW_Hlin(scan->start,(unsigned)end,0,color);\r
+               VW_Plot(scan->start,0,15);\r
+               if (scan->next->start > end+1)\r
+                       VW_Hlin(end+1,scan->next->start,0,0);   // black = free\r
+\r
+#if 0\r
+strcpy (scratch,"Size:");\r
+ltoa ((long)scan->length*16,str,10);\r
+strcat (scratch,str);\r
+strcat (scratch,"\tOwner:0x");\r
+owner = (unsigned)scan->useptr;\r
+ultoa (owner,str,16);\r
+strcat (scratch,str);\r
+strcat (scratch,"\n");\r
+write (debughandle,scratch,strlen(scratch));\r
+#endif\r
+\r
+               scan = scan->next;\r
+       }\r
+\r
+//CA_CloseDebug ();\r
+\r
+       IN_Ack();\r
+       VW_SetLineWidth(64);\r
+       bufferofs = temp;\r
+}\r
+#endif\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MM_UnusedMemory\r
+=\r
+= Returns the total free space without purging\r
+=\r
+======================\r
+*/\r
+\r
+long MM_UnusedMemory (void)\r
+{\r
+       unsigned free;\r
+       mmblocktype far *scan;\r
+\r
+       free = 0;\r
+       scan = mmhead;\r
+\r
+       while (scan->next)\r
+       {\r
+               free += scan->next->start - (scan->start + scan->length);\r
+               scan = scan->next;\r
+       }\r
+\r
+       return free*16l;\r
+}\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= MM_TotalFree\r
+=\r
+= Returns the total free space with purging\r
+=\r
+======================\r
+*/\r
+\r
+long MM_TotalFree (void)\r
+{\r
+       unsigned free;\r
+       mmblocktype far *scan;\r
+\r
+       free = 0;\r
+       scan = mmhead;\r
+\r
+       while (scan->next)\r
+       {\r
+               if ((scan->attributes&PURGEBITS) && !(scan->attributes&LOCKBIT))\r
+                       free += scan->length;\r
+               free += scan->next->start - (scan->start + scan->length);\r
+               scan = scan->next;\r
+       }\r
+\r
+       return free*16l;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= MM_BombOnError\r
+=\r
+=====================\r
+*/\r
+\r
+void MM_BombOnError (boolean bomb)\r
+{\r
+       bombonerror = bomb;\r
+}\r
+\r
+\r
diff --git a/16/cawat/ID_MM.H b/16/cawat/ID_MM.H
new file mode 100644 (file)
index 0000000..968c5a7
--- /dev/null
@@ -0,0 +1,110 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_MM.H
+
+#ifndef __ID_EXMM__\r
+\r
+#define __ID_EXMM__
+
+#include "LIB_HEAD.H"\r
+\r
+#define SAVENEARHEAP   0x400           // space to leave in data segment\r
+#define SAVEFARHEAP            0                       // space to leave in far heap\r
+\r
+#define        BUFFERSIZE              0x1000          // miscelanious, allways available buffer\r
+\r
+#define MAXBLOCKS              600\r
+\r
+\r
+//--------\r
+\r
+#define        EMS_INT                 0x67\r
+\r
+#define        EMS_STATUS              0x40\r
+#define        EMS_GETFRAME    0x41\r
+#define        EMS_GETPAGES    0x42\r
+#define        EMS_ALLOCPAGES  0x43\r
+#define        EMS_MAPPAGE             0x44\r
+#define        EMS_FREEPAGES   0x45\r
+#define        EMS_VERSION             0x46\r
+\r
+//--------\r
+\r
+#define        XMS_VERSION             0x00\r
+\r
+#define        XMS_ALLOCHMA    0x01\r
+#define        XMS_FREEHMA             0x02\r
+\r
+#define        XMS_GENABLEA20  0x03\r
+#define        XMS_GDISABLEA20 0x04\r
+#define        XMS_LENABLEA20  0x05\r
+#define        XMS_LDISABLEA20 0x06\r
+#define        XMS_QUERYA20    0x07\r
+\r
+#define        XMS_QUERYREE    0x08\r
+#define        XMS_ALLOC               0x09\r
+#define        XMS_FREE                0x0A\r
+#define        XMS_MOVE                0x0B\r
+#define        XMS_LOCK                0x0C\r
+#define        XMS_UNLOCK              0x0D\r
+#define        XMS_GETINFO             0x0E\r
+#define        XMS_RESIZE              0x0F\r
+\r
+#define        XMS_ALLOCUMB    0x10\r
+#define        XMS_FREEUMB             0x11\r
+\r
+//==========================================================================\r
+\r
+typedef void /*_seg*/ * memptr;\r
+\r
+typedef struct\r
+{\r
+       long    nearheap,farheap,EMSmem,XMSmem,mainmem;\r
+} mminfotype;\r
+\r
+//==========================================================================\r
+\r
+extern mminfotype      mminfo;\r
+extern memptr          bufferseg;\r
+extern boolean         mmerror;\r
+\r
+extern void            (* beforesort) (void);\r
+extern void            (* aftersort) (void);\r
+\r
+//==========================================================================\r
+\r
+void MM_Startup (void);\r
+void MM_Shutdown (void);\r
+void MM_MapEMS (void);\r
+\r
+void MM_GetPtr (memptr *baseptr,unsigned long size);\r
+void MM_FreePtr (memptr *baseptr);\r
+\r
+void MM_SetPurge (memptr *baseptr, int purge);\r
+void MM_SetLock (memptr *baseptr, boolean locked);\r
+void MM_SortMem (void);\r
+\r
+void MM_ShowMemory (void);\r
+\r
+long MM_UnusedMemory (void);\r
+long MM_TotalFree (void);\r
+\r
+void MM_BombOnError (boolean bomb);
+
+#endif
diff --git a/16/cawat/ID_RF.C b/16/cawat/ID_RF.C
new file mode 100644 (file)
index 0000000..62dfa91
--- /dev/null
@@ -0,0 +1,2845 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_RF.C\r
+\r
+/*\r
+=============================================================================\r
+\r
+notes\r
+-----\r
+\r
+scrolling more than one tile / refresh forces a total redraw\r
+\r
+two overlapping sprites of equal priority can change drawing order when\r
+updated\r
+\r
+=============================================================================\r
+*/\r
+\r
+#include "ID_HEADS.H"\r
+#pragma hdrstop\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define        SCREENTILESWIDE 20\r
+#define        SCREENTILESHIGH 13\r
+\r
+#define        SCREENSPACE             (SCREENWIDTH*240)\r
+#define FREEEGAMEM             (0x10000l-3l*SCREENSPACE)\r
+\r
+//\r
+// the update array must have enough space for two screens that can float\r
+// up two two tiles each way\r
+//\r
+// (PORTTILESWIDE+1)*PORTTILESHIGH must be even so the arrays can be cleared\r
+// by word width instructions\r
+\r
+#define        UPDATESCREENSIZE        (UPDATEWIDE*PORTTILESHIGH+2)\r
+#define        UPDATESPARESIZE         (UPDATEWIDE*2+4)\r
+#define UPDATESIZE                     (UPDATESCREENSIZE+2*UPDATESPARESIZE)\r
+\r
+#define G_EGASX_SHIFT  7       // global >> ?? = screen x\r
+#define G_CGASX_SHIFT  6       // global >> ?? = screen x\r
+#define G_SY_SHIFT             4       // global >> ?? = screen y\r
+\r
+unsigned       SX_T_SHIFT;             // screen x >> ?? = tile EGA = 1, CGA = 2;\r
+#define        SY_T_SHIFT              4       // screen y >> ?? = tile\r
+\r
+\r
+#define        EGAPORTSCREENWIDE       42\r
+#define        CGAPORTSCREENWIDE       84\r
+#define        PORTSCREENHIGH          224\r
+\r
+#define        UPDATESCREENSIZE        (UPDATEWIDE*PORTTILESHIGH+2)\r
+#define        UPDATESPARESIZE         (UPDATEWIDE*2+4)\r
+#define UPDATESIZE                     (UPDATESCREENSIZE+2*UPDATESPARESIZE)\r
+\r
+#define MAXSCROLLEDGES 6\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                  LOCAL TYPES\r
+\r
+=============================================================================\r
+*/\r
+\r
+typedef        struct spriteliststruct\r
+{\r
+       int                     screenx,screeny;\r
+       int                     width,height;\r
+\r
+       unsigned        grseg,sourceofs,planesize;\r
+       drawtype        draw;\r
+       unsigned        tilex,tiley,tilewide,tilehigh;\r
+       int                     priority,updatecount;\r
+       struct spriteliststruct **prevptr,*nextsprite;\r
+} spritelisttype;\r
+\r
+\r
+typedef struct\r
+{\r
+       int                     screenx,screeny;\r
+       int                     width,height;\r
+} eraseblocktype;\r
+\r
+\r
+typedef struct\r
+{\r
+       unsigned        current;                // foreground tiles have high bit set\r
+       int                     count;\r
+} tiletype;\r
+\r
+\r
+typedef struct animtilestruct\r
+{\r
+       unsigned        x,y,tile;\r
+       tiletype        *chain;\r
+       unsigned        far *mapplane;\r
+       struct animtilestruct **prevptr,*nexttile;\r
+} animtiletype;\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+unsigned       tics;\r
+long           lasttimecount;\r
+\r
+boolean                compatability;                  // crippled refresh for wierdo SVGAs\r
+\r
+unsigned       mapwidth,mapheight,mapbyteswide,mapwordswide\r
+                       ,mapbytesextra,mapwordsextra;\r
+unsigned       mapbwidthtable[MAXMAPHEIGHT];\r
+\r
+//\r
+// Global : Actor coordinates are in this, at 1/16 th of a pixel, to allow\r
+// for fractional movement and acceleration.\r
+//\r
+// Tiles  : Tile offsets from the upper left corner of the current map.\r
+//\r
+// Screen : Graphics level offsets from map origin, x in bytes, y in pixels.\r
+// originxscreen is the same spot as originxtile, just with extra precision\r
+// so graphics don't need to be done in tile boundaries.\r
+//\r
+\r
+unsigned       originxglobal,originyglobal;\r
+unsigned       originxtile,originytile;\r
+unsigned       originxscreen,originyscreen;\r
+unsigned       originmap;\r
+unsigned       originxmin,originxmax,originymin,originymax;\r
+\r
+unsigned       masterofs;\r
+\r
+//\r
+// Table of the offsets from bufferofs of each tile spot in the\r
+// view port.  The extra wide tile should never be drawn, but the space\r
+// is needed to account for the extra 0 in the update arrays.  Built by\r
+// RF_Startup\r
+//\r
+\r
+unsigned       blockstarts[UPDATEWIDE*UPDATEHIGH];\r
+unsigned       updatemapofs[UPDATEWIDE*UPDATEHIGH];\r
+\r
+unsigned       uwidthtable[PORTTILESHIGH];             // lookup instead of multiply\r
+\r
+byte           update[2][UPDATESIZE];\r
+byte           *updateptr,*baseupdateptr,                                              // current start of update window\r
+                       *updatestart[2],\r
+                       *baseupdatestart[2];\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+static         char    scratch[20],str[80];\r
+\r
+tiletype       allanims[MAXANIMTYPES];\r
+unsigned       numanimchains;\r
+\r
+void           (*refreshvector) (void);\r
+\r
+unsigned       screenstart[3] =\r
+       {0,SCREENSPACE,SCREENSPACE*2};\r
+\r
+unsigned       xpanmask;                       // prevent panning to odd pixels\r
+\r
+unsigned       screenpage;                     // screen currently being displayed\r
+unsigned       otherpage;\r
+\r
+\r
+spritelisttype spritearray[MAXSPRITES],*prioritystart[PRIORITIES],\r
+                               *spritefreeptr;\r
+\r
+animtiletype   animarray[MAXANIMTILES],*animhead,*animfreeptr;\r
+\r
+int                            animfreespot;\r
+\r
+eraseblocktype eraselist[2][MAXSPRITES],*eraselistptr[2];\r
+\r
+int            hscrollblocks,vscrollblocks;\r
+int            hscrolledge[MAXSCROLLEDGES],vscrolledge[MAXSCROLLEDGES];\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL PROTOTYPES\r
+\r
+=============================================================================\r
+*/\r
+\r
+void RFL_NewTile (unsigned updateoffset);\r
+void RFL_MaskForegroundTiles (void);\r
+void RFL_UpdateTiles (void);\r
+\r
+void RFL_BoundScroll (int x, int y);\r
+void RFL_CalcOriginStuff (long x, long y);\r
+void RFL_ClearScrollBlocks (void);\r
+void RFL_InitSpriteList (void);\r
+void RFL_InitAnimList (void);\r
+void RFL_CheckForAnimTile (unsigned x, unsigned y);\r
+void RFL_AnimateTiles (void);\r
+void RFL_RemoveAnimsOnX (unsigned x);\r
+void RFL_RemoveAnimsOnY (unsigned y);\r
+void RFL_EraseBlocks (void);\r
+void RFL_UpdateSprites (void);\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                        GRMODE INDEPENDANT ROUTINES\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_Startup\r
+=\r
+=====================\r
+*/\r
+\r
+static char *ParmStrings[] = {"comp",""};\r
+\r
+void RF_Startup (void)\r
+{\r
+       int i,x,y;\r
+       unsigned        *blockstart;\r
+\r
+       if (grmode == EGAGR)\r
+               for (i = 1;i < _argc;i++)\r
+                       if (US_CheckParm(_argv[i],ParmStrings) == 0)\r
+                       {\r
+                               compatability = true;\r
+                               break;\r
+                       }\r
+\r
+       for (i=0;i<PORTTILESHIGH;i++)\r
+               uwidthtable[i] = UPDATEWIDE*i;\r
+\r
+       originxmin = originymin = MAPBORDER*TILEGLOBAL;\r
+\r
+       eraselistptr[0] = &eraselist[0][0];\r
+       eraselistptr[1] = &eraselist[1][0];\r
+\r
+\r
+\r
+       if (grmode == EGAGR)\r
+       {\r
+               SX_T_SHIFT = 1;\r
+\r
+               baseupdatestart[0] = &update[0][UPDATESPARESIZE];\r
+               baseupdatestart[1] = &update[1][UPDATESPARESIZE];\r
+\r
+               screenpage = 0;\r
+               otherpage = 1;\r
+               displayofs = screenstart[screenpage];\r
+               bufferofs = screenstart[otherpage];\r
+               masterofs = screenstart[2];\r
+\r
+               updateptr = baseupdatestart[otherpage];\r
+\r
+               blockstart = &blockstarts[0];\r
+               for (y=0;y<UPDATEHIGH;y++)\r
+                       for (x=0;x<UPDATEWIDE;x++)\r
+                               *blockstart++ = SCREENWIDTH*16*y+x*TILEWIDTH;\r
+\r
+               xpanmask = 6;   // dont pan to odd pixels\r
+       }\r
+\r
+       else if (grmode == CGAGR)\r
+       {\r
+               SX_T_SHIFT = 2;\r
+\r
+               updateptr = baseupdateptr = &update[0][UPDATESPARESIZE];\r
+\r
+               bufferofs = 0;\r
+               masterofs = 0x8000;\r
+\r
+               blockstart = &blockstarts[0];\r
+               for (y=0;y<UPDATEHIGH;y++)\r
+                       for (x=0;x<UPDATEWIDE;x++)\r
+                               *blockstart++ = SCREENWIDTH*16*y+x*TILEWIDTH;\r
+       }\r
+}\r
+\r
+\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_Shutdown\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_Shutdown (void)\r
+{\r
+\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_FixOfs\r
+=\r
+= Sets bufferofs,displayofs, and masterofs to regular values, for the\r
+= occasions when you have moved them around manually\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_FixOfs (void)\r
+{\r
+       if (grmode == EGAGR)\r
+       {\r
+               screenpage = 0;\r
+               otherpage = 1;\r
+               panx = pany = pansx = pansy = panadjust = 0;\r
+               displayofs = screenstart[screenpage];\r
+               bufferofs = screenstart[otherpage];\r
+               masterofs = screenstart[2];\r
+               VW_SetScreen (displayofs,0);\r
+       }\r
+       else\r
+       {\r
+               bufferofs = 0;\r
+               masterofs = 0x8000;\r
+       }\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_NewMap\r
+=\r
+= Makes some convienient calculations based on maphead->\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_NewMap (void)\r
+{\r
+       int i,x,y;\r
+       unsigned spot,*table;\r
+\r
+       mapwidth = mapheaderseg[mapon]->width;\r
+       mapbyteswide = 2*mapwidth;\r
+       mapheight = mapheaderseg[mapon]->height;\r
+       mapwordsextra = mapwidth-PORTTILESWIDE;\r
+       mapbytesextra = 2*mapwordsextra;\r
+\r
+//\r
+// make a lookup table for the maps left edge\r
+//\r
+       if (mapheight > MAXMAPHEIGHT)\r
+               Quit ("RF_NewMap: Map too tall!");\r
+       spot = 0;\r
+       for (i=0;i<mapheight;i++)\r
+       {\r
+         mapbwidthtable[i] = spot;\r
+         spot += mapbyteswide;\r
+       }\r
+\r
+//\r
+// fill in updatemapofs with the new width info\r
+//\r
+       table = &updatemapofs[0];\r
+       for (y=0;y<PORTTILESHIGH;y++)\r
+               for (x=0;x<UPDATEWIDE;x++)\r
+                       *table++ = mapbwidthtable[y]+x*2;\r
+\r
+//\r
+// the y max value clips off the bottom half of a tile so a map that is\r
+// 13 + MAPBORDER*2 tile high will not scroll at all vertically\r
+//\r
+       originxmax = (mapwidth-MAPBORDER-SCREENTILESWIDE)*TILEGLOBAL;\r
+       originymax = (mapheight-MAPBORDER-SCREENTILESHIGH)*TILEGLOBAL;\r
+       if (originxmax<originxmin)              // for very small maps\r
+               originxmax=originxmin;\r
+       if (originymax<originymin)\r
+               originymax=originymin;\r
+\r
+//\r
+// clear out the lists\r
+//\r
+       RFL_InitSpriteList ();\r
+       RFL_InitAnimList ();\r
+       RFL_ClearScrollBlocks ();\r
+       RF_SetScrollBlock (0,MAPBORDER-1,true);\r
+       RF_SetScrollBlock (0,mapheight-MAPBORDER,true);\r
+       RF_SetScrollBlock (MAPBORDER-1,0,false);\r
+       RF_SetScrollBlock (mapwidth-MAPBORDER,0,false);\r
+\r
+\r
+       lasttimecount = TimeCount;              // setup for adaptive timing\r
+       tics = 1;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+==========================\r
+=\r
+= RF_MarkTileGraphics\r
+=\r
+= Goes through mapplane[0/1] and marks all background/foreground tiles\r
+= needed, then follows all animation sequences to make sure animated\r
+= tiles get all the stages.  Every unique animating tile is given an\r
+= entry in allanims[], so every instance of that tile will animate at the\r
+= same rate.  The info plane for each animating tile will hold a pointer\r
+= into allanims[], therefore you can't have both an animating foreground\r
+= and background tile in the same spot!\r
+=\r
+==========================\r
+*/\r
+\r
+void RF_MarkTileGraphics (void)\r
+{\r
+       unsigned        size;\r
+       int                     tile,next,anims,change;\r
+       unsigned        far     *start,far *end,far *info;\r
+       unsigned        i,tilehigh;\r
+       char            str[80],str2[10];\r
+\r
+       memset (allanims,0,sizeof(allanims));\r
+       numanimchains = 0;\r
+\r
+       size = mapwidth*mapheight;\r
+\r
+//\r
+// background plane\r
+//\r
+       start = mapsegs[0];\r
+       info = mapsegs[2];\r
+       end = start+size;\r
+       do\r
+       {\r
+               tile = *start++;\r
+               if (tile>=0)                    // <0 is a tile that is never drawn\r
+               {\r
+                       CA_MarkGrChunk(STARTTILE16+tile);\r
+                       if (tinf[ANIM+tile])\r
+                       {\r
+                               // this tile will animated\r
+\r
+                               if (tinf[SPEED+tile])\r
+                               {\r
+                                       if (!tinf[ANIM+tile])\r
+                                       {\r
+                                               strcpy (str,"RF_MarkTileGraphics: Background anim of 0:");\r
+                                               itoa (tile,str2,10);\r
+                                               strcat (str,str2);\r
+                                               Quit (str);\r
+                                       }\r
+                                       for (i=0;i<numanimchains;i++)\r
+                                               if (allanims[i].current == tile)\r
+                                               {\r
+                                                       *info = (unsigned)&allanims[i];\r
+                                                       goto nextback;\r
+                                               }\r
+\r
+                                       // new chain of animating tiles\r
+\r
+                                       if (i>=MAXANIMTYPES)\r
+                                               Quit ("RF_MarkTileGraphics: Too many unique animated tiles!");\r
+                                       allanims[i].current = tile;\r
+                                       allanims[i].count = tinf[SPEED+tile];\r
+                                       *info = (unsigned)&allanims[i];\r
+                                       numanimchains++;\r
+                               }\r
+\r
+                               anims = 0;\r
+                               change = (signed char)(tinf[ANIM+tile]);\r
+                               next = tile+change;\r
+                               while (change && next != tile)\r
+                               {\r
+                                       CA_MarkGrChunk(STARTTILE16+next);\r
+                                       change = (signed char)(tinf[ANIM+next]);\r
+                                       next += change;\r
+                                       if (++anims > 20)\r
+                                       {\r
+                                               strcpy (str,"RF_MarkTileGraphics: Unending background animation:");\r
+                                               itoa (next,str2,10);\r
+                                               strcat (str,str2);\r
+                                               Quit (str);\r
+                                       }\r
+                               }\r
+\r
+                       }\r
+               }\r
+nextback:\r
+               info++;\r
+       } while (start<end);\r
+\r
+//\r
+// foreground plane\r
+//\r
+       start = mapsegs[1];\r
+       info = mapsegs[2];\r
+       end = start+size;\r
+       do\r
+       {\r
+               tile = *start++;\r
+               if (tile>=0)                    // <0 is a tile that is never drawn\r
+               {\r
+                       CA_MarkGrChunk(STARTTILE16M+tile);\r
+                       if (tinf[MANIM+tile])\r
+                       {\r
+                               // this tile will animated\r
+\r
+                               if (tinf[MSPEED+tile])\r
+                               {\r
+                                       if (!tinf[MANIM+tile])\r
+                                       {\r
+                                               strcpy (str,"RF_MarkTileGraphics: Foreground anim of 0:");\r
+                                               itoa (tile,str2,10);\r
+                                               strcat (str,str2);\r
+                                               Quit (str);\r
+                                       }\r
+                                       tilehigh = tile | 0x8000;       // foreground tiles have high bit\r
+                                       for (i=0;i<numanimchains;i++)\r
+                                               if (allanims[i].current == tilehigh)\r
+                                               {\r
+                                                       *info = (unsigned)&allanims[i];\r
+                                                       goto nextfront;\r
+                                               }\r
+\r
+                                       // new chain of animating tiles\r
+\r
+                                       if (i>=MAXANIMTYPES)\r
+                                               Quit ("RF_MarkTileGraphics: Too many unique animated tiles!");\r
+                                       allanims[i].current = tilehigh;\r
+                                       allanims[i].count = tinf[MSPEED+tile];\r
+\r
+                                       *info = (unsigned)&allanims[i];\r
+                                       numanimchains++;\r
+                               }\r
+\r
+                               anims = 0;\r
+                               change = (signed char)(tinf[MANIM+tile]);\r
+                               next = tile+change;\r
+                               while (change && next != tile)\r
+                               {\r
+                                       CA_MarkGrChunk(STARTTILE16M+next);\r
+                                       change = (signed char)(tinf[MANIM+next]);\r
+                                       next += change;\r
+                                       if (++anims > 20)\r
+                                       {\r
+                                               strcpy (str,"RF_MarkTileGraphics: Unending foreground animation:");\r
+                                               itoa (next,str2,10);\r
+                                               strcat (str,str2);\r
+                                               Quit (str);\r
+                                       }\r
+                               }\r
+\r
+                       }\r
+               }\r
+nextfront:\r
+               info++;\r
+       } while (start<end);\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=========================\r
+=\r
+= RFL_InitAnimList\r
+=\r
+= Call to clear out the entire animating tile list and return all of them to\r
+= the free list.\r
+=\r
+=========================\r
+*/\r
+\r
+void RFL_InitAnimList (void)\r
+{\r
+       int     i;\r
+\r
+       animfreeptr = &animarray[0];\r
+\r
+       for (i=0;i<MAXANIMTILES-1;i++)\r
+               animarray[i].nexttile = &animarray[i+1];\r
+\r
+       animarray[i].nexttile = NULL;\r
+\r
+       animhead = NULL;                        // nothing in list\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_CheckForAnimTile\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_CheckForAnimTile (unsigned x, unsigned y)\r
+{\r
+       unsigned        tile,offset,speed,lasttime,thistime,timemissed;\r
+       unsigned        far *map;\r
+       animtiletype    *anim,*next;\r
+\r
+// the info plane of each animating tile has a near pointer into allanims[]\r
+// which gives the current state of all concurrently animating tiles\r
+\r
+       offset = mapbwidthtable[y]/2+x;\r
+\r
+//\r
+// background\r
+//\r
+       map = mapsegs[0]+offset;\r
+       tile = *map;\r
+       if (tinf[ANIM+tile] && tinf[SPEED+tile])\r
+       {\r
+               if (!animfreeptr)\r
+                       Quit ("RF_CheckForAnimTile: No free spots in tilearray!");\r
+               anim = animfreeptr;\r
+               animfreeptr = animfreeptr->nexttile;\r
+               next = animhead;                                // stick it at the start of the list\r
+               animhead = anim;\r
+               if (next)\r
+                       next->prevptr = &anim->nexttile;\r
+               anim->nexttile = next;\r
+               anim->prevptr = &animhead;\r
+\r
+               anim->x = x;\r
+               anim->y = y;\r
+               anim->tile = tile;\r
+               anim->mapplane = map;\r
+               anim->chain = (tiletype *)*(mapsegs[2]+offset);\r
+       }\r
+\r
+//\r
+// foreground\r
+//\r
+       map = mapsegs[1]+offset;\r
+       tile = *map;\r
+       if (tinf[MANIM+tile] && tinf[MSPEED+tile])\r
+       {\r
+               if (!animfreeptr)\r
+                       Quit ("RF_CheckForAnimTile: No free spots in tilearray!");\r
+               anim = animfreeptr;\r
+               animfreeptr = animfreeptr->nexttile;\r
+               next = animhead;                                // stick it at the start of the list\r
+               animhead = anim;\r
+               if (next)\r
+                       next->prevptr = &anim->nexttile;\r
+               anim->nexttile = next;\r
+               anim->prevptr = &animhead;\r
+\r
+               anim->x = x;\r
+               anim->y = y;\r
+               anim->tile = tile;\r
+               anim->mapplane = map;\r
+               anim->chain = (tiletype *)*(mapsegs[2]+offset);\r
+       }\r
+\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_RemoveAnimsOnX\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_RemoveAnimsOnX (unsigned x)\r
+{\r
+       animtiletype *current,*next;\r
+\r
+       current = animhead;\r
+       while (current)\r
+       {\r
+               if (current->x == x)\r
+               {\r
+                       *(void **)current->prevptr = current->nexttile;\r
+                       if (current->nexttile)\r
+                               current->nexttile->prevptr = current->prevptr;\r
+                       next = current->nexttile;\r
+                       current->nexttile = animfreeptr;\r
+                       animfreeptr = current;\r
+                       current = next;\r
+               }\r
+               else\r
+                       current = current->nexttile;\r
+       }\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_RemoveAnimsOnY\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_RemoveAnimsOnY (unsigned y)\r
+{\r
+       animtiletype *current,*next;\r
+\r
+       current = animhead;\r
+       while (current)\r
+       {\r
+               if (current->y == y)\r
+               {\r
+                       *(void **)current->prevptr = current->nexttile;\r
+                       if (current->nexttile)\r
+                               current->nexttile->prevptr = current->prevptr;\r
+                       next = current->nexttile;\r
+                       current->nexttile = animfreeptr;\r
+                       animfreeptr = current;\r
+                       current = next;\r
+               }\r
+               else\r
+                       current = current->nexttile;\r
+       }\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_RemoveAnimsInBlock\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_RemoveAnimsInBlock (unsigned x, unsigned y, unsigned width, unsigned height)\r
+{\r
+       animtiletype *current,*next;\r
+\r
+       current = animhead;\r
+       while (current)\r
+       {\r
+               if (current->x - x < width && current->y - y < height)\r
+               {\r
+                       *(void **)current->prevptr = current->nexttile;\r
+                       if (current->nexttile)\r
+                               current->nexttile->prevptr = current->prevptr;\r
+                       next = current->nexttile;\r
+                       current->nexttile = animfreeptr;\r
+                       animfreeptr = current;\r
+                       current = next;\r
+               }\r
+               else\r
+                       current = current->nexttile;\r
+       }\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_AnimateTiles\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_AnimateTiles (void)\r
+{\r
+       animtiletype *current;\r
+       unsigned        updateofs,tile,x,y;\r
+       tiletype        *anim;\r
+\r
+//\r
+// animate the lists of tiles\r
+//\r
+       anim = &allanims[0];\r
+       while (anim->current)\r
+       {\r
+               anim->count-=tics;\r
+               while ( anim->count < 1)\r
+               {\r
+                       if (anim->current & 0x8000)\r
+                       {\r
+                               tile = anim->current & 0x7fff;\r
+                               tile += (signed char)tinf[MANIM+tile];\r
+                               anim->count += tinf[MSPEED+tile];\r
+                               tile |= 0x8000;\r
+                       }\r
+                       else\r
+                       {\r
+                               tile = anim->current;\r
+                               tile += (signed char)tinf[ANIM+tile];\r
+                               anim->count += tinf[SPEED+tile];\r
+                       }\r
+                       anim->current = tile;\r
+               }\r
+               anim++;\r
+       }\r
+\r
+\r
+//\r
+// traverse the list of animating tiles\r
+//\r
+       current = animhead;\r
+       while (current)\r
+       {\r
+               tile =current->chain->current;\r
+               if ( tile != current->tile)\r
+               {\r
+               // tile has animated\r
+               //\r
+               // remove tile from master screen cache,\r
+               // change a tile to its next state, set the structure up for\r
+               // next animation, and post an update region to both update pages\r
+               //\r
+                       current->tile = tile;\r
+\r
+                       *(current->mapplane) = tile & 0x7fff;           // change in map\r
+\r
+                       x = current->x-originxtile;\r
+                       y = current->y-originytile;\r
+\r
+                       if (x>=PORTTILESWIDE || y>=PORTTILESHIGH)\r
+                               Quit ("RFL_AnimateTiles: Out of bounds!");\r
+\r
+                       updateofs = uwidthtable[y] + x;\r
+                       RFL_NewTile(updateofs);                         // puts "1"s in both pages\r
+               }\r
+               current = current->nexttile;\r
+       }\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=========================\r
+=\r
+= RFL_InitSpriteList\r
+=\r
+= Call to clear out the entire sprite list and return all of them to\r
+= the free list.\r
+=\r
+=========================\r
+*/\r
+\r
+void RFL_InitSpriteList (void)\r
+{\r
+       int     i;\r
+\r
+       spritefreeptr = &spritearray[0];\r
+       for (i=0;i<MAXSPRITES-1;i++)\r
+               spritearray[i].nextsprite = &spritearray[i+1];\r
+\r
+       spritearray[i].nextsprite = NULL;\r
+\r
+// NULL in all priority levels\r
+\r
+       memset (prioritystart,0,sizeof(prioritystart));\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=================\r
+=\r
+= RFL_CalcOriginStuff\r
+=\r
+= Calculate all the global variables for a new position\r
+= Long parms so position can be clipped to a maximum near 64k\r
+=\r
+=================\r
+*/\r
+\r
+void RFL_CalcOriginStuff (long x, long y)\r
+{\r
+       originxglobal = x;\r
+       originyglobal = y;\r
+       originxtile = originxglobal>>G_T_SHIFT;\r
+       originytile = originyglobal>>G_T_SHIFT;\r
+       originxscreen = originxtile<<SX_T_SHIFT;\r
+       originyscreen = originytile<<SY_T_SHIFT;\r
+       originmap = mapbwidthtable[originytile] + originxtile*2;\r
+\r
+#if GRMODE == EGAGR\r
+       panx = (originxglobal>>G_P_SHIFT) & 15;\r
+       pansx = panx & 8;\r
+       pany = pansy = (originyglobal>>G_P_SHIFT) & 15;\r
+       panadjust = panx/8 + ylookup[pany];\r
+#endif\r
+\r
+#if GRMODE == CGAGR\r
+       panx = (originxglobal>>G_P_SHIFT) & 15;\r
+       pansx = panx & 12;\r
+       pany = pansy = (originyglobal>>G_P_SHIFT) & 15;\r
+       panadjust = pansx/4 + ylookup[pansy];\r
+#endif\r
+\r
+}\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= RFL_ClearScrollBlocks\r
+=\r
+=================\r
+*/\r
+\r
+void RFL_ClearScrollBlocks (void)\r
+{\r
+       hscrollblocks = vscrollblocks = 0;\r
+}\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= RF_SetScrollBlock\r
+=\r
+= Sets a horizontal or vertical scroll block\r
+= a horizontal block is ----, meaning it blocks up/down movement\r
+=\r
+=================\r
+*/\r
+\r
+void RF_SetScrollBlock (int x, int y, boolean horizontal)\r
+{\r
+       if (horizontal)\r
+       {\r
+               hscrolledge[hscrollblocks] = y;\r
+               if (hscrollblocks++ == MAXSCROLLEDGES)\r
+                       Quit ("RF_SetScrollBlock: Too many horizontal scroll blocks");\r
+       }\r
+       else\r
+       {\r
+               vscrolledge[vscrollblocks] = x;\r
+               if (vscrollblocks++ == MAXSCROLLEDGES)\r
+                       Quit ("RF_SetScrollBlock: Too many vertical scroll blocks");\r
+       }\r
+}\r
+\r
+\r
+/*\r
+=================\r
+=\r
+= RFL_BoundScroll\r
+=\r
+= Bound a given x/y movement to scroll blocks\r
+=\r
+=================\r
+*/\r
+\r
+void RFL_BoundScroll (int x, int y)\r
+{\r
+       int     check,newxtile,newytile;\r
+\r
+       originxglobal += x;\r
+       originyglobal += y;\r
+\r
+       newxtile= originxglobal >> G_T_SHIFT;\r
+       newytile = originyglobal >> G_T_SHIFT;\r
+\r
+       if (x>0)\r
+       {\r
+               newxtile+=SCREENTILESWIDE;\r
+               for (check=0;check<vscrollblocks;check++)\r
+                       if (vscrolledge[check] == newxtile)\r
+                       {\r
+                               originxglobal = originxglobal&0xff00;\r
+                               break;\r
+                       }\r
+       }\r
+       else if (x<0)\r
+       {\r
+               for (check=0;check<vscrollblocks;check++)\r
+                       if (vscrolledge[check] == newxtile)\r
+                       {\r
+                               originxglobal = (originxglobal&0xff00)+0x100;\r
+                               break;\r
+                       }\r
+       }\r
+\r
+\r
+       if (y>0)\r
+       {\r
+               newytile+=SCREENTILESHIGH;\r
+               for (check=0;check<hscrollblocks;check++)\r
+                       if (hscrolledge[check] == newytile)\r
+                       {\r
+                               originyglobal = originyglobal&0xff00;\r
+                               break;\r
+                       }\r
+       }\r
+       else if (y<0)\r
+       {\r
+               for (check=0;check<hscrollblocks;check++)\r
+                       if (hscrolledge[check] == newytile)\r
+                       {\r
+                               originyglobal = (originyglobal&0xff00)+0x100;\r
+                               break;\r
+                       }\r
+       }\r
+\r
+\r
+       RFL_CalcOriginStuff (originxglobal, originyglobal);\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_SetRefreshHook\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_SetRefreshHook (void (*func) (void) )\r
+{\r
+       refreshvector = func;\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=================\r
+=\r
+= RFL_NewRow\r
+=\r
+= Bring a new row of tiles onto the port, spawning animating tiles\r
+=\r
+=================\r
+*/\r
+\r
+void   RFL_NewRow (int dir)\r
+{\r
+       unsigned count,updatespot,updatestep;\r
+       int             x,y,xstep,ystep;\r
+\r
+       switch (dir)\r
+       {\r
+       case 0:         // top row\r
+               updatespot = 0;\r
+               updatestep = 1;\r
+               x = originxtile;\r
+               y = originytile;\r
+               xstep = 1;\r
+               ystep = 0;\r
+               count = PORTTILESWIDE;\r
+               break;\r
+\r
+       case 1:         // right row\r
+               updatespot = PORTTILESWIDE-1;\r
+               updatestep = UPDATEWIDE;\r
+               x = originxtile + PORTTILESWIDE-1;\r
+               y = originytile;\r
+               xstep = 0;\r
+               ystep = 1;\r
+               count = PORTTILESHIGH;\r
+               break;\r
+\r
+       case 2:         // bottom row\r
+               updatespot = UPDATEWIDE*(PORTTILESHIGH-1);\r
+               updatestep = 1;\r
+               x = originxtile;\r
+               y = originytile + PORTTILESHIGH-1;\r
+               xstep = 1;\r
+               ystep = 0;\r
+               count = PORTTILESWIDE;\r
+               break;\r
+\r
+       case 3:         // left row\r
+               updatespot = 0;\r
+               updatestep = UPDATEWIDE;\r
+               x = originxtile;\r
+               y = originytile;\r
+               xstep = 0;\r
+               ystep = 1;\r
+               count = PORTTILESHIGH;\r
+               break;\r
+       default:\r
+               Quit ("RFL_NewRow: Bad dir!");\r
+       }\r
+\r
+       while (count--)\r
+       {\r
+               RFL_NewTile(updatespot);\r
+               RFL_CheckForAnimTile (x,y);\r
+               updatespot+=updatestep;\r
+               x+=xstep;\r
+               y+=ystep;\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_ForceRefresh\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_ForceRefresh (void)\r
+{\r
+       RF_NewPosition (originxglobal,originyglobal);\r
+       RF_Refresh ();\r
+       RF_Refresh ();\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_MapToMap\r
+=\r
+= Copies a block of tiles (all three planes) from one point\r
+= in the map to another, accounting for animating tiles\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_MapToMap (unsigned srcx, unsigned srcy,\r
+                                 unsigned destx, unsigned desty,\r
+                                 unsigned width, unsigned height)\r
+{\r
+       int                     x,y;\r
+       unsigned        source,destofs,xspot,yspot;\r
+       unsigned        linedelta,p0,p1,p2,updatespot;\r
+       unsigned        far *source0, far *source1, far *source2;\r
+       unsigned        far *dest0, far *dest1, far *dest2;\r
+       boolean         changed;\r
+\r
+       RFL_RemoveAnimsInBlock (destx,desty,width,height);\r
+\r
+       source = mapbwidthtable[srcy]/2 + srcx;\r
+\r
+       source0 = mapsegs[0]+source;\r
+       source1 = mapsegs[1]+source;\r
+       source2 = mapsegs[2]+source;\r
+\r
+       destofs = mapbwidthtable[desty]/2 + destx;\r
+       destofs -= source;\r
+\r
+       linedelta = mapwidth - width;\r
+\r
+       for (y=0;y<height;y++,source0+=linedelta,source1+=linedelta,source2+=linedelta)\r
+               for (x=0;x<width;x++,source0++,source1++,source2++)\r
+               {\r
+                       p0 = *source0;\r
+                       p1 = *source1;\r
+                       p2 = *source2;\r
+\r
+                       dest0 = source0 + destofs;\r
+                       dest1 = source1 + destofs;\r
+                       dest2 = source2 + destofs;\r
+\r
+//\r
+// only make a new tile if it is different\r
+//\r
+                       if (p0 != *dest0 || p1 != *dest1 || p2 != *dest2)\r
+                       {\r
+                               *dest0 = p0;\r
+                               *dest1 = p1;\r
+                               *dest2 = p2;\r
+                               changed = true;\r
+                       }\r
+                       else\r
+                               changed = false;\r
+\r
+//\r
+// if tile is on the view port\r
+//\r
+                       xspot = destx+x-originxtile;\r
+                       yspot = desty+y-originytile;\r
+                       if (yspot < PORTTILESHIGH && xspot < PORTTILESWIDE)\r
+                       {\r
+                               if (changed)\r
+                               {\r
+                                       updatespot = uwidthtable[yspot]+xspot;\r
+                                       RFL_NewTile(updatespot);\r
+                               }\r
+                               RFL_CheckForAnimTile (destx+x,desty+y);\r
+                       }\r
+               }\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_MemToMap\r
+=\r
+= Copies a string of tiles from main memory to the map,\r
+= accounting for animating tiles\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_MemToMap (unsigned far *source, unsigned plane,\r
+                                 unsigned destx, unsigned desty,\r
+                                 unsigned width, unsigned height)\r
+{\r
+       int                     x,y;\r
+       unsigned        xspot,yspot;\r
+       unsigned        linedelta,updatespot;\r
+       unsigned        far *dest,old,new;\r
+       boolean         changed;\r
+\r
+       RFL_RemoveAnimsInBlock (destx,desty,width,height);\r
+\r
+       dest = mapsegs[plane] + mapbwidthtable[desty]/2 + destx;\r
+\r
+       linedelta = mapwidth - width;\r
+\r
+       for (y=0;y<height;y++,dest+=linedelta)\r
+               for (x=0;x<width;x++)\r
+               {\r
+                       old = *dest;\r
+                       new = *source++;\r
+                       if (old != new)\r
+                       {\r
+                               *dest = new;\r
+                               changed = true;\r
+                       }\r
+                       else\r
+                               changed = false;\r
+\r
+                       dest++;\r
+                       xspot = destx+x-originxtile;\r
+                       yspot = desty+y-originytile;\r
+                       if (yspot < PORTTILESHIGH && xspot < PORTTILESWIDE)\r
+                       {\r
+                               if (changed)\r
+                               {\r
+                                       updatespot = uwidthtable[yspot]+xspot;\r
+                                       RFL_NewTile(updatespot);\r
+                               }\r
+                               RFL_CheckForAnimTile (destx+x,desty+y);\r
+                       }\r
+               }\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RFL_BoundNewOrigin\r
+=\r
+= Copies a string of tiles from main memory to the map,\r
+= accounting for animating tiles\r
+=\r
+=====================\r
+*/\r
+\r
+void RFL_BoundNewOrigin (unsigned orgx,unsigned orgy)\r
+{\r
+       int     check,edge;\r
+\r
+//\r
+// calculate new origin related globals\r
+//\r
+       if (orgx<originxmin)\r
+         orgx=originxmin;\r
+       else if (orgx>originxmax)\r
+         orgx=originxmax;\r
+\r
+       if (orgy<originymin)\r
+         orgy=originymin;\r
+       else if (orgy>originymax)\r
+         orgy=originymax;\r
+\r
+       originxtile = orgx>>G_T_SHIFT;\r
+       originytile = orgy>>G_T_SHIFT;\r
+\r
+       for (check=0;check<vscrollblocks;check++)\r
+       {\r
+               edge = vscrolledge[check];\r
+               if (edge>=originxtile && edge <=originxtile+10)\r
+               {\r
+                       orgx = (edge+1)*TILEGLOBAL;\r
+                       break;\r
+               }\r
+               if (edge>=originxtile+11 && edge <=originxtile+20)\r
+               {\r
+                       orgx = (edge-20)*TILEGLOBAL;\r
+                       break;\r
+               }\r
+       }\r
+\r
+       for (check=0;check<hscrollblocks;check++)\r
+       {\r
+               edge = hscrolledge[check];\r
+               if (edge>=originytile && edge <=originytile+6)\r
+               {\r
+                       orgy = (edge+1)*TILEGLOBAL;\r
+                       break;\r
+               }\r
+               if (edge>=originytile+7 && edge <=originytile+13)\r
+               {\r
+                       orgy = (edge-13)*TILEGLOBAL;\r
+                       break;\r
+               }\r
+       }\r
+\r
+\r
+       RFL_CalcOriginStuff (orgx,orgy);\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_ClearBlock\r
+=\r
+= Posts erase blocks to clear a certain area of the screen to the master\r
+= screen, to erase text or something draw directly to the screen\r
+=\r
+= Parameters in pixels, but erasure is byte bounded\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_ClearBlock (int        x, int y, int width, int height)\r
+{\r
+       eraseblocktype block;\r
+\r
+#if GRMODE == EGAGR\r
+       block.screenx = x/8+originxscreen;\r
+       block.screeny = y+originyscreen;\r
+       block.width = (width+(x&7)+7)/8;\r
+       block.height = height;\r
+       memcpy (eraselistptr[0]++,&block,sizeof(block));\r
+       memcpy (eraselistptr[1]++,&block,sizeof(block));\r
+#endif\r
+\r
+#if GRMODE == CGAGR\r
+       block.screenx = x/4+originxscreen;\r
+       block.screeny = y+originyscreen;\r
+       block.width = (width+(x&3)+3)/4;\r
+       block.height = height;\r
+       memcpy (eraselistptr[0]++,&block,sizeof(block));\r
+#endif\r
+\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_RedrawBlock\r
+=\r
+= Causes a number of tiles to be redrawn to the master screen and updated\r
+=\r
+= Parameters in pixels, but erasure is tile bounded\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_RedrawBlock (int x, int y, int width, int height)\r
+{\r
+       int     xx,yy,xl,xh,yl,yh;\r
+\r
+       xl=(x+panx)/16;\r
+       xh=(x+panx+width+15)/16;\r
+       yl=(y+pany)/16;\r
+       yh=(y+pany+height+15)/16;\r
+       for (yy=yl;yy<=yh;yy++)\r
+               for (xx=xl;xx<=xh;xx++)\r
+                       RFL_NewTile (yy*UPDATEWIDE+xx);\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_CalcTics\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_CalcTics (void)\r
+{\r
+       long    newtime,oldtimecount;\r
+\r
+//\r
+// calculate tics since last refresh for adaptive timing\r
+//\r
+       if (lasttimecount > TimeCount)\r
+               TimeCount = lasttimecount;              // if the game was paused a LONG time\r
+\r
+       if (DemoMode)                                   // demo recording and playback needs\r
+       {                                                               // to be constant\r
+//\r
+// take DEMOTICS or more tics, and modify Timecount to reflect time taken\r
+//\r
+               oldtimecount = lasttimecount;\r
+               while (TimeCount<oldtimecount+DEMOTICS*2)\r
+               ;\r
+               lasttimecount = oldtimecount + DEMOTICS;\r
+               TimeCount = lasttimecount + DEMOTICS;\r
+               tics = DEMOTICS;\r
+       }\r
+       else\r
+       {\r
+//\r
+// non demo, so report actual time\r
+//\r
+               do\r
+               {\r
+                       newtime = TimeCount;\r
+                       tics = newtime-lasttimecount;\r
+               } while (tics<MINTICS);\r
+               lasttimecount = newtime;\r
+\r
+#ifdef PROFILE\r
+                       strcpy (scratch,"\tTics:");\r
+                       itoa (tics,str,10);\r
+                       strcat (scratch,str);\r
+                       strcat (scratch,"\n");\r
+                       write (profilehandle,scratch,strlen(scratch));\r
+#endif\r
+\r
+               if (tics>MAXTICS)\r
+               {\r
+                       TimeCount -= (tics-MAXTICS);\r
+                       tics = MAXTICS;\r
+               }\r
+       }\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                       EGA specific routines\r
+\r
+=============================================================================\r
+*/\r
+\r
+#if GRMODE == EGAGR\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_FindFreeBuffer\r
+=\r
+= Finds the start of unused, non visable buffer space\r
+=\r
+=====================\r
+*/\r
+\r
+unsigned RF_FindFreeBuffer (void)\r
+{\r
+       unsigned        spot,i,j;\r
+       boolean         ok;\r
+\r
+       for (i=0;i<3;i++)\r
+       {\r
+               spot = screenstart[i]+SCREENSPACE;\r
+               ok = true;\r
+               for (j=0;j<3;j++)\r
+                       if (spot == screenstart[j])\r
+                       {\r
+                               ok = false;\r
+                               break;\r
+                       }\r
+               if (ok)\r
+                       return spot;\r
+       }\r
+\r
+       return 0;       // never get here...\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_NewPosition EGA\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_NewPosition (unsigned x, unsigned y)\r
+{\r
+       int mx,my;\r
+       byte    *page0ptr,*page1ptr;\r
+       unsigned        updatenum;\r
+\r
+       RFL_BoundNewOrigin (x,y);\r
+//\r
+// clear out all animating tiles\r
+//\r
+       RFL_InitAnimList ();\r
+\r
+//\r
+// set up the new update arrays at base position\r
+//\r
+       updatestart[0] = baseupdatestart[0];\r
+       updatestart[1] = baseupdatestart[1];\r
+       updateptr = updatestart[otherpage];\r
+\r
+       page0ptr = updatestart[0]+PORTTILESWIDE;        // used to stick "0"s after rows\r
+       page1ptr = updatestart[1]+PORTTILESWIDE;\r
+\r
+       updatenum = 0;                          // start at first visable tile\r
+\r
+       for (my=0;my<PORTTILESHIGH;my++)\r
+       {\r
+               for (mx=0;mx<PORTTILESWIDE;mx++)\r
+               {\r
+                       RFL_NewTile(updatenum);                 // puts "1"s in both pages\r
+                       RFL_CheckForAnimTile(mx+originxtile,my+originytile);\r
+                       updatenum++;\r
+               }\r
+               updatenum++;\r
+               *page0ptr = *page1ptr = 0; // set a 0 at end of a line of tiles\r
+               page0ptr+=(PORTTILESWIDE+1);\r
+               page1ptr+=(PORTTILESWIDE+1);\r
+       }\r
+       *(word *)(page0ptr-PORTTILESWIDE)\r
+               = *(word *)(page1ptr-PORTTILESWIDE) = UPDATETERMINATE;\r
+}\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_Scroll  EGA\r
+=\r
+= Move the origin x/y global coordinates, readjust the screen panning, and\r
+= scroll if needed.  If the scroll distance is greater than one tile, the\r
+= entire screen will be redrawn (this could be generalized, but scrolling\r
+= more than one tile per refresh is a bad idea!).\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_Scroll (int x, int y)\r
+{\r
+       long            neworgx,neworgy;\r
+       int                     i,deltax,deltay,absdx,absdy;\r
+       int                     oldxt,oldyt,move,yy;\r
+       unsigned        updatespot;\r
+       byte            *update0,*update1;\r
+       unsigned        oldpanx,oldpanadjust,oldscreen,newscreen,screencopy;\r
+       int                     screenmove;\r
+\r
+       oldxt = originxtile;\r
+       oldyt = originytile;\r
+       oldpanadjust = panadjust;\r
+       oldpanx = panx;\r
+\r
+       RFL_BoundScroll (x,y);\r
+\r
+       deltax = originxtile - oldxt;\r
+       absdx = abs(deltax);\r
+       deltay = originytile - oldyt;\r
+       absdy = abs(deltay);\r
+\r
+       if (absdx>1 || absdy>1)\r
+       {\r
+       //\r
+       // scrolled more than one tile, so start from scratch\r
+       //\r
+               RF_NewPosition(originxglobal,originyglobal);\r
+               return;\r
+       }\r
+\r
+       if (!absdx && !absdy)\r
+               return;                                 // the screen has not scrolled an entire tile\r
+\r
+\r
+//\r
+// adjust screens and handle SVGA crippled compatability mode\r
+//\r
+       screenmove = deltay*16*SCREENWIDTH + deltax*TILEWIDTH;\r
+       for (i=0;i<3;i++)\r
+       {\r
+               screenstart[i]+= screenmove;\r
+               if (compatability && screenstart[i] > (0x10000l-SCREENSPACE) )\r
+               {\r
+                       //\r
+                       // move the screen to the opposite end of the buffer\r
+                       //\r
+                       screencopy = screenmove>0 ? FREEEGAMEM : -FREEEGAMEM;\r
+                       oldscreen = screenstart[i] - screenmove;\r
+                       newscreen = oldscreen + screencopy;\r
+                       screenstart[i] = newscreen + screenmove;\r
+                       VW_ScreenToScreen (oldscreen,newscreen,\r
+                               PORTTILESWIDE*2,PORTTILESHIGH*16);\r
+\r
+                       if (i==screenpage)\r
+                               VW_SetScreen(newscreen+oldpanadjust,oldpanx & xpanmask);\r
+               }\r
+       }\r
+       bufferofs = screenstart[otherpage];\r
+       displayofs = screenstart[screenpage];\r
+       masterofs = screenstart[2];\r
+\r
+\r
+//\r
+// float the update regions\r
+//\r
+       move = deltax;\r
+       if (deltay==1)\r
+         move += UPDATEWIDE;\r
+       else if (deltay==-1)\r
+         move -= UPDATEWIDE;\r
+\r
+       updatestart[0]+=move;\r
+       updatestart[1]+=move;\r
+\r
+//\r
+// draw the new tiles just scrolled on to the master screen, and\r
+// mark them as needing to be copied to each screen next refreshes\r
+// Make sure a zero is at the end of each row in update\r
+//\r
+\r
+       if (deltax)\r
+       {\r
+               if (deltax==1)\r
+               {\r
+                       RFL_NewRow (1);                 // new right row\r
+                       RFL_RemoveAnimsOnX (originxtile-1);\r
+               }\r
+               else\r
+               {\r
+                       RFL_NewRow (3);                 // new left row\r
+                       RFL_RemoveAnimsOnX (originxtile+PORTTILESWIDE);\r
+               }\r
+\r
+               update0 = updatestart[0]+PORTTILESWIDE;\r
+               update1 = updatestart[1]+PORTTILESWIDE;\r
+               for     (yy=0;yy<PORTTILESHIGH;yy++)\r
+               {\r
+                       *update0 = *update1 = 0;        // drop a 0 at end of each row\r
+                       update0+=UPDATEWIDE;\r
+                       update1+=UPDATEWIDE;\r
+               }\r
+       }\r
+\r
+//----------------\r
+\r
+       if (deltay)\r
+       {\r
+               if (deltay==1)\r
+               {\r
+                       updatespot = UPDATEWIDE*(PORTTILESHIGH-1);\r
+                       RFL_NewRow (2);                 // new bottom row\r
+                       RFL_RemoveAnimsOnY (originytile-1);\r
+               }\r
+               else\r
+               {\r
+                       updatespot = 0;\r
+                       RFL_NewRow (0);                 // new top row\r
+                       RFL_RemoveAnimsOnY (originytile+PORTTILESHIGH);\r
+               }\r
+\r
+               *(updatestart[0]+updatespot+PORTTILESWIDE) =\r
+                       *(updatestart[1]+updatespot+PORTTILESWIDE) = 0;\r
+       }\r
+\r
+//----------------\r
+\r
+       //\r
+       // place a new terminator\r
+       //\r
+       update0 = updatestart[0]+UPDATEWIDE*PORTTILESHIGH-1;\r
+       update1 = updatestart[1]+UPDATEWIDE*PORTTILESHIGH-1;\r
+       *update0++ = *update1++ = 0;\r
+       *(unsigned *)update0 = *(unsigned *)update1 = UPDATETERMINATE;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_PlaceSprite   EGA\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_PlaceSprite (void **user,unsigned globalx,unsigned globaly,\r
+       unsigned spritenumber, drawtype draw, int priority)\r
+{\r
+       spritelisttype  register *sprite,*next;\r
+       spritetabletype far *spr;\r
+       spritetype _seg *block;\r
+       unsigned        shift,pixx;\r
+       char            str[80],str2[10];\r
+\r
+       if (!spritenumber || spritenumber == (unsigned)-1)\r
+       {\r
+               RF_RemoveSprite (user);\r
+               return;\r
+       }\r
+\r
+       sprite = (spritelisttype *)*user;\r
+\r
+       if      (sprite)\r
+       {\r
+       // sprite allready exists in the list, so we can use it's block\r
+\r
+       //\r
+       // post an erase block to both pages by copying screenx,screeny,width,height\r
+       // both pages may not need to be erased if the sprite just changed last frame\r
+       //\r
+               if (sprite->updatecount<2)\r
+               {\r
+                       if (!sprite->updatecount)\r
+                               memcpy (eraselistptr[otherpage]++,sprite,sizeof(eraseblocktype));\r
+                       memcpy (eraselistptr[screenpage]++,sprite,sizeof(eraseblocktype));\r
+               }\r
+\r
+               if (priority != sprite->priority)\r
+               {\r
+               // sprite mvoed to another priority, so unlink the old one and\r
+               // relink it in the new priority\r
+\r
+                       next = sprite->nextsprite;                      // cut old links\r
+                       if (next)\r
+                               next->prevptr = sprite->prevptr;\r
+                       *sprite->prevptr = next;\r
+                       goto linknewspot;\r
+               }\r
+       }\r
+       else\r
+       {\r
+       // this is a brand new sprite, so allocate a block from the array\r
+\r
+               if (!spritefreeptr)\r
+                       Quit ("RF_PlaceSprite: No free spots in spritearray!");\r
+\r
+               sprite = spritefreeptr;\r
+               spritefreeptr = spritefreeptr->nextsprite;\r
+\r
+linknewspot:\r
+               next = prioritystart[priority];         // stick it in new spot\r
+               if (next)\r
+                       next->prevptr = &sprite->nextsprite;\r
+               sprite->nextsprite = next;\r
+               prioritystart[priority] = sprite;\r
+               sprite->prevptr = &prioritystart[priority];\r
+       }\r
+\r
+//\r
+// write the new info to the sprite\r
+//\r
+       spr = &spritetable[spritenumber-STARTSPRITES];\r
+       block = (spritetype _seg *)grsegs[spritenumber];\r
+\r
+       if (!block)\r
+       {\r
+               strcpy (str,"RF_PlaceSprite: Placed an uncached sprite:");\r
+               itoa (spritenumber,str2,10);\r
+               strcat (str,str2);\r
+               Quit (str);\r
+       }\r
+\r
+       globaly+=spr->orgy;\r
+       globalx+=spr->orgx;\r
+\r
+       pixx = globalx >> G_SY_SHIFT;\r
+       shift = (pixx&7)/2;\r
+\r
+       sprite->screenx = pixx >> (G_EGASX_SHIFT-G_SY_SHIFT);\r
+       sprite->screeny = globaly >> G_SY_SHIFT;\r
+       sprite->width = block->width[shift];\r
+       sprite->height = spr->height;\r
+       sprite->grseg = spritenumber;\r
+       sprite->sourceofs = block->sourceoffset[shift];\r
+       sprite->planesize = block->planesize[shift];\r
+       sprite->draw = draw;\r
+       sprite->priority = priority;\r
+       sprite->tilex = sprite->screenx >> SX_T_SHIFT;\r
+       sprite->tiley = sprite->screeny >> SY_T_SHIFT;\r
+       sprite->tilewide = ( (sprite->screenx + sprite->width -1) >> SX_T_SHIFT )\r
+               - sprite->tilex + 1;\r
+       sprite->tilehigh = ( (sprite->screeny + sprite->height -1) >> SY_T_SHIFT )\r
+               - sprite->tiley + 1;\r
+\r
+       sprite->updatecount = 2;                // draw on next two refreshes\r
+\r
+// save the sprite pointer off in the user's pointer so it can be moved\r
+// again later\r
+\r
+       *user = sprite;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_RemoveSprite  EGA\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_RemoveSprite (void **user)\r
+{\r
+       spritelisttype  *sprite,*next;\r
+\r
+       sprite = (spritelisttype *)*user;\r
+       if (!sprite)\r
+               return;\r
+\r
+//\r
+// post an erase block to both pages by copying screenx,screeny,width,height\r
+// both pages may not need to be erased if the sprite just changed last frame\r
+//\r
+       if (sprite->updatecount<2)\r
+       {\r
+               if (!sprite->updatecount)\r
+                       memcpy (eraselistptr[otherpage]++,sprite,sizeof(eraseblocktype));\r
+               memcpy (eraselistptr[screenpage]++,sprite,sizeof(eraseblocktype));\r
+       }\r
+\r
+//\r
+// unlink the sprite node\r
+//\r
+       next = sprite->nextsprite;\r
+       if (next)                                               // if (!next), sprite is last in chain\r
+               next->prevptr = sprite->prevptr;\r
+       *sprite->prevptr = next;\r
+\r
+//\r
+// add it back to the free list\r
+//\r
+       sprite->nextsprite = spritefreeptr;\r
+       spritefreeptr = sprite;\r
+\r
+//\r
+// null the users pointer, so next time that actor gets placed, it will\r
+// allocate a new block\r
+//\r
+\r
+       *user = 0;\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_EraseBlocks  EGA\r
+=\r
+= Write mode 1 should be set\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_EraseBlocks (void)\r
+{\r
+       eraseblocktype  *block,*done;\r
+       int                     screenxh,screenyh;\r
+       unsigned        pos,xtl,ytl,xth,yth,x,y;\r
+       byte            *updatespot;\r
+       unsigned        updatedelta;\r
+       unsigned        erasecount;\r
+\r
+#ifdef PROFILE\r
+       erasecount = 0;\r
+#endif\r
+\r
+       block = otherpage ? &eraselist[1][0] : &eraselist[0][0];\r
+\r
+       done = eraselistptr[otherpage];\r
+\r
+       while (block != done)\r
+       {\r
+\r
+       //\r
+       // clip the block to the current screen view\r
+       //\r
+               block->screenx -= originxscreen;\r
+               block->screeny -= originyscreen;\r
+\r
+               if (block->screenx < 0)\r
+               {\r
+                       block->width += block->screenx;\r
+                       if (block->width<1)\r
+                               goto next;\r
+                       block->screenx = 0;\r
+               }\r
+\r
+               if (block->screeny < 0)\r
+               {\r
+                       block->height += block->screeny;\r
+                       if (block->height<1)\r
+                               goto next;\r
+                       block->screeny = 0;\r
+               }\r
+\r
+               screenxh = block->screenx + block->width;\r
+               screenyh = block->screeny + block->height;\r
+\r
+               if (screenxh > EGAPORTSCREENWIDE)\r
+               {\r
+                       block->width = EGAPORTSCREENWIDE-block->screenx;\r
+                       screenxh = block->screenx + block->width;\r
+               }\r
+\r
+               if (screenyh > PORTSCREENHIGH)\r
+               {\r
+                       block->height = PORTSCREENHIGH-block->screeny;\r
+                       screenyh = block->screeny + block->height;\r
+               }\r
+\r
+               if (block->width<1 || block->height<1)\r
+                       goto next;\r
+\r
+       //\r
+       // erase the block by copying from the master screen\r
+       //\r
+               pos = ylookup[block->screeny]+block->screenx;\r
+               VW_ScreenToScreen (masterofs+pos,bufferofs+pos,\r
+                       block->width,block->height);\r
+\r
+       //\r
+       // put 2s in update where the block was, to force sprites to update\r
+       //\r
+               xtl = block->screenx >> SX_T_SHIFT;\r
+               xth = (block->screenx+block->width-1) >> SX_T_SHIFT;\r
+               ytl = block->screeny >> SY_T_SHIFT;\r
+               yth = (block->screeny+block->height-1) >> SY_T_SHIFT;\r
+\r
+               updatespot = updateptr + uwidthtable[ytl] + xtl;\r
+               updatedelta = UPDATEWIDE - (xth-xtl+1);\r
+\r
+               for (y=ytl;y<=yth;y++)\r
+               {\r
+                       for (x=xtl;x<=xth;x++)\r
+                               *updatespot++ = 2;\r
+                       updatespot += updatedelta;              // down to next line\r
+               }\r
+#ifdef PROFILE\r
+               erasecount++;\r
+#endif\r
+\r
+next:\r
+               block++;\r
+       }\r
+       eraselistptr[otherpage] = otherpage ? &eraselist[1][0] : &eraselist[0][0];\r
+#ifdef PROFILE\r
+       strcpy (scratch,"\tErase:");\r
+       itoa (erasecount,str,10);\r
+       strcat (scratch,str);\r
+       write (profilehandle,scratch,strlen(scratch));\r
+#endif\r
+\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_UpdateSprites EGA\r
+=\r
+= NOTE: Implement vertical clipping!\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_UpdateSprites (void)\r
+{\r
+       spritelisttype  *sprite;\r
+       int     portx,porty,x,y,xtl,xth,ytl,yth;\r
+       int     priority;\r
+       unsigned dest;\r
+       byte            *updatespot,*baseupdatespot;\r
+       unsigned        updatedelta;\r
+       unsigned        updatecount;\r
+       unsigned        height,sourceofs;\r
+\r
+#ifdef PROFILE\r
+       updatecount = 0;\r
+#endif\r
+\r
+       for (priority=0;priority<PRIORITIES;priority++)\r
+       {\r
+               if (priority==MASKEDTILEPRIORITY)\r
+                       RFL_MaskForegroundTiles ();\r
+\r
+               for (sprite = prioritystart[priority]; sprite ;\r
+                       sprite = (spritelisttype *)sprite->nextsprite)\r
+               {\r
+               //\r
+               // see if the sprite has any visable area in the port\r
+               //\r
+\r
+                       portx = sprite->screenx - originxscreen;\r
+                       porty = sprite->screeny - originyscreen;\r
+                       xtl = portx >> SX_T_SHIFT;\r
+                       xth = (portx + sprite->width-1) >> SX_T_SHIFT;\r
+                       ytl = porty >> SY_T_SHIFT;\r
+                       yth = (porty + sprite->height-1) >> SY_T_SHIFT;\r
+\r
+                       if (xtl<0)\r
+                         xtl = 0;\r
+                       if (xth>=PORTTILESWIDE)\r
+                         xth = PORTTILESWIDE-1;\r
+                       if (ytl<0)\r
+                         ytl = 0;\r
+                       if (yth>=PORTTILESHIGH)\r
+                         yth = PORTTILESHIGH-1;\r
+\r
+                       if (xtl>xth || ytl>yth)\r
+                               continue;\r
+\r
+               //\r
+               // see if it's visable area covers any non 0 update tiles\r
+               //\r
+                       updatespot = baseupdatespot = updateptr + uwidthtable[ytl] + xtl;\r
+                       updatedelta = UPDATEWIDE - (xth-xtl+1);\r
+\r
+                       if (sprite->updatecount)\r
+                       {\r
+                               sprite->updatecount--;                  // the sprite was just placed,\r
+                               goto redraw;                                    // so draw it for sure\r
+                       }\r
+\r
+                       for (y=ytl;y<=yth;y++)\r
+                       {\r
+                               for (x=xtl;x<=xth;x++)\r
+                                       if (*updatespot++)\r
+                                               goto redraw;\r
+                               updatespot += updatedelta;              // down to next line\r
+                       }\r
+                       continue;                                                       // no need to update\r
+\r
+redraw:\r
+               //\r
+               // set the tiles it covers to 3, because those tiles are being\r
+               // updated\r
+               //\r
+                       updatespot = baseupdatespot;\r
+                       for (y=ytl;y<=yth;y++)\r
+                       {\r
+                               for (x=xtl;x<=xth;x++)\r
+                                       *updatespot++ = 3;\r
+                               updatespot += updatedelta;              // down to next line\r
+                       }\r
+               //\r
+               // draw it!\r
+               //\r
+                       height = sprite->height;\r
+                       sourceofs = sprite->sourceofs;\r
+                       if (porty<0)\r
+                       {\r
+                               height += porty;                                        // clip top off\r
+                               sourceofs -= porty*sprite->width;\r
+                               porty = 0;\r
+                       }\r
+                       else if (porty+height>PORTSCREENHIGH)\r
+                       {\r
+                               height = PORTSCREENHIGH - porty;    // clip bottom off\r
+                       }\r
+\r
+                       dest = bufferofs + ylookup[porty] + portx;\r
+\r
+                       switch (sprite->draw)\r
+                       {\r
+                       case spritedraw:\r
+                               VW_MaskBlock(grsegs[sprite->grseg], sourceofs,\r
+                                       dest,sprite->width,height,sprite->planesize);\r
+                               break;\r
+\r
+                       case maskdraw:\r
+                               break;\r
+\r
+                       }\r
+#ifdef PROFILE\r
+                       updatecount++;\r
+#endif\r
+\r
+\r
+               }\r
+       }\r
+#ifdef PROFILE\r
+       strcpy (scratch,"\tSprites:");\r
+       itoa (updatecount,str,10);\r
+       strcat (scratch,str);\r
+       write (profilehandle,scratch,strlen(scratch));\r
+#endif\r
+\r
+}\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_Refresh   EGA\r
+=\r
+= All routines will draw at the port at bufferofs, possibly copying from\r
+= the port at masterofs.  The EGA version then page flips, while the\r
+= CGA version updates the screen from the buffer port.\r
+=\r
+= Screenpage is the currently displayed page, not the one being drawn\r
+= Otherpage is the page to be worked with now\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_Refresh (void)\r
+{\r
+       byte    *newupdate;\r
+\r
+       updateptr = updatestart[otherpage];\r
+\r
+       RFL_AnimateTiles ();            // DEBUG\r
+\r
+//\r
+// update newly scrolled on tiles and animated tiles from the master screen\r
+//\r
+       EGAWRITEMODE(1);\r
+       EGAMAPMASK(15);\r
+       RFL_UpdateTiles ();\r
+       RFL_EraseBlocks ();\r
+\r
+//\r
+// Update is all 0 except where sprites have changed or new area has\r
+// been scrolled on.  Go through all sprites and update the ones that cover\r
+// a non 0 update tile\r
+//\r
+       EGAWRITEMODE(0);\r
+       RFL_UpdateSprites ();\r
+\r
+//\r
+// if the main program has a refresh hook set, call their function before\r
+// displaying the new page\r
+//\r
+       if (refreshvector)\r
+               refreshvector();\r
+\r
+//\r
+// display the changed screen\r
+//\r
+       VW_SetScreen(bufferofs+panadjust,panx & xpanmask);\r
+\r
+//\r
+// prepare for next refresh\r
+//\r
+// Set the update array to the middle position and clear it out to all "0"s\r
+// with an UPDATETERMINATE at the end\r
+//\r
+       updatestart[otherpage] = newupdate = baseupdatestart[otherpage];\r
+asm    mov     ax,ds\r
+asm    mov     es,ax\r
+asm    xor     ax,ax\r
+asm    mov     cx,(UPDATESCREENSIZE-2)/2\r
+asm    mov     di,[newupdate]\r
+asm    rep     stosw\r
+asm    mov     [WORD PTR es:di],UPDATETERMINATE\r
+\r
+       screenpage ^= 1;\r
+       otherpage ^= 1;\r
+       bufferofs = screenstart[otherpage];\r
+       displayofs = screenstart[screenpage];\r
+\r
+//\r
+// calculate tics since last refresh for adaptive timing\r
+//\r
+       RF_CalcTics ();\r
+}\r
+\r
+#endif         // GRMODE == EGAGR\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                       CGA specific routines\r
+\r
+=============================================================================\r
+*/\r
+\r
+#if GRMODE == CGAGR\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_NewPosition   CGA\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_NewPosition (unsigned x, unsigned y)\r
+{\r
+       int mx,my;\r
+       byte    *spotptr;\r
+       unsigned        updatenum;\r
+\r
+       RFL_BoundNewOrigin (x,y);\r
+\r
+//\r
+// clear out all animating tiles\r
+//\r
+       RFL_InitAnimList ();\r
+\r
+//\r
+// set up the new update arrays at base position\r
+//\r
+       updateptr = baseupdateptr;\r
+\r
+       spotptr = updateptr + PORTTILESWIDE;    // used to stick "0"s after rows\r
+\r
+       updatenum = 0;                          // start at first visable tile\r
+\r
+       for (my=0;my<PORTTILESHIGH;my++)\r
+       {\r
+               for (mx=0;mx<PORTTILESWIDE;mx++)\r
+               {\r
+                       RFL_NewTile(updatenum);                 // puts "1"s in both pages\r
+                       RFL_CheckForAnimTile(mx+originxtile,my+originytile);\r
+                       updatenum++;\r
+               }\r
+               updatenum++;\r
+               *spotptr = 0; // set a 0 at end of a line of tiles\r
+               spotptr +=(PORTTILESWIDE+1);\r
+       }\r
+       *(word *)(spotptr-PORTTILESWIDE) = UPDATETERMINATE;\r
+}\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_Scroll       CGA\r
+=\r
+= Move the origin x/y global coordinates, readjust the screen panning, and\r
+= scroll if needed.  If the scroll distance is greater than one tile, the\r
+= entire screen will be redrawn (this could be generalized, but scrolling\r
+= more than one tile per refresh is a bad idea!).\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_Scroll (int x, int y)\r
+{\r
+       long            neworgx,neworgy;\r
+       int                     i,deltax,deltay,absdx,absdy;\r
+       int                     oldxt,oldyt,move,yy;\r
+       unsigned        updatespot;\r
+       byte            *spotptr;\r
+       unsigned        oldoriginmap,oldscreen,newscreen,screencopy;\r
+       int                     screenmove;\r
+\r
+       oldxt = originxtile;\r
+       oldyt = originytile;\r
+\r
+       RFL_BoundScroll (x,y);\r
+\r
+       deltax = originxtile - oldxt;\r
+       absdx = abs(deltax);\r
+       deltay = originytile - oldyt;\r
+       absdy = abs(deltay);\r
+\r
+       if (absdx>1 || absdy>1)\r
+       {\r
+       //\r
+       // scrolled more than one tile, so start from scratch\r
+       //\r
+               RF_NewPosition(originxglobal,originyglobal);\r
+               return;\r
+       }\r
+\r
+       if (!absdx && !absdy)\r
+               return;                                 // the screen has not scrolled an entire tile\r
+\r
+\r
+//\r
+// float screens\r
+//\r
+       screenmove = deltay*16*SCREENWIDTH + deltax*TILEWIDTH;\r
+       bufferofs += screenmove;\r
+       masterofs += screenmove;\r
+\r
+\r
+//\r
+// float the update regions\r
+//\r
+       move = deltax;\r
+       if (deltay==1)\r
+         move += UPDATEWIDE;\r
+       else if (deltay==-1)\r
+         move -= UPDATEWIDE;\r
+\r
+       updateptr+=move;\r
+\r
+//\r
+// draw the new tiles just scrolled on to the master screen, and\r
+// mark them as needing to be copied to each screen next refreshes\r
+// Make sure a zero is at the end of each row in update\r
+//\r
+\r
+       if (deltax)\r
+       {\r
+               if (deltax==1)\r
+               {\r
+                       RFL_NewRow (1);                 // new right row\r
+                       RFL_RemoveAnimsOnX (originxtile-1);\r
+               }\r
+               else\r
+               {\r
+                       RFL_NewRow (3);                 // new left row\r
+                       RFL_RemoveAnimsOnX (originxtile+PORTTILESWIDE);\r
+               }\r
+\r
+               spotptr = updateptr+PORTTILESWIDE;\r
+               for     (yy=0;yy<PORTTILESHIGH;yy++)\r
+               {\r
+                       *spotptr = 0;           // drop a 0 at end of each row\r
+                       spotptr+=UPDATEWIDE;\r
+               }\r
+       }\r
+\r
+//----------------\r
+\r
+       if (deltay)\r
+       {\r
+               if (deltay==1)\r
+               {\r
+                       RFL_NewRow (2);                 // new bottom row\r
+                       *(updateptr+UPDATEWIDE*(PORTTILESHIGH-1)+PORTTILESWIDE) = 0;\r
+                       RFL_RemoveAnimsOnY (originytile-1);\r
+               }\r
+               else\r
+               {\r
+                       RFL_NewRow (0);                 // new top row\r
+                       *(updateptr+PORTTILESWIDE) = 0;\r
+                       RFL_RemoveAnimsOnY (originytile+PORTTILESHIGH);\r
+               }\r
+       }\r
+\r
+//----------------\r
+\r
+       //\r
+       // place a new terminator\r
+       //\r
+       spotptr = updateptr+UPDATEWIDE*PORTTILESHIGH-1;\r
+       *spotptr++ = 0;\r
+       *(unsigned *)spotptr = UPDATETERMINATE;\r
+}\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_PlaceSprite  CGA\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_PlaceSprite (void **user,unsigned globalx,unsigned globaly,\r
+       unsigned spritenumber, drawtype draw, int priority)\r
+{\r
+       spritelisttype  register *sprite,*next;\r
+       spritetabletype far *spr;\r
+       spritetype _seg *block;\r
+       unsigned        shift,pixx;\r
+       char            str[80],str2[10];\r
+\r
+       if (!spritenumber || spritenumber == (unsigned)-1)\r
+       {\r
+               RF_RemoveSprite (user);\r
+               return;\r
+       }\r
+\r
+       sprite = (spritelisttype *)*user;\r
+\r
+       if      (sprite)\r
+       {\r
+       // sprite allready exists in the list, so we can use it's block\r
+\r
+       //\r
+       // post an erase block to erase the old position by copying\r
+       // screenx,screeny,width,height\r
+       //\r
+               if (!sprite->updatecount)               // may not have been drawn at all yet\r
+                       memcpy (eraselistptr[0]++,sprite,sizeof(eraseblocktype));\r
+\r
+               if (priority != sprite->priority)\r
+               {\r
+               // sprite moved to another priority, so unlink the old one and\r
+               // relink it in the new priority\r
+\r
+                       next = sprite->nextsprite;                      // cut old links\r
+                       if (next)\r
+                               next->prevptr = sprite->prevptr;\r
+                       *sprite->prevptr = next;\r
+                       goto linknewspot;\r
+               }\r
+       }\r
+       else\r
+       {\r
+       // this is a brand new sprite, so allocate a block from the array\r
+\r
+               if (!spritefreeptr)\r
+                       Quit ("RF_PlaceSprite: No free spots in spritearray!");\r
+\r
+               sprite = spritefreeptr;\r
+               spritefreeptr = spritefreeptr->nextsprite;\r
+\r
+linknewspot:\r
+               next = prioritystart[priority];         // stick it in new spot\r
+               if (next)\r
+                       next->prevptr = &sprite->nextsprite;\r
+               sprite->nextsprite = next;\r
+               prioritystart[priority] = sprite;\r
+               sprite->prevptr = &prioritystart[priority];\r
+       }\r
+\r
+//\r
+// write the new info to the sprite\r
+//\r
+       spr = &spritetable[spritenumber-STARTSPRITES];\r
+       block = (spritetype _seg *)grsegs[spritenumber];\r
+\r
+       if (!block)\r
+       {\r
+               strcpy (str,"RF_PlaceSprite: Placed an uncached sprite!");\r
+               itoa (spritenumber,str2,10);\r
+               strcat (str,str2);\r
+               Quit (str);\r
+       }\r
+\r
+\r
+       globaly+=spr->orgy;\r
+       globalx+=spr->orgx;\r
+\r
+       sprite->screenx = globalx >> G_CGASX_SHIFT;\r
+       sprite->screeny = globaly >> G_SY_SHIFT;\r
+       sprite->width = block->width[0];\r
+       sprite->height = spr->height;\r
+       sprite->grseg = spritenumber;\r
+       sprite->sourceofs = block->sourceoffset[0];\r
+       sprite->planesize = block->planesize[0];\r
+       sprite->draw = draw;\r
+       sprite->priority = priority;\r
+       sprite->tilex = sprite->screenx >> SX_T_SHIFT;\r
+       sprite->tiley = sprite->screeny >> SY_T_SHIFT;\r
+       sprite->tilewide = ( (sprite->screenx + sprite->width -1) >> SX_T_SHIFT )\r
+               - sprite->tilex + 1;\r
+       sprite->tilehigh = ( (sprite->screeny + sprite->height -1) >> SY_T_SHIFT )\r
+               - sprite->tiley + 1;\r
+\r
+       sprite->updatecount = 1;                // draw on next refresh\r
+\r
+// save the sprite pointer off in the user's pointer so it can be moved\r
+// again later\r
+\r
+       *user = sprite;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_RemoveSprite CGA\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_RemoveSprite (void **user)\r
+{\r
+       spritelisttype  *sprite,*next;\r
+\r
+       sprite = (spritelisttype *)*user;\r
+       if (!sprite)\r
+               return;\r
+\r
+//\r
+// post an erase block to erase the old position by copying\r
+// screenx,screeny,width,height\r
+//\r
+       if (!sprite->updatecount)\r
+       {\r
+               memcpy (eraselistptr[0]++,sprite,sizeof(eraseblocktype));\r
+       }\r
+\r
+//\r
+// unlink the sprite node\r
+//\r
+       next = sprite->nextsprite;\r
+       if (next)                                               // if (!next), sprite is last in chain\r
+               next->prevptr = sprite->prevptr;\r
+       *sprite->prevptr = next;\r
+\r
+//\r
+// add it back to the free list\r
+//\r
+       sprite->nextsprite = spritefreeptr;\r
+       spritefreeptr = sprite;\r
+\r
+//\r
+// null the users pointer, so next time that actor gets placed, it will\r
+// allocate a new block\r
+//\r
+\r
+       *user = 0;\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_EraseBlocks CGA\r
+=\r
+= Write mode 1 should be set\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_EraseBlocks (void)\r
+{\r
+       eraseblocktype  *block,*done;\r
+       int                     screenxh,screenyh;\r
+       unsigned        pos,xtl,ytl,xth,yth,x,y;\r
+       byte            *updatespot;\r
+       unsigned        updatedelta;\r
+\r
+       block = &eraselist[0][0];\r
+\r
+       done = eraselistptr[0];\r
+\r
+       while (block != done)\r
+       {\r
+\r
+       //\r
+       // clip the block to the current screen view\r
+       //\r
+               block->screenx -= originxscreen;\r
+               block->screeny -= originyscreen;\r
+\r
+               if (block->screenx < 0)\r
+               {\r
+                       block->width += block->screenx;\r
+                       if (block->width<1)\r
+                               goto next;\r
+                       block->screenx = 0;\r
+               }\r
+\r
+               if (block->screeny < 0)\r
+               {\r
+                       block->height += block->screeny;\r
+                       if (block->height<1)\r
+                               goto next;\r
+                       block->screeny = 0;\r
+               }\r
+\r
+               screenxh = block->screenx + block->width;\r
+               screenyh = block->screeny + block->height;\r
+\r
+               if (screenxh > CGAPORTSCREENWIDE)\r
+               {\r
+                       block->width = CGAPORTSCREENWIDE-block->screenx;\r
+                       screenxh = block->screenx + block->width;\r
+               }\r
+\r
+               if (screenyh > PORTSCREENHIGH)\r
+               {\r
+                       block->height = PORTSCREENHIGH-block->screeny;\r
+                       screenyh = block->screeny + block->height;\r
+               }\r
+\r
+               if (block->width<1 || block->height<1)\r
+                       goto next;\r
+\r
+       //\r
+       // erase the block by copying from the master screen\r
+       //\r
+               pos = ylookup[block->screeny]+block->screenx;\r
+               block->width = (block->width + (pos&1) + 1)& ~1;\r
+               pos &= ~1;                              // make sure a word copy gets used\r
+               VW_ScreenToScreen (masterofs+pos,bufferofs+pos,\r
+                       block->width,block->height);\r
+\r
+       //\r
+       // put 2s in update where the block was, to force sprites to update\r
+       //\r
+               xtl = block->screenx >> SX_T_SHIFT;\r
+               xth = (block->screenx+block->width-1) >> SX_T_SHIFT;\r
+               ytl = block->screeny >> SY_T_SHIFT;\r
+               yth = (block->screeny+block->height-1) >> SY_T_SHIFT;\r
+\r
+               updatespot = updateptr + uwidthtable[ytl] + xtl;\r
+               updatedelta = UPDATEWIDE - (xth-xtl+1);\r
+\r
+               for (y=ytl;y<=yth;y++)\r
+               {\r
+                       for (x=xtl;x<=xth;x++)\r
+                               *updatespot++ = 2;\r
+                       updatespot += updatedelta;              // down to next line\r
+               }\r
+\r
+next:\r
+               block++;\r
+       }\r
+       eraselistptr[0] = &eraselist[0][0];\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= RFL_UpdateSprites      CGA\r
+=\r
+= NOTE: Implement vertical clipping!\r
+=\r
+====================\r
+*/\r
+\r
+void RFL_UpdateSprites (void)\r
+{\r
+       spritelisttype  *sprite;\r
+       int     portx,porty,x,y,xtl,xth,ytl,yth;\r
+       int     priority;\r
+       unsigned dest;\r
+       byte            *updatespot,*baseupdatespot;\r
+       unsigned        updatedelta;\r
+\r
+       unsigned        updatecount;\r
+       unsigned        height,sourceofs;\r
+\r
+#ifdef PROFILE\r
+       updatecount = 0;\r
+#endif\r
+\r
+\r
+       for (priority=0;priority<PRIORITIES;priority++)\r
+       {\r
+               if (priority==MASKEDTILEPRIORITY)\r
+                       RFL_MaskForegroundTiles ();\r
+\r
+               for (sprite = prioritystart[priority]; sprite ;\r
+                       sprite = (spritelisttype *)sprite->nextsprite)\r
+               {\r
+               //\r
+               // see if the sprite has any visable area in the port\r
+               //\r
+\r
+                       portx = sprite->screenx - originxscreen;\r
+                       porty = sprite->screeny - originyscreen;\r
+                       xtl = portx >> SX_T_SHIFT;\r
+                       xth = (portx + sprite->width-1) >> SX_T_SHIFT;\r
+                       ytl = porty >> SY_T_SHIFT;\r
+                       yth = (porty + sprite->height-1) >> SY_T_SHIFT;\r
+\r
+                       if (xtl<0)\r
+                         xtl = 0;\r
+                       if (xth>=PORTTILESWIDE)\r
+                         xth = PORTTILESWIDE-1;\r
+                       if (ytl<0)\r
+                         ytl = 0;\r
+                       if (yth>=PORTTILESHIGH)\r
+                         yth = PORTTILESHIGH-1;\r
+\r
+                       if (xtl>xth || ytl>yth)\r
+                               continue;\r
+\r
+               //\r
+               // see if it's visable area covers any non 0 update tiles\r
+               //\r
+                       updatespot = baseupdatespot = updateptr + uwidthtable[ytl] + xtl;\r
+                       updatedelta = UPDATEWIDE - (xth-xtl+1);\r
+\r
+                       if (sprite->updatecount)\r
+                       {\r
+                               sprite->updatecount--;                  // the sprite was just placed,\r
+                               goto redraw;                                    // so draw it for sure\r
+                       }\r
+\r
+                       for (y=ytl;y<=yth;y++)\r
+                       {\r
+                               for (x=xtl;x<=xth;x++)\r
+                                       if (*updatespot++)\r
+                                               goto redraw;\r
+                               updatespot += updatedelta;              // down to next line\r
+                       }\r
+                       continue;                                                       // no need to update\r
+\r
+redraw:\r
+               //\r
+               // set the tiles it covers to 3, because those tiles are being\r
+               // updated\r
+               //\r
+                       updatespot = baseupdatespot;\r
+                       for (y=ytl;y<=yth;y++)\r
+                       {\r
+                               for (x=xtl;x<=xth;x++)\r
+                                       *updatespot++ = 3;\r
+                               updatespot += updatedelta;              // down to next line\r
+                       }\r
+               //\r
+               // draw it!\r
+               //\r
+                       height = sprite->height;\r
+                       sourceofs = sprite->sourceofs;\r
+                       if (porty<0)\r
+                       {\r
+                               height += porty;                                        // clip top off\r
+                               sourceofs -= porty*sprite->width;\r
+                               porty = 0;\r
+                       }\r
+                       else if (porty+height>PORTSCREENHIGH)\r
+                       {\r
+                               height = PORTSCREENHIGH - porty;    // clip bottom off\r
+                       }\r
+\r
+                       dest = bufferofs + ylookup[porty] + portx;\r
+\r
+                       switch (sprite->draw)\r
+                       {\r
+                       case spritedraw:\r
+                               VW_MaskBlock(grsegs[sprite->grseg], sourceofs,\r
+                                       dest,sprite->width,height,sprite->planesize);\r
+                               break;\r
+\r
+                       case maskdraw:\r
+                               break;\r
+\r
+                       }\r
+#ifdef PROFILE\r
+                       updatecount++;\r
+#endif\r
+\r
+\r
+               }\r
+       }\r
+}\r
+\r
+\r
+/*\r
+=====================\r
+=\r
+= RF_Refresh        CGA\r
+=\r
+= All routines will draw at the port at bufferofs, possibly copying from\r
+= the port at masterofs.  The EGA version then page flips, while the\r
+= CGA version updates the screen from the buffer port.\r
+=\r
+= Screenpage is the currently displayed page, not the one being drawn\r
+= Otherpage is the page to be worked with now\r
+=\r
+=====================\r
+*/\r
+\r
+void RF_Refresh (void)\r
+{\r
+       long newtime,oldtimecount;\r
+\r
+       RFL_AnimateTiles ();\r
+\r
+//\r
+// update newly scrolled on tiles and animated tiles from the master screen\r
+//\r
+       RFL_UpdateTiles ();\r
+       RFL_EraseBlocks ();\r
+\r
+//\r
+// Update is all 0 except where sprites have changed or new area has\r
+// been scrolled on.  Go through all sprites and update the ones that cover\r
+// a non 0 update tile\r
+//\r
+       RFL_UpdateSprites ();\r
+\r
+//\r
+// if the main program has a refresh hook set, call their function before\r
+// displaying the new page\r
+//\r
+       if (refreshvector)\r
+               refreshvector();\r
+\r
+//\r
+// update everything to the screen\r
+//\r
+       VW_CGAFullUpdate ();\r
+\r
+//\r
+// calculate tics since last refresh for adaptive timing\r
+//\r
+       RFL_CalcTics ();\r
+}\r
+\r
+#endif         // GRMODE == CGAGR\r
diff --git a/16/cawat/ID_RF.H b/16/cawat/ID_RF.H
new file mode 100644 (file)
index 0000000..8a3b0e2
--- /dev/null
@@ -0,0 +1,153 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_RF.H\r
+\r
+#define __ID_RF__\r
+\r
+#ifndef __ID_MM__\r
+#include "ID_MM.H"\r
+#endif\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                       CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define        MINTICS                         2\r
+#define        MAXTICS                         6\r
+#define DEMOTICS                       3\r
+\r
+#define        MAPBORDER                       2               // map border must be at least 1\r
+\r
+#define        MAXSPRITES                      50              // max tracked sprites\r
+#define        MAXANIMTILES            90              // max animating tiles on screen\r
+#define MAXANIMTYPES           50              // max different unique anim tiles on map\r
+\r
+#define        MAXMAPHEIGHT            200\r
+\r
+#define        PRIORITIES                      4\r
+#define        MASKEDTILEPRIORITY      3               // planes go: 0,1,2,MTILES,3\r
+\r
+#define TILEGLOBAL                     256\r
+#define PIXGLOBAL                      16\r
+\r
+#define        G_T_SHIFT                       8               // global >> ?? = tile\r
+#define        G_P_SHIFT                       4               // global >> ?? = pixels\r
+#define P_T_SHIFT                      4               // pixels >> ?? = tile\r
+\r
+#define        PORTTILESWIDE           21      // all drawing takes place inside a\r
+#define        PORTTILESHIGH           14              // non displayed port of this size\r
+\r
+//#define      PORTGLOBALWIDE          (21*TILEGLOBAL)\r
+//#define      PORTGLOBALHIGH          (14*TILEGLOBAL)\r
+\r
+#define UPDATEWIDE                     (PORTTILESWIDE+1)\r
+#define UPDATEHIGH                     PORTTILESHIGH\r
+\r
+\r
+//===========================================================================\r
+\r
+typedef enum {spritedraw,maskdraw} drawtype;\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                PUBLIC VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+\r
+extern boolean         compatability;                  // crippled refresh for wierdo SVGAs\r
+\r
+extern unsigned        tics;\r
+extern long            lasttimecount;\r
+\r
+extern unsigned        originxglobal,originyglobal;\r
+extern unsigned        originxtile,originytile;\r
+extern unsigned        originxscreen,originyscreen;\r
+\r
+extern unsigned        mapwidth,mapheight,mapbyteswide,mapwordswide\r
+                                       ,mapbytesextra,mapwordsextra;\r
+extern unsigned        mapbwidthtable[MAXMAPHEIGHT];\r
+\r
+extern unsigned        originxmin,originxmax,originymin,originymax;\r
+\r
+extern unsigned        masterofs;\r
+\r
+//\r
+// the floating update window is also used by the view manager for\r
+// double buffer tracking\r
+//\r
+\r
+extern byte            *updateptr;                             // current start of update window\r
+\r
+#if GRMODE == CGAGR\r
+extern byte            *baseupdateptr;\r
+#endif\r
+\r
+extern unsigned        blockstarts[UPDATEWIDE*UPDATEHIGH];\r
+extern unsigned        updatemapofs[UPDATEWIDE*UPDATEHIGH];\r
+extern unsigned        uwidthtable[UPDATEHIGH];                // lookup instead of multiple\r
+\r
+#define        UPDATETERMINATE 0x0301\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                PUBLIC FUNCTIONS\r
+\r
+=============================================================================\r
+*/\r
+\r
+void RF_Startup (void);\r
+void RF_Shutdown (void);\r
+\r
+void RF_FixOfs (void);\r
+void RF_NewMap (void);\r
+void RF_MarkTileGraphics (void);\r
+void RF_SetScrollBlock (int x, int y, boolean horizontal);\r
+void RF_NewPosition (unsigned x, unsigned y);\r
+void RF_Scroll (int x, int y);\r
+\r
+void RF_MapToMap (unsigned srcx, unsigned srcy,\r
+                                 unsigned destx, unsigned desty,\r
+                                 unsigned width, unsigned height);\r
+void RF_MemToMap (unsigned far *source, unsigned plane,\r
+                                 unsigned destx, unsigned desty,\r
+                                 unsigned width, unsigned height);\r
+\r
+void RF_ClearBlock (int        x, int y, int width, int height);\r
+void RF_RedrawBlock (int x, int y, int width, int height);\r
+\r
+void RF_PlaceSprite (void **user,unsigned globalx,unsigned globaly,\r
+       unsigned spritenumber, drawtype draw, int priority);\r
+void RF_RemoveSprite (void **user);\r
+\r
+void RF_CalcTics (void);\r
+\r
+void RF_Refresh (void);\r
+void RF_ForceRefresh (void);\r
+void RF_SetRefreshHook (void (*func) (void) );\r
+\r
+unsigned RF_FindFreeBuffer (void);\r
+\r
diff --git a/16/cawat/ID_RF_A.ASM b/16/cawat/ID_RF_A.ASM
new file mode 100644 (file)
index 0000000..77af0e9
--- /dev/null
@@ -0,0 +1,690 @@
+; Catacomb Armageddon Source Code\r
+; Copyright (C) 1993-2014 Flat Rock Software\r
+;\r
+; This program is free software; you can redistribute it and/or modify\r
+; it under the terms of the GNU General Public License as published by\r
+; the Free Software Foundation; either version 2 of the License, or\r
+; (at your option) any later version.\r
+;\r
+; This program is distributed in the hope that it will be useful,\r
+; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+; GNU General Public License for more details.\r
+;\r
+; You should have received a copy of the GNU General Public License along\r
+; with this program; if not, write to the Free Software Foundation, Inc.,\r
+; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+; ID_RF_A.ASM\r
+\r
+IDEAL\r
+MODEL  MEDIUM,C\r
+\r
+INCLUDE        "ID_ASM.EQU"\r
+\r
+;============================================================================\r
+\r
+TILESWIDE      =       21\r
+TILESHIGH      =       14\r
+\r
+UPDATESIZE     =       (TILESWIDE+1)*TILESHIGH+1\r
+\r
+DATASEG\r
+\r
+EXTRN  screenseg:WORD\r
+EXTRN  updateptr:WORD\r
+EXTRN  updatestart:WORD\r
+EXTRN  masterofs:WORD          ;start of master tile port\r
+EXTRN  bufferofs:WORD          ;start of current buffer port\r
+EXTRN  screenstart:WORD        ;starts of three screens (0/1/master) in EGA mem\r
+EXTRN  grsegs:WORD\r
+EXTRN  mapsegs:WORD\r
+EXTRN  originmap:WORD\r
+EXTRN  updatemapofs:WORD\r
+EXTRN  tinf:WORD                       ;seg pointer to map header and tile info\r
+EXTRN  blockstarts:WORD        ;offsets from bufferofs for each update block\r
+\r
+planemask      db      ?\r
+planenum       db      ?\r
+\r
+CODESEG\r
+\r
+screenstartcs  dw      ?               ;in code segment for accesability\r
+\r
+\r
+\r
+\r
+IFE GRMODE-CGAGR\r
+;============================================================================\r
+;\r
+; CGA refresh routines\r
+;\r
+;============================================================================\r
+\r
+TILEWIDTH      =       4\r
+\r
+;=================\r
+;\r
+; RFL_NewTile\r
+;\r
+; Draws a composit two plane tile to the master screen and sets the update\r
+; spot to 1 in both update pages, forcing the tile to be copied to the\r
+; view pages the next two refreshes\r
+;\r
+; Called to draw newlly scrolled on strips and animating tiles\r
+;\r
+;=================\r
+\r
+PROC   RFL_NewTile     updateoffset:WORD\r
+PUBLIC RFL_NewTile\r
+USES   SI,DI\r
+\r
+;\r
+; mark both update lists at this spot\r
+;\r
+       mov     di,[updateoffset]\r
+\r
+       mov     bx,[updateptr]                  ;start of update matrix\r
+       mov     [BYTE bx+di],1\r
+\r
+       mov     dx,SCREENWIDTH-TILEWIDTH                ;add to get to start of next line\r
+\r
+;\r
+; set di to the location in screenseg to draw the tile\r
+;\r
+       shl     di,1\r
+       mov     si,[updatemapofs+di]    ;offset in map from origin\r
+       add     si,[originmap]\r
+       mov     di,[blockstarts+di]             ;screen location for tile\r
+       add     di,[masterofs]\r
+\r
+;\r
+; set BX to the foreground tile number and SI to the background number\r
+; If either BX or SI = 0xFFFF, the tile does not need to be masked together\r
+; as one of the planes totally eclipses the other\r
+;\r
+       mov     es,[mapsegs+2]                  ;foreground plane\r
+       mov     bx,[es:si]\r
+       mov     es,[mapsegs]                    ;background plane\r
+       mov     si,[es:si]\r
+\r
+       mov     es,[screenseg]\r
+\r
+       or      bx,bx\r
+       jz      @@singletile\r
+       jmp     @@maskeddraw                    ;draw both together\r
+\r
+;=============\r
+;\r
+; Draw single background tile from main memory\r
+;\r
+;=============\r
+\r
+@@singletile:\r
+       shl     si,1\r
+       mov     ds,[grsegs+STARTTILE16*2+si]\r
+\r
+       xor     si,si                                   ;block is segment aligned\r
+\r
+REPT   15\r
+       movsw\r
+       movsw\r
+       add     di,dx\r
+ENDM\r
+       movsw\r
+       movsw\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+       ret\r
+\r
+\r
+;=========\r
+;\r
+; Draw a masked tile combo\r
+; Interupts are disabled and the stack segment is reassigned\r
+;\r
+;=========\r
+@@maskeddraw:\r
+       cli                                                     ; don't allow ints when SS is set\r
+       shl     bx,1\r
+       mov     ss,[grsegs+STARTTILE16M*2+bx]\r
+       shl     si,1\r
+       mov     ds,[grsegs+STARTTILE16*2+si]\r
+\r
+       xor     si,si                                   ;first word of tile data\r
+\r
+REPT   16\r
+       mov     ax,[si]                                 ;background tile\r
+       and     ax,[ss:si]                              ;mask\r
+       or      ax,[ss:si+64]                   ;masked data\r
+       stosw\r
+       mov     ax,[si+2]                               ;background tile\r
+       and     ax,[ss:si+2]                    ;mask\r
+       or      ax,[ss:si+66]                   ;masked data\r
+       stosw\r
+       add     si,4\r
+       add     di,dx\r
+ENDM\r
+\r
+       mov     ax,@DATA\r
+       mov     ss,ax\r
+       sti\r
+       mov     ds,ax\r
+       ret\r
+ENDP\r
+\r
+ENDIF\r
+\r
+\r
+\r
+IFE GRMODE-EGAGR\r
+;===========================================================================\r
+;\r
+; EGA refresh routines\r
+;\r
+;===========================================================================\r
+\r
+TILEWIDTH      =       2\r
+\r
+;=================\r
+;\r
+; RFL_NewTile\r
+;\r
+; Draws a composit two plane tile to the master screen and sets the update\r
+; spot to 1 in both update pages, forcing the tile to be copied to the\r
+; view pages the next two refreshes\r
+;\r
+; Called to draw newlly scrolled on strips and animating tiles\r
+;\r
+; Assumes write mode 0\r
+;\r
+;=================\r
+\r
+PROC   RFL_NewTile     updateoffset:WORD\r
+PUBLIC RFL_NewTile\r
+USES   SI,DI\r
+\r
+;\r
+; mark both update lists at this spot\r
+;\r
+       mov     di,[updateoffset]\r
+\r
+       mov     bx,[updatestart]                ;page 0 pointer\r
+       mov     [BYTE bx+di],1\r
+       mov     bx,[updatestart+2]              ;page 1 pointer\r
+       mov     [BYTE bx+di],1\r
+\r
+;\r
+; set screenstartcs to the location in screenseg to draw the tile\r
+;\r
+       shl     di,1\r
+       mov     si,[updatemapofs+di]    ;offset in map from origin\r
+       add     si,[originmap]\r
+       mov     di,[blockstarts+di]             ;screen location for tile\r
+       add     di,[masterofs]\r
+       mov     [cs:screenstartcs],di\r
+\r
+;\r
+; set BX to the foreground tile number and SI to the background number\r
+; If either BX or SI = 0xFFFF, the tile does not need to be masked together\r
+; as one of the planes totally eclipses the other\r
+;\r
+       mov     es,[mapsegs+2]                  ;foreground plane\r
+       mov     bx,[es:si]\r
+       mov     es,[mapsegs]                    ;background plane\r
+       mov     si,[es:si]\r
+\r
+       mov     es,[screenseg]\r
+       mov     dx,SC_INDEX                             ;for stepping through map mask planes\r
+\r
+       or      bx,bx\r
+       jz      @@singletile\r
+       jmp     @@maskeddraw                    ;draw both together\r
+\r
+;=========\r
+;\r
+; No foreground tile, so draw a single background tile.\r
+;\r
+;=========\r
+@@singletile:\r
+\r
+       mov     bx,SCREENWIDTH-2                ;add to get to start of next line\r
+       shl     si,1\r
+\r
+       mov     ax,[cs:screenstartcs]\r
+       mov     ds,[grsegs+STARTTILE16*2+si]\r
+\r
+       xor     si,si                                   ;block is segment aligned\r
+\r
+       mov     ax,SC_MAPMASK+0001b*256 ;map mask for plane 0\r
+\r
+       mov     cx,4                                    ;draw four planes\r
+@@planeloop:\r
+       mov     dx,SC_INDEX\r
+       WORDOUT\r
+\r
+       mov     di,[cs:screenstartcs]   ;start at same place in all planes\r
+\r
+REPT   15\r
+       movsw\r
+       add     di,bx\r
+ENDM\r
+       movsw\r
+\r
+       shl     ah,1                                    ;shift plane mask over for next plane\r
+       loop    @@planeloop\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+       ret\r
+\r
+\r
+;=========\r
+;\r
+; Draw a masked tile combo\r
+; Interupts are disabled and the stack segment is reassigned\r
+;\r
+;=========\r
+@@maskeddraw:\r
+       cli                                                     ; don't allow ints when SS is set\r
+       shl     bx,1\r
+       mov     ss,[grsegs+STARTTILE16M*2+bx]\r
+       shl     si,1\r
+       mov     ds,[grsegs+STARTTILE16*2+si]\r
+\r
+       xor     si,si                                   ;first word of tile data\r
+\r
+       mov     ax,SC_MAPMASK+0001b*256 ;map mask for plane 0\r
+\r
+       mov     di,[cs:screenstartcs]\r
+@@planeloopm:\r
+       WORDOUT\r
+tileofs                =       0\r
+lineoffset     =       0\r
+REPT   16\r
+       mov     bx,[si+tileofs]                 ;background tile\r
+       and     bx,[ss:tileofs]                 ;mask\r
+       or      bx,[ss:si+tileofs+32]   ;masked data\r
+       mov     [es:di+lineoffset],bx\r
+tileofs                =       tileofs + 2\r
+lineoffset     =       lineoffset + SCREENWIDTH\r
+ENDM\r
+       add     si,32\r
+       shl     ah,1                                    ;shift plane mask over for next plane\r
+       cmp     ah,10000b\r
+       je      @@done                                  ;drawn all four planes\r
+       jmp     @@planeloopm\r
+\r
+@@done:\r
+       mov     ax,@DATA\r
+       mov     ss,ax\r
+       sti\r
+       mov     ds,ax\r
+       ret\r
+ENDP\r
+\r
+ENDIF\r
+\r
+IFE GRMODE-VGAGR\r
+;============================================================================\r
+;\r
+; VGA refresh routines\r
+;\r
+;============================================================================\r
+\r
+\r
+ENDIF\r
+\r
+\r
+;============================================================================\r
+;\r
+; reasonably common refresh routines\r
+;\r
+;============================================================================\r
+\r
+\r
+;=================\r
+;\r
+; RFL_UpdateTiles\r
+;\r
+; Scans through the update matrix pointed to by updateptr, looking for 1s.\r
+; A 1 represents a tile that needs to be copied from the master screen to the\r
+; current screen (a new row or an animated tiled).  If more than one adjacent\r
+; tile in a horizontal row needs to be copied, they will be copied as a group.\r
+;\r
+; Assumes write mode 1\r
+;\r
+;=================\r
+\r
+\r
+; AX   0/1 for scasb, temp for segment register transfers\r
+; BX    width for block copies\r
+; CX   REP counter\r
+; DX   line width deltas\r
+; SI   source for copies\r
+; DI   scas dest / movsb dest\r
+; BP   pointer to UPDATETERMINATE\r
+;\r
+; DS\r
+; ES\r
+; SS\r
+\r
+PROC   RFL_UpdateTiles\r
+PUBLIC RFL_UpdateTiles\r
+USES   SI,DI,BP\r
+\r
+       jmp     SHORT @@realstart\r
+@@done:\r
+;\r
+; all tiles have been scanned\r
+;\r
+       ret\r
+\r
+@@realstart:\r
+       mov     di,[updateptr]\r
+       mov     bp,(TILESWIDE+1)*TILESHIGH+1\r
+       add     bp,di                                   ; when di = bx, all tiles have been scanned\r
+       push    di\r
+       mov     cx,-1                                   ; definately scan the entire thing\r
+\r
+;\r
+; scan for a 1 in the update list, meaning a tile needs to be copied\r
+; from the master screen to the current screen\r
+;\r
+@@findtile:\r
+       pop     di                                              ; place to continue scaning from\r
+       mov     ax,ss\r
+       mov     es,ax                                   ; search in the data segment\r
+       mov     ds,ax\r
+       mov al,1\r
+       repne   scasb\r
+       cmp     di,bp\r
+       je      @@done\r
+\r
+       cmp     [BYTE di],al\r
+       jne     @@singletile\r
+       jmp     @@tileblock\r
+\r
+;============\r
+;\r
+; copy a single tile\r
+;\r
+;============\r
+EVEN\r
+@@singletile:\r
+       inc     di                                              ; we know the next tile is nothing\r
+       push    di                                      ; save off the spot being scanned\r
+       sub     di,[updateptr]\r
+       shl     di,1\r
+       mov     di,[blockstarts-4+di]   ; start of tile location on screen\r
+       mov     si,di\r
+       add     di,[bufferofs]                  ; dest in current screen\r
+       add     si,[masterofs]                  ; source in master screen\r
+\r
+       mov     dx,SCREENWIDTH-TILEWIDTH\r
+       mov     ax,[screenseg]\r
+       mov     ds,ax\r
+       mov     es,ax\r
+\r
+;--------------------------\r
+\r
+IFE GRMODE-CGAGR\r
+\r
+REPT   15\r
+       movsw\r
+       movsw\r
+       add     si,dx\r
+       add     di,dx\r
+ENDM\r
+       movsw\r
+       movsw\r
+\r
+ENDIF\r
+\r
+;--------------------------\r
+\r
+IFE GRMODE-EGAGR\r
+\r
+REPT   15\r
+       movsb\r
+       movsb\r
+       add     si,dx\r
+       add     di,dx\r
+ENDM\r
+       movsb\r
+       movsb\r
+\r
+ENDIF\r
+\r
+;--------------------------\r
+\r
+       jmp     @@findtile\r
+\r
+;============\r
+;\r
+; more than one tile in a row needs to be updated, so do it as a group\r
+;\r
+;============\r
+EVEN\r
+@@tileblock:\r
+       mov     dx,di                                   ; hold starting position + 1 in dx\r
+       inc     di                                              ; we know the next tile also gets updated\r
+       repe    scasb                           ; see how many more in a row\r
+       push    di                                      ; save off the spot being scanned\r
+\r
+       mov     bx,di\r
+       sub     bx,dx                                   ; number of tiles in a row\r
+       shl     bx,1                                    ; number of bytes / row\r
+\r
+       mov     di,dx                                   ; lookup position of start tile\r
+       sub     di,[updateptr]\r
+       shl     di,1\r
+       mov     di,[blockstarts-2+di]   ; start of tile location\r
+       mov     si,di\r
+       add     di,[bufferofs]                  ; dest in current screen\r
+       add     si,[masterofs]                  ; source in master screen\r
+\r
+       mov     dx,SCREENWIDTH\r
+       sub     dx,bx                                   ; offset to next line on screen\r
+IFE GRMODE-CGAGR\r
+       sub     dx,bx                                   ; bx is words wide in CGA tiles\r
+ENDIF\r
+\r
+       mov     ax,[screenseg]\r
+       mov     ds,ax\r
+       mov     es,ax\r
+\r
+REPT   15\r
+       mov     cx,bx\r
+IFE GRMODE-CGAGR\r
+       rep     movsw\r
+ENDIF\r
+IFE GRMODE-EGAGR\r
+       rep     movsb\r
+ENDIF\r
+       add     si,dx\r
+       add     di,dx\r
+ENDM\r
+       mov     cx,bx\r
+IFE GRMODE-CGAGR\r
+       rep     movsw\r
+ENDIF\r
+IFE GRMODE-EGAGR\r
+       rep     movsb\r
+ENDIF\r
+\r
+       dec     cx                                              ; was 0 from last rep movsb, now $ffff for scasb\r
+       jmp     @@findtile\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+\r
+\r
+;=================\r
+;\r
+; RFL_MaskForegroundTiles\r
+;\r
+; Scan through update looking for 3's.  If the foreground tile there is a\r
+; masked foreground tile, draw it to the screen\r
+;\r
+;=================\r
+\r
+PROC   RFL_MaskForegroundTiles\r
+PUBLIC RFL_MaskForegroundTiles\r
+USES   SI,DI,BP\r
+       jmp     SHORT @@realstart\r
+@@done:\r
+;\r
+; all tiles have been scanned\r
+;\r
+       ret\r
+\r
+@@realstart:\r
+       mov     di,[updateptr]\r
+       mov     bp,(TILESWIDE+1)*TILESHIGH+2\r
+       add     bp,di                                   ; when di = bx, all tiles have been scanned\r
+       push    di\r
+       mov     cx,-1                                   ; definately scan the entire thing\r
+;\r
+; scan for a 3 in the update list\r
+;\r
+@@findtile:\r
+       mov     ax,ss\r
+       mov     es,ax                                   ; scan in the data segment\r
+       mov     al,3\r
+       pop     di                                              ; place to continue scaning from\r
+       repne   scasb\r
+       cmp     di,bp\r
+       je      @@done\r
+\r
+;============\r
+;\r
+; found a tile, see if it needs to be masked on\r
+;\r
+;============\r
+\r
+       push    di\r
+\r
+       sub     di,[updateptr]\r
+       shl     di,1\r
+       mov     si,[updatemapofs-2+di]  ; offset from originmap\r
+       add     si,[originmap]\r
+\r
+       mov     es,[mapsegs+2]                  ; foreground map plane segment\r
+       mov     si,[es:si]                              ; foreground tile number\r
+\r
+       or      si,si\r
+       jz      @@findtile                              ; 0 = no foreground tile\r
+\r
+       mov     bx,si\r
+       add     bx,INTILE                               ;INTILE tile info table\r
+       mov     es,[tinf]\r
+       test    [BYTE PTR es:bx],80h            ;high bit = masked tile\r
+       jz      @@findtile\r
+\r
+;-------------------\r
+\r
+IFE GRMODE-CGAGR\r
+;=================\r
+;\r
+; mask the tile CGA\r
+;\r
+;=================\r
+\r
+       mov     di,[blockstarts-2+di]\r
+       add     di,[bufferofs]\r
+       mov     es,[screenseg]\r
+       shl     si,1\r
+       mov     ds,[grsegs+STARTTILE16M*2+si]\r
+\r
+       mov     bx,64                                   ;data starts 64 bytes after mask\r
+\r
+       xor     si,si\r
+\r
+lineoffset     =       0\r
+REPT   16\r
+       mov     ax,[es:di+lineoffset]   ;background\r
+       and     ax,[si]                                 ;mask\r
+       or      ax,[si+bx]                              ;masked data\r
+       mov     [es:di+lineoffset],ax   ;background\r
+       inc     si\r
+       inc     si\r
+       mov     ax,[es:di+lineoffset+2] ;background\r
+       and     ax,[si]                                 ;mask\r
+       or      ax,[si+bx]                              ;masked data\r
+       mov     [es:di+lineoffset+2],ax ;background\r
+       inc     si\r
+       inc     si\r
+lineoffset     =       lineoffset + SCREENWIDTH\r
+ENDM\r
+ENDIF\r
+\r
+;-------------------\r
+\r
+IFE GRMODE-EGAGR\r
+;=================\r
+;\r
+; mask the tile\r
+;\r
+;=================\r
+\r
+       mov     [BYTE planemask],1\r
+       mov     [BYTE planenum],0\r
+\r
+       mov     di,[blockstarts-2+di]\r
+       add     di,[bufferofs]\r
+       mov     [cs:screenstartcs],di\r
+       mov     es,[screenseg]\r
+       shl     si,1\r
+       mov     ds,[grsegs+STARTTILE16M*2+si]\r
+\r
+       mov     bx,32                                   ;data starts 32 bytes after mask\r
+\r
+@@planeloopm:\r
+       mov     dx,SC_INDEX\r
+       mov     al,SC_MAPMASK\r
+       mov     ah,[ss:planemask]\r
+       WORDOUT\r
+       mov     dx,GC_INDEX\r
+       mov     al,GC_READMAP\r
+       mov     ah,[ss:planenum]\r
+       WORDOUT\r
+\r
+       xor     si,si\r
+       mov     di,[cs:screenstartcs]\r
+lineoffset     =       0\r
+REPT   16\r
+       mov     cx,[es:di+lineoffset]   ;background\r
+       and     cx,[si]                                 ;mask\r
+       or      cx,[si+bx]                              ;masked data\r
+       inc     si\r
+       inc     si\r
+       mov     [es:di+lineoffset],cx\r
+lineoffset     =       lineoffset + SCREENWIDTH\r
+ENDM\r
+       add     bx,32                                   ;the mask is now further away\r
+       inc     [ss:planenum]\r
+       shl     [ss:planemask],1                ;shift plane mask over for next plane\r
+       cmp     [ss:planemask],10000b   ;done all four planes?\r
+       je      @@drawn                                 ;drawn all four planes\r
+       jmp     @@planeloopm\r
+\r
+@@drawn:\r
+ENDIF\r
+\r
+;-------------------\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax\r
+       mov     cx,-1                                   ;definately scan the entire thing\r
+\r
+       jmp     @@findtile\r
+\r
+ENDP\r
+\r
+\r
+END\r
+\r
diff --git a/16/cawat/ID_SD.C b/16/cawat/ID_SD.C
new file mode 100644 (file)
index 0000000..c62a0aa
--- /dev/null
@@ -0,0 +1,1319 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+//     ID Engine\r
+//     ID_SD.c - Sound Manager\r
+//     v1.1d1\r
+//     By Jason Blochowiak\r
+//\r
+\r
+//\r
+//     This module handles dealing with generating sound on the appropriate\r
+//             hardware\r
+//\r
+//     Depends on: User Mgr (for parm checking)\r
+//\r
+//     Globals:\r
+//             For User Mgr:\r
+//                     SoundSourcePresent - Sound Source thingie present?\r
+//                     SoundBlasterPresent - SoundBlaster card present?\r
+//                     AdLibPresent - AdLib card present?\r
+//                     SoundMode - What device is used for sound effects\r
+//                             (Use SM_SetSoundMode() to set)\r
+//                     MusicMode - What device is used for music\r
+//                             (Use SM_SetMusicMode() to set)\r
+//             For Cache Mgr:\r
+//                     NeedsDigitized - load digitized sounds?\r
+//                     NeedsMusic - load music?\r
+//\r
+\r
+#define USE_MUSIC      0\r
+\r
+#pragma hdrstop                // Wierdo thing with MUSE\r
+\r
+#include <dos.h>\r
+\r
+#ifdef _MUSE_      // Will be defined in ID_Types.h\r
+#include "ID_SD.h"\r
+#else\r
+#include "ID_HEADS.H"\r
+#endif\r
+#pragma        hdrstop\r
+#pragma        warn    -pia\r
+\r
+#define        SDL_SoundFinished()     {SoundNumber = SoundPriority = 0;}\r
+\r
+// Macros for AdLib stuff\r
+#define        selreg(n)       outportb(0x388,n)\r
+#define        writereg(n)     outportb(0x389,n)\r
+#define        readstat()      inportb(0x388)\r
+\r
+//     Global variables\r
+       boolean         SoundSourcePresent,SoundBlasterPresent,AdLibPresent,\r
+                               NeedsDigitized,NeedsMusic;\r
+       SDMode          SoundMode;\r
+       SMMode          MusicMode;\r
+       longword        TimeCount;\r
+       word            HackCount;\r
+       word            *SoundTable;    // Really * _seg *SoundTable, but that don't work\r
+       boolean         ssIsTandy;\r
+       word            ssPort = 2;\r
+\r
+//     Internal variables\r
+static boolean                 SD_Started;\r
+static boolean                 TimerDone;\r
+static word                    TimerVal,TimerDelay10,TimerDelay25,TimerDelay100;\r
+static longword                TimerDivisor,TimerCount;\r
+static char                    *ParmStrings[] =\r
+                                               {\r
+                                                       "noal",\r
+                                                       nil\r
+                                               };\r
+static void                    (*SoundUserHook)(void);\r
+static word                    SoundNumber,SoundPriority;\r
+static void interrupt  (*t0OldService)(void);\r
+//static       word                    t0CountTable[] = {8,8,8,8,40,40};\r
+static long                    LocalTime;\r
+\r
+//     PC Sound variables\r
+static byte                    pcLastSample,far *pcSound;\r
+static longword                pcLengthLeft;\r
+static word                    pcSoundLookup[255];\r
+\r
+//     AdLib variables\r
+static boolean                 alNoCheck;\r
+static byte                    far *alSound;\r
+static word                    alBlock;\r
+static longword                alLengthLeft;\r
+static longword                alTimeCount;\r
+static Instrument              alZeroInst;\r
+\r
+// This table maps channel numbers to carrier and modulator op cells\r
+static byte                    carriers[9] =  { 3, 4, 5,11,12,13,19,20,21},\r
+                                               modifiers[9] = { 0, 1, 2, 8, 9,10,16,17,18},\r
+// This table maps percussive voice numbers to op cells\r
+                                               pcarriers[5] = {19,0xff,0xff,0xff,0xff},\r
+                                               pmodifiers[5] = {16,17,18,20,21};\r
+\r
+//     Sequencer variables\r
+static boolean                 sqActive;\r
+static word                    alFXReg;\r
+static ActiveTrack             *tracks[sqMaxTracks],\r
+                                               mytracks[sqMaxTracks];\r
+static word                    sqMode,sqFadeStep;\r
+static word                    far *sqHack,far *sqHackPtr,sqHackLen,sqHackSeqLen;\r
+static long                    sqHackTime;\r
+\r
+//     Internal routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_SetTimer0() - Sets system timer 0 to the specified speed\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+#pragma        argsused\r
+static void\r
+SDL_SetTimer0(word speed)\r
+{\r
+#ifndef TPROF  // If using Borland's profiling, don't screw with the timer\r
+       outportb(0x43,0x36);                            // Change timer 0\r
+       outportb(0x40,speed);\r
+       outportb(0x40,speed >> 8);\r
+       TimerDivisor = speed;\r
+#else\r
+       TimerDivisor = 0x10000;\r
+#endif\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_SetIntsPerSec() - Uses SDL_SetTimer0() to set the number of\r
+//             interrupts generated by system timer 0 per second\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_SetIntsPerSec(word ints)\r
+{\r
+       SDL_SetTimer0(1192030 / ints);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_TimingService() - Used by SDL_InitDelay() to determine a timing\r
+//             value for the current system that we're running on\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void interrupt\r
+SDL_TimingService(void)\r
+{\r
+       TimerVal = _CX;\r
+       TimerDone++;\r
+\r
+       outportb(0x20,0x20);                            // Ack interrupt\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_InitDelay() - Sets up TimerDelay's for SDL_Delay()\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_InitDelay(void)\r
+{\r
+       int             i;\r
+       word    timer;\r
+\r
+       setvect(8,SDL_TimingService);           // Set to my timer 0 ISR\r
+\r
+       SDL_SetIntsPerSec(1000);                        // Time 1ms\r
+\r
+       for (i = 0,timer = 0;i < 10;i++)        // Do timing test 10 times\r
+       {\r
+       asm     xor             dx,dx                                   // Zero DX\r
+       asm     mov             cx,0xffff                               // Put starting value in CX\r
+       asm     mov             [TimerDone],cx                  // TimerDone = false - 1\r
+startloop:\r
+       asm     or              [TimerDone],0\r
+       asm     jnz             startloop                               // Make sure we're at the start\r
+loop:\r
+       asm     test    [TimerDone],1                   // See if TimerDone flag got hit\r
+       asm     jnz             done                                    // Yep - drop out of the loop\r
+       asm     loop    loop\r
+done:\r
+\r
+               if (0xffff - TimerVal > timer)\r
+                       timer = 0xffff - TimerVal;\r
+       }\r
+       timer += timer / 2;                                     // Use some slop\r
+       TimerDelay10 =  timer / (1000 / 10);\r
+       TimerDelay25 =  timer / (1000 / 25);\r
+       TimerDelay100 = timer / (1000 / 100);\r
+\r
+       SDL_SetTimer0(0);                                       // Reset timer 0\r
+\r
+       setvect(8,t0OldService);                        // Set back to old ISR\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_Delay() - Delays the specified amount of time\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_Delay(word delay)\r
+{\r
+       if (!delay)\r
+               return;\r
+\r
+asm    mov             cx,[delay]\r
+loop:\r
+asm    test    [TimerDone],0   // Useless code - just for timing equivilency\r
+asm    jnz             done\r
+asm    loop    loop\r
+done:;\r
+}\r
+\r
+//\r
+//     PC Sound code\r
+//\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_PCPlaySound() - Plays the specified sound on the PC speaker\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+#ifdef _MUSE_\r
+void\r
+#else\r
+static void\r
+#endif\r
+SDL_PCPlaySound(PCSound far *sound)\r
+{\r
+asm    pushf\r
+asm    cli\r
+\r
+       pcLastSample = -1;\r
+       pcLengthLeft = sound->common.length;\r
+       pcSound = sound->data;\r
+\r
+asm    popf\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_PCStopSound() - Stops the current sound playing on the PC Speaker\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+#ifdef _MUSE_\r
+void\r
+#else\r
+static void\r
+#endif\r
+SDL_PCStopSound(void)\r
+{\r
+asm    pushf\r
+asm    cli\r
+\r
+       (long)pcSound = 0;\r
+\r
+asm    in      al,0x61                 // Turn the speaker off\r
+asm    and     al,0xfd                 // ~2\r
+asm    out     0x61,al\r
+\r
+asm    popf\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_PCService() - Handles playing the next sample in a PC sound\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_PCService(void)\r
+{\r
+       byte    s;\r
+       word    t;\r
+\r
+       if (pcSound)\r
+       {\r
+               s = *pcSound++;\r
+               if (s != pcLastSample)\r
+               {\r
+               asm     pushf\r
+               asm     cli\r
+\r
+                       pcLastSample = s;\r
+                       if (s)                                  // We have a frequency!\r
+                       {\r
+                               t = pcSoundLookup[s];\r
+                       asm     mov     bx,[t]\r
+\r
+                       asm     mov     al,0xb6                 // Write to channel 2 (speaker) timer\r
+                       asm     out     43h,al\r
+                       asm     mov     al,bl\r
+                       asm     out     42h,al                  // Low byte\r
+                       asm     mov     al,bh\r
+                       asm     out     42h,al                  // High byte\r
+\r
+                       asm     in      al,0x61                 // Turn the speaker & gate on\r
+                       asm     or      al,3\r
+                       asm     out     0x61,al\r
+                       }\r
+                       else                                    // Time for some silence\r
+                       {\r
+                       asm     in      al,0x61                 // Turn the speaker & gate off\r
+                       asm     and     al,0xfc                 // ~3\r
+                       asm     out     0x61,al\r
+                       }\r
+\r
+               asm     popf\r
+               }\r
+\r
+               if (!(--pcLengthLeft))\r
+               {\r
+                       SDL_PCStopSound();\r
+                       SDL_SoundFinished();\r
+               }\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_ShutPC() - Turns off the pc speaker\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_ShutPC(void)\r
+{\r
+asm    pushf\r
+asm    cli\r
+\r
+       pcSound = 0;\r
+\r
+asm    in      al,0x61                 // Turn the speaker & gate off\r
+asm    and     al,0xfc                 // ~3\r
+asm    out     0x61,al\r
+\r
+asm    popf\r
+}\r
+\r
+//     AdLib Code\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     alOut(n,b) - Puts b in AdLib card register n\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+alOut(byte n,byte b)\r
+{\r
+asm    pushf\r
+asm    cli\r
+\r
+asm    mov             dx,0x388\r
+asm    mov             al,[n]\r
+asm    out             dx,al\r
+#if 0\r
+       SDL_Delay(TimerDelay10);\r
+#else\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+#endif\r
+\r
+asm    mov             dx,0x389\r
+asm    mov             al,[b]\r
+asm    out             dx,al\r
+\r
+asm    popf\r
+\r
+#if 0\r
+       SDL_Delay(TimerDelay25);\r
+#else\r
+asm    mov     dx,0x388\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+asm    in      al, dx\r
+#endif\r
+}\r
+\r
+#if 0\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_SetInstrument() - Puts an instrument into a generator\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_SetInstrument(int track,int which,Instrument far *inst,boolean percussive)\r
+{\r
+       byte            c,m;\r
+\r
+       if (percussive)\r
+       {\r
+               c = pcarriers[which];\r
+               m = pmodifiers[which];\r
+       }\r
+       else\r
+       {\r
+               c = carriers[which];\r
+               m = modifiers[which];\r
+       }\r
+\r
+       tracks[track - 1]->inst = *inst;\r
+       tracks[track - 1]->percussive = percussive;\r
+\r
+       alOut(m + alChar,inst->mChar);\r
+       alOut(m + alScale,inst->mScale);\r
+       alOut(m + alAttack,inst->mAttack);\r
+       alOut(m + alSus,inst->mSus);\r
+       alOut(m + alWave,inst->mWave);\r
+\r
+       // Most percussive instruments only use one cell\r
+       if (c != 0xff)\r
+       {\r
+               alOut(c + alChar,inst->cChar);\r
+               alOut(c + alScale,inst->cScale);\r
+               alOut(c + alAttack,inst->cAttack);\r
+               alOut(c + alSus,inst->cSus);\r
+               alOut(c + alWave,inst->cWave);\r
+       }\r
+\r
+       alOut(which + alFeedCon,inst->nConn);   // DEBUG - I think this is right\r
+}\r
+#endif\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_ALStopSound() - Turns off any sound effects playing through the\r
+//             AdLib card\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+#ifdef _MUSE_\r
+void\r
+#else\r
+static void\r
+#endif\r
+SDL_ALStopSound(void)\r
+{\r
+asm    pushf\r
+asm    cli\r
+\r
+       (long)alSound = 0;\r
+       alOut(alFreqH + 0,0);\r
+\r
+asm    popf\r
+}\r
+\r
+static void\r
+SDL_AlSetFXInst(Instrument far *inst)\r
+{\r
+       byte            c,m;\r
+\r
+       m = modifiers[0];\r
+       c = carriers[0];\r
+       alOut(m + alChar,inst->mChar);\r
+       alOut(m + alScale,inst->mScale);\r
+       alOut(m + alAttack,inst->mAttack);\r
+       alOut(m + alSus,inst->mSus);\r
+       alOut(m + alWave,inst->mWave);\r
+       alOut(c + alChar,inst->cChar);\r
+       alOut(c + alScale,inst->cScale);\r
+       alOut(c + alAttack,inst->cAttack);\r
+       alOut(c + alSus,inst->cSus);\r
+       alOut(c + alWave,inst->cWave);\r
+       // DEBUG!!! - I just put this in\r
+//     alOut(alFeedCon,inst->nConn);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_ALPlaySound() - Plays the specified sound on the AdLib card\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+#ifdef _MUSE_\r
+void\r
+#else\r
+static void\r
+#endif\r
+SDL_ALPlaySound(AdLibSound far *sound)\r
+{\r
+       Instrument      far *inst;\r
+\r
+       SDL_ALStopSound();\r
+\r
+asm    pushf\r
+asm    cli\r
+\r
+       alLengthLeft = sound->common.length;\r
+       alSound = sound->data;\r
+       alBlock = ((sound->block & 7) << 2) | 0x20;\r
+       inst = &sound->inst;\r
+\r
+       if (!(inst->mSus | inst->cSus))\r
+       {\r
+       asm     popf\r
+               Quit("SDL_ALPlaySound() - Bad instrument");\r
+       }\r
+\r
+       SDL_AlSetFXInst(inst);\r
+\r
+asm    popf\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_ALSoundService() - Plays the next sample out through the AdLib card\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_ALSoundService(void)\r
+{\r
+       byte    s;\r
+\r
+       if (alSound)\r
+       {\r
+               s = *alSound++;\r
+               if (!s)\r
+                       alOut(alFreqH + 0,0);\r
+               else\r
+               {\r
+                       alOut(alFreqL + 0,s);\r
+                       alOut(alFreqH + 0,alBlock);\r
+               }\r
+\r
+               if (!(--alLengthLeft))\r
+               {\r
+                       (long)alSound = 0;\r
+                       alOut(alFreqH + 0,0);\r
+                       SDL_SoundFinished();\r
+               }\r
+       }\r
+}\r
+\r
+#if 0\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_SelectMeasure() - sets up sequencing variables for a given track\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_SelectMeasure(ActiveTrack *track)\r
+{\r
+       track->seq = track->moods[track->mood];\r
+       track->nextevent = 0;\r
+}\r
+#endif\r
+\r
+static void\r
+SDL_ALService(void)\r
+{\r
+       byte    a,v;\r
+       word    w;\r
+\r
+       if (!sqActive)\r
+               return;\r
+\r
+       while (sqHackLen && (sqHackTime <= alTimeCount))\r
+       {\r
+               w = *sqHackPtr++;\r
+               sqHackTime = alTimeCount + *sqHackPtr++;\r
+       asm     mov     dx,[w]\r
+       asm     mov     [a],dl\r
+       asm     mov     [v],dh\r
+               alOut(a,v);\r
+               sqHackLen -= 4;\r
+       }\r
+       alTimeCount++;\r
+       if (!sqHackLen)\r
+       {\r
+               sqHackPtr = (word far *)sqHack;\r
+               sqHackLen = sqHackSeqLen;\r
+               alTimeCount = sqHackTime = 0;\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_ShutAL() - Shuts down the AdLib card for sound effects\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_ShutAL(void)\r
+{\r
+asm    pushf\r
+asm    cli\r
+\r
+       alOut(alEffects,0);\r
+       alOut(alFreqH + 0,0);\r
+       SDL_AlSetFXInst(&alZeroInst);\r
+       alSound = 0;\r
+\r
+asm    popf\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_CleanAL() - Totally shuts down the AdLib card\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_CleanAL(void)\r
+{\r
+       int     i;\r
+\r
+asm    pushf\r
+asm    cli\r
+\r
+       alOut(alEffects,0);\r
+       for (i = 1;i < 0xf5;i++)\r
+               alOut(i,0);\r
+\r
+asm    popf\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_StartAL() - Starts up the AdLib card for sound effects\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_StartAL(void)\r
+{\r
+       alFXReg = 0;\r
+       alOut(alEffects,alFXReg);\r
+       SDL_AlSetFXInst(&alZeroInst);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_DetectAdLib() - Determines if there's an AdLib (or SoundBlaster\r
+//             emulating an AdLib) present\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+SDL_DetectAdLib(void)\r
+{\r
+       byte    status1,status2;\r
+       int             i;\r
+\r
+       alOut(4,0x60);  // Reset T1 & T2\r
+       alOut(4,0x80);  // Reset IRQ\r
+       status1 = readstat();\r
+       alOut(2,0xff);  // Set timer 1\r
+       alOut(4,0x21);  // Start timer 1\r
+       SDL_Delay(TimerDelay100);\r
+\r
+       status2 = readstat();\r
+       alOut(4,0x60);\r
+       alOut(4,0x80);\r
+\r
+       if (((status1 & 0xe0) == 0x00) && ((status2 & 0xe0) == 0xc0))\r
+       {\r
+               for (i = 1;i <= 0xf5;i++)       // Zero all the registers\r
+                       alOut(i,0);\r
+\r
+               alOut(1,0x20);  // Set WSE=1\r
+               alOut(8,0);             // Set CSM=0 & SEL=0\r
+\r
+               return(true);\r
+       }\r
+       else\r
+               return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_t0Service() - My timer 0 ISR which handles the different timings and\r
+//             dispatches to whatever other routines are appropriate\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void interrupt\r
+SDL_t0Service(void)\r
+{\r
+static word    count = 1;\r
+\r
+#if 0  // for debugging\r
+asm    mov     dx,STATUS_REGISTER_1\r
+asm    in      al,dx\r
+asm    mov     dx,ATR_INDEX\r
+asm    mov     al,ATR_OVERSCAN\r
+asm    out     dx,al\r
+asm    mov     al,4    // red\r
+asm    out     dx,al\r
+#endif\r
+\r
+       HackCount++;\r
+\r
+#if USE_MUSIC\r
+       if (MusicMode == smm_AdLib)\r
+       {\r
+               SDL_ALService();\r
+               if (!(++count & 7))\r
+               {\r
+                       LocalTime++;\r
+                       TimeCount++;\r
+                       if (SoundUserHook)\r
+                               SoundUserHook();\r
+               }\r
+               if (!(count & 3))\r
+               {\r
+                       switch (SoundMode)\r
+                       {\r
+                       case sdm_PC:\r
+                               SDL_PCService();\r
+                               break;\r
+                       case sdm_AdLib:\r
+                               SDL_ALSoundService();\r
+                               break;\r
+                       }\r
+               }\r
+       }\r
+       else\r
+#endif\r
+       {\r
+               if (!(++count & 1))\r
+               {\r
+                       LocalTime++;\r
+                       TimeCount++;\r
+                       if (SoundUserHook)\r
+                               SoundUserHook();\r
+               }\r
+               switch (SoundMode)\r
+               {\r
+               case sdm_PC:\r
+                       SDL_PCService();\r
+                       break;\r
+               case sdm_AdLib:\r
+                       SDL_ALSoundService();\r
+                       break;\r
+               }\r
+       }\r
+\r
+asm    mov     ax,[WORD PTR TimerCount]\r
+asm    add     ax,[WORD PTR TimerDivisor]\r
+asm    mov     [WORD PTR TimerCount],ax\r
+asm    jnc     myack\r
+       t0OldService();                 // If we overflow a word, time to call old int handler\r
+asm    jmp     olddone\r
+myack:;\r
+       outportb(0x20,0x20);    // Ack the interrupt\r
+olddone:;\r
+\r
+#if 0  // for debugging\r
+asm    mov     dx,STATUS_REGISTER_1\r
+asm    in      al,dx\r
+asm    mov     dx,ATR_INDEX\r
+asm    mov     al,ATR_OVERSCAN\r
+asm    out     dx,al\r
+asm    mov     al,3    // blue\r
+asm    out     dx,al\r
+asm    mov     al,0x20 // normal\r
+asm    out     dx,al\r
+#endif\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_ShutDevice() - turns off whatever device was being used for sound fx\r
+//\r
+////////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_ShutDevice(void)\r
+{\r
+       switch (SoundMode)\r
+       {\r
+       case sdm_PC:\r
+               SDL_ShutPC();\r
+               break;\r
+       case sdm_AdLib:\r
+               SDL_ShutAL();\r
+               break;\r
+       }\r
+       SoundMode = sdm_Off;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_CleanDevice() - totally shuts down all sound devices\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_CleanDevice(void)\r
+{\r
+       if ((SoundMode == sdm_AdLib) || (MusicMode == smm_AdLib))\r
+               SDL_CleanAL();\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SDL_StartDevice() - turns on whatever device is to be used for sound fx\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+SDL_StartDevice(void)\r
+{\r
+       switch (SoundMode)\r
+       {\r
+       case sdm_AdLib:\r
+               SDL_StartAL();\r
+               break;\r
+       }\r
+       SoundNumber = SoundPriority = 0;\r
+}\r
+\r
+static void\r
+SDL_SetTimerSpeed(void)\r
+{\r
+       word    rate;\r
+\r
+#if USE_MUSIC\r
+       if (MusicMode == smm_AdLib)\r
+               rate = TickBase * 8;\r
+       else\r
+#endif\r
+               rate = TickBase * 2;\r
+       SDL_SetIntsPerSec(rate);\r
+}\r
+\r
+//     Public routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SD_SetSoundMode() - Sets which sound hardware to use for sound effects\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+SD_SetSoundMode(SDMode mode)\r
+{\r
+       boolean result;\r
+       word    tableoffset;\r
+\r
+       SD_StopSound();\r
+\r
+#ifndef        _MUSE_\r
+       switch (mode)\r
+       {\r
+       case sdm_Off:\r
+               NeedsDigitized = false;\r
+               result = true;\r
+               break;\r
+       case sdm_PC:\r
+               tableoffset = STARTPCSOUNDS;\r
+               NeedsDigitized = false;\r
+               result = true;\r
+               break;\r
+       case sdm_AdLib:\r
+               if (AdLibPresent)\r
+               {\r
+                       tableoffset = STARTADLIBSOUNDS;\r
+                       NeedsDigitized = false;\r
+                       result = true;\r
+               }\r
+               break;\r
+       default:\r
+               result = false;\r
+               break;\r
+       }\r
+#endif\r
+\r
+       if (result && (mode != SoundMode))\r
+       {\r
+               SDL_ShutDevice();\r
+               SoundMode = mode;\r
+#ifndef        _MUSE_\r
+               SoundTable = (word *)(&audiosegs[tableoffset]);\r
+#endif\r
+               SDL_StartDevice();\r
+       }\r
+\r
+       SDL_SetTimerSpeed();\r
+\r
+       return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SD_SetMusicMode() - sets the device to use for background music\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+SD_SetMusicMode(SMMode mode)\r
+{\r
+#if USE_MUSIC\r
+       boolean result;\r
+\r
+       SD_FadeOutMusic();\r
+       while (SD_MusicPlaying())\r
+               ;\r
+\r
+       switch (mode)\r
+       {\r
+       case smm_Off:\r
+               NeedsMusic = false;\r
+               result = true;\r
+               break;\r
+       case smm_AdLib:\r
+               if (AdLibPresent)\r
+               {\r
+                       NeedsMusic = true;\r
+                       result = true;\r
+               }\r
+               break;\r
+       default:\r
+               result = false;\r
+               break;\r
+       }\r
+\r
+       if (result)\r
+               MusicMode = mode;\r
+\r
+       SDL_SetTimerSpeed();\r
+\r
+       return(result);\r
+#endif\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SD_Startup() - starts up the Sound Mgr\r
+//             Detects all additional sound hardware and installs my ISR\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_Startup(void)\r
+{\r
+       int     i;\r
+\r
+       if (SD_Started)\r
+               return;\r
+\r
+       ssIsTandy = false;\r
+       alNoCheck = false;\r
+#ifndef        _MUSE_\r
+       for (i = 1;i < _argc;i++)\r
+       {\r
+               switch (US_CheckParm(_argv[i],ParmStrings))\r
+               {\r
+               case 0:                                         // No AdLib detection\r
+                       alNoCheck = true;\r
+                       break;\r
+               }\r
+       }\r
+#endif\r
+\r
+       SoundUserHook = 0;\r
+\r
+       t0OldService = getvect(8);      // Get old timer 0 ISR\r
+\r
+       SDL_InitDelay();                        // SDL_InitDelay() uses t0OldService\r
+\r
+       setvect(8,SDL_t0Service);       // Set to my timer 0 ISR\r
+       LocalTime = TimeCount = alTimeCount = 0;\r
+\r
+       SD_SetSoundMode(sdm_Off);\r
+#if USE_MUSIC\r
+       SD_SetMusicMode(smm_Off);\r
+#endif\r
+\r
+       if (!alNoCheck)\r
+               AdLibPresent = SDL_DetectAdLib();\r
+\r
+       for (i = 0;i < 255;i++)\r
+               pcSoundLookup[i] = i * 60;\r
+\r
+       SD_Started = true;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SD_Default() - Sets up the default behaviour for the Sound Mgr whether\r
+//             the config file was present or not.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_Default(boolean gotit,SDMode sd,SMMode sm)\r
+{\r
+       boolean gotsd,gotsm;\r
+\r
+       gotsd = gotsm = gotit;\r
+\r
+       if (gotsd)      // Make sure requested sound hardware is available\r
+       {\r
+               switch (sd)\r
+               {\r
+               case sdm_AdLib:\r
+                       gotsd = AdLibPresent;\r
+                       break;\r
+               }\r
+       }\r
+       if (!gotsd)\r
+       {\r
+               if (AdLibPresent)\r
+                       sd = sdm_AdLib;\r
+               else\r
+                       sd = sdm_PC;\r
+       }\r
+       if (sd != SoundMode)\r
+               SD_SetSoundMode(sd);\r
+\r
+\r
+       if (gotsm)      // Make sure requested music hardware is available\r
+       {\r
+               switch (sm)\r
+               {\r
+               case sdm_AdLib:\r
+                       gotsm = AdLibPresent;\r
+                       break;\r
+               }\r
+       }\r
+       if (!gotsm)\r
+       {\r
+               if (AdLibPresent)\r
+                       sm = smm_AdLib;\r
+       }\r
+#if USE_MUSIC\r
+       if (sm != MusicMode)\r
+               SD_SetMusicMode(sm);\r
+#endif\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SD_Shutdown() - shuts down the Sound Mgr\r
+//             Removes sound ISR and turns off whatever sound hardware was active\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_Shutdown(void)\r
+{\r
+       if (!SD_Started)\r
+               return;\r
+\r
+#if USE_MUSIC\r
+       SD_MusicOff();\r
+#endif\r
+       SDL_ShutDevice();\r
+       SDL_CleanDevice();\r
+\r
+       asm     pushf\r
+       asm     cli\r
+\r
+       SDL_SetTimer0(0);\r
+\r
+       setvect(8,t0OldService);\r
+\r
+       asm     popf\r
+\r
+       SD_Started = false;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SD_SetUserHook() - sets the routine that the Sound Mgr calls every 1/70th\r
+//             of a second from its timer 0 ISR\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_SetUserHook(void (* hook)(void))\r
+{\r
+       SoundUserHook = hook;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SD_PlaySound() - plays the specified sound on the appropriate hardware\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_PlaySound(soundnames sound)\r
+{\r
+       SoundCommon     far *s;\r
+\r
+       if ((SoundMode == sdm_Off) || (sound == -1))\r
+               return;\r
+\r
+       s = MK_FP(SoundTable[sound],0);\r
+       if (!s)\r
+               Quit("SD_PlaySound() - Uncached sound");\r
+       if (!s->length)\r
+               Quit("SD_PlaySound() - Zero length sound");\r
+       if (s->priority < SoundPriority)\r
+               return;\r
+\r
+       switch (SoundMode)\r
+       {\r
+       case sdm_PC:\r
+               SDL_PCPlaySound((void far *)s);\r
+               break;\r
+       case sdm_AdLib:\r
+               SDL_ALPlaySound((void far *)s);\r
+               break;\r
+       }\r
+\r
+       SoundNumber = sound;\r
+       SoundPriority = s->priority;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SD_SoundPlaying() - returns the sound number that's playing, or 0 if\r
+//             no sound is playing\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+word\r
+SD_SoundPlaying(void)\r
+{\r
+       boolean result = false;\r
+\r
+       switch (SoundMode)\r
+       {\r
+       case sdm_PC:\r
+               result = pcSound? true : false;\r
+               break;\r
+       case sdm_AdLib:\r
+               result = alSound? true : false;\r
+               break;\r
+       }\r
+\r
+       if (result)\r
+               return(SoundNumber);\r
+       else\r
+               return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SD_StopSound() - if a sound is playing, stops it\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_StopSound(void)\r
+{\r
+       switch (SoundMode)\r
+       {\r
+       case sdm_PC:\r
+               SDL_PCStopSound();\r
+               break;\r
+       case sdm_AdLib:\r
+               SDL_ALStopSound();\r
+               break;\r
+       }\r
+\r
+       SDL_SoundFinished();\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SD_WaitSoundDone() - waits until the current sound is done playing\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_WaitSoundDone(void)\r
+{\r
+       while (SD_SoundPlaying())\r
+               ;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SD_MusicOn() - turns on the sequencer\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_MusicOn(void)\r
+{\r
+#if USE_MUSIC\r
+       sqActive = true;\r
+#endif\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SD_MusicOff() - turns off the sequencer and any playing notes\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_MusicOff(void)\r
+{\r
+#if USE_MUSIC\r
+       word    i;\r
+\r
+\r
+       switch (MusicMode)\r
+       {\r
+       case smm_AdLib:\r
+               alFXReg = 0;\r
+               alOut(alEffects,0);\r
+               for (i = 0;i < sqMaxTracks;i++)\r
+                       alOut(alFreqH + i + 1,0);\r
+               break;\r
+       }\r
+       sqActive = false;\r
+#endif\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SD_StartMusic() - starts playing the music pointed to\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_StartMusic(MusicGroup far *music)\r
+{\r
+#if USE_MUSIC\r
+       SD_MusicOff();\r
+asm    pushf\r
+asm    cli\r
+\r
+       if (MusicMode == smm_AdLib)\r
+       {\r
+               sqHackPtr = sqHack = music->values;\r
+               sqHackSeqLen = sqHackLen = music->length;\r
+               sqHackTime = 0;\r
+               alTimeCount = 0;\r
+               SD_MusicOn();\r
+       }\r
+\r
+asm    popf\r
+#endif\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SD_FadeOutMusic() - starts fading out the music. Call SD_MusicPlaying()\r
+//             to see if the fadeout is complete\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+SD_FadeOutMusic(void)\r
+{\r
+#if USE_MUSIC\r
+       switch (MusicMode)\r
+       {\r
+       case smm_AdLib:\r
+               // DEBUG - quick hack to turn the music off\r
+               SD_MusicOff();\r
+               break;\r
+       }\r
+#endif\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//     SD_MusicPlaying() - returns true if music is currently playing, false if\r
+//             not\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+SD_MusicPlaying(void)\r
+{\r
+#if USE_MUSIC\r
+       boolean result;\r
+\r
+       switch (MusicMode)\r
+       {\r
+       case smm_AdLib:\r
+               result = false;\r
+               // DEBUG - not written\r
+               break;\r
+       default:\r
+               result = false;\r
+       }\r
+\r
+       return(result);\r
+#endif\r
+}\r
diff --git a/16/cawat/ID_SD.H b/16/cawat/ID_SD.H
new file mode 100644 (file)
index 0000000..d60aea3
--- /dev/null
@@ -0,0 +1,206 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+//     ID Engine\r
+//     ID_SD.h - Sound Manager Header\r
+//     v1.0d1\r
+//     By Jason Blochowiak\r
+//\r
+\r
+#ifndef        __TYPES__\r
+#include "ID_Types.h"\r
+#endif\r
+\r
+#ifndef        __ID_SD__\r
+#define        __ID_SD__\r
+\r
+#ifdef __DEBUG__\r
+#define        __DEBUG_SoundMgr__\r
+#endif\r
+\r
+#define        TickBase        70              // 70Hz per tick - used as a base for timer 0\r
+\r
+typedef        enum    {\r
+                                       sdm_Off,\r
+                                       sdm_PC,sdm_AdLib,\r
+                               }       SDMode;\r
+typedef        enum    {\r
+                                       smm_Off,smm_AdLib\r
+                               }       SMMode;\r
+\r
+typedef        struct\r
+               {\r
+                       longword        length;\r
+                       word            priority;\r
+               } SoundCommon;\r
+\r
+//     PC Sound stuff\r
+#define        pcTimer         0x42\r
+#define        pcTAccess       0x43\r
+#define        pcSpeaker       0x61\r
+\r
+#define        pcSpkBits       3\r
+\r
+typedef        struct\r
+               {\r
+                       SoundCommon     common;\r
+                       byte            data[1];\r
+               } PCSound;\r
+\r
+//     Registers for the Sound Blaster card - needs to be offset by n0\r
+#define        sbReset         0x206\r
+#define        sbReadData      0x20a\r
+#define        sbWriteCmd      0x20c\r
+#define        sbWriteData     0x20c\r
+#define        sbWriteStat     0x20c\r
+#define        sbDataAvail     0x20e\r
+\r
+typedef        struct\r
+               {\r
+                       SoundCommon     common;\r
+                       word            hertz;\r
+                       byte            bits,\r
+                                               reference,\r
+                                               data[1];\r
+               } SampledSound;\r
+\r
+//     Registers for the AdLib card\r
+// Operator stuff\r
+#define        alChar          0x20\r
+#define        alScale         0x40\r
+#define        alAttack        0x60\r
+#define        alSus           0x80\r
+#define        alWave          0xe0\r
+// Channel stuff\r
+#define        alFreqL         0xa0\r
+#define        alFreqH         0xb0\r
+#define        alFeedCon       0xc0\r
+// Global stuff\r
+#define        alEffects       0xbd\r
+\r
+typedef        struct\r
+               {\r
+                       byte    mChar,cChar,\r
+                                       mScale,cScale,\r
+                                       mAttack,cAttack,\r
+                                       mSus,cSus,\r
+                                       mWave,cWave,\r
+                                       nConn,\r
+\r
+                                       // These are only for Muse - these bytes are really unused\r
+                                       voice,\r
+                                       mode,\r
+                                       unused[3];\r
+               } Instrument;\r
+\r
+typedef        struct\r
+               {\r
+                       SoundCommon     common;\r
+                       Instrument      inst;\r
+                       byte            block,\r
+                                               data[1];\r
+               } AdLibSound;\r
+\r
+//\r
+//     Sequencing stuff\r
+//\r
+#define        sqMaxTracks     10\r
+#define        sqMaxMoods      1       // DEBUG\r
+\r
+#define        sev_Null                0       // Does nothing\r
+#define        sev_NoteOff             1       // Turns a note off\r
+#define        sev_NoteOn              2       // Turns a note on\r
+#define        sev_NotePitch   3       // Sets the pitch of a currently playing note\r
+#define        sev_NewInst             4       // Installs a new instrument\r
+#define        sev_NewPerc             5       // Installs a new percussive instrument\r
+#define        sev_PercOn              6       // Turns a percussive note on\r
+#define        sev_PercOff             7       // Turns a percussive note off\r
+#define        sev_SeqEnd              -1      // Terminates a sequence\r
+\r
+//     Flags for MusicGroup.flags\r
+#define        sf_Melodic              0\r
+#define        sf_Percussive   1\r
+\r
+#if 1\r
+typedef        struct\r
+               {\r
+                       word    length,\r
+                                       values[1];\r
+               } MusicGroup;\r
+#else\r
+typedef        struct\r
+               {\r
+                       word    flags,\r
+                                       count,\r
+                                       offsets[1];\r
+               } MusicGroup;\r
+#endif\r
+\r
+typedef        struct\r
+               {\r
+                       /* This part needs to be set up by the user */\r
+                       word        mood,far *moods[sqMaxMoods];\r
+\r
+                       /* The rest is set up by the code */\r
+                       Instrument      inst;\r
+                       boolean         percussive;\r
+                       word            far *seq;\r
+                       longword        nextevent;\r
+               } ActiveTrack;\r
+\r
+#define        sqmode_Normal           0\r
+#define        sqmode_FadeIn           1\r
+#define        sqmode_FadeOut          2\r
+\r
+#define        sqMaxFade               64      // DEBUG\r
+\r
+\r
+// Global variables\r
+extern boolean         AdLibPresent,\r
+                                       NeedsMusic;     // For Caching Mgr\r
+extern SDMode          SoundMode;\r
+extern SMMode          MusicMode;\r
+extern longword        TimeCount;                                      // Global time in ticks\r
+extern         SDMode          oldsoundmode;\r
+\r
+// Function prototypes\r
+extern void    SD_Startup(void),\r
+                               SD_Shutdown(void),\r
+                               SD_Default(boolean gotit,SDMode sd,SMMode sm),\r
+                               SD_PlaySound(soundnames sound),\r
+                               SD_StopSound(void),\r
+                               SD_WaitSoundDone(void),\r
+                               SD_StartMusic(MusicGroup far *music),\r
+                               SD_MusicOn(void),\r
+                               SD_MusicOff(void),\r
+                               SD_FadeOutMusic(void),\r
+                               SD_SetUserHook(void (*hook)(void));\r
+extern boolean SD_MusicPlaying(void),\r
+                               SD_SetSoundMode(SDMode mode),\r
+                               SD_SetMusicMode(SMMode mode);\r
+extern word    SD_SoundPlaying(void);\r
+\r
+#ifdef _MUSE_  // MUSE Goes directly to the lower level routines\r
+extern void    SDL_PCPlaySound(PCSound far *sound),\r
+                               SDL_PCStopSound(void),\r
+                               SDL_ALPlaySound(AdLibSound far *sound),\r
+                               SDL_ALStopSound(void);\r
+#endif\r
+\r
+#endif\r
diff --git a/16/cawat/ID_STRS.H b/16/cawat/ID_STRS.H
new file mode 100644 (file)
index 0000000..c34a245
--- /dev/null
@@ -0,0 +1,128 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#define S_LOADING      "Loading"\r
+#define S_EMPTYSPOT    "Empty"\r
+#define S_SVGACOMP     "SVGA Compatibility Mode Enabled."\r
+#define S_READYPRESS   " Ready - Press a Key     "\r
+#define S_NOSFX                "NO SOUND EFFECTS"\r
+#define S_PCSPKR       "PC SPEAKER"\r
+#define S_ALSB         "ADLIB/SOUNDBLASTER"\r
+#define S_QUIET                "QUIET ADLIB/SOUNDBLASTER"\r
+#define S_NOMUSIC      "NO MUSIC"\r
+#define S_BEGINE       "BEGIN EASY GAME"\r
+#define S_BEGINN       "BEGIN NORMAL GAME"\r
+#define S_BEGINH       "BEGIN HARD GAME"\r
+#define S_UPLEFT       "UP & LEFT"\r
+#define S_UP           "UP"\r
+#define S_UPRIGHT      "UP & RIGHT"\r
+#define S_RIGHT                "RIGHT"\r
+#define S_DNRIGHT      "DOWN & RIGHT"\r
+#define S_DN           "DOWN"\r
+#define S_DNLEFT       "DOWN & LEFT"\r
+#define S_LEFT         "LEFT"\r
+\r
+#define S_JUMP         "JUMP"\r
+#define S_POGO         "POGO"\r
+#define S_FIRE         "FIRE"\r
+#define S_MOVEMENT     "MOVEMENT"\r
+#define S_BUTTONS      "BUTTONS"\r
+\r
+#define S_SOUND                "SOUND"\r
+#define S_MUSIC                "MUSIC"\r
+#define S_OPTIONS      "OPTIONS"\r
+#define        S_USEKB         "USE KEYBOARD"\r
+#define S_USEJOY1      "USE JOYSTICK #1"\r
+#define S_USEJOY2      "USE JOYSTICK #2"\r
+#define S_NEWGAME      "NEW GAME"\r
+#define S_LOADGAME     "LOAD GAME"\r
+#define S_SAVEGAME     "SAVE GAME"\r
+#define S_CONFIG       "CONFIGURE"\r
+#define S_ENDGAME      "END GAME"\r
+#define S_PADDLEWAR    "PADDLE WAR"\r
+#define S_QUIT         "QUIT"\r
+\r
+#define S_ESCBACK      "ESC TO BACK OUT"\r
+#define S_ESCBACK1     "ESC to back out"\r
+#define S_ESCQUIT      "ESC to quit"\r
+#define S_F1HELP       "F1 for help"\r
+#define S_REALLYEND    "REALLY END CURRENT GAME?"\r
+#define S_PRESSY       "PRESS Y TO END IT"\r
+#define S_REALLYQUIT   "REALLY QUIT?"\r
+#define S_PRESSYQ      "PRESS Y TO QUIT"\r
+#define S_INAGAME      "YOU'RE IN A GAME"\r
+#define S_PRESSY2L     "PRESS Y TO LOAD GAME"\r
+#define S_PRESSY4N     "PRESS Y FOR NEW GAME"\r
+\r
+#define S_USLERROR     "Error: "\r
+#define S_USLUNKNOWN   "Unknown"\r
+#define S_USLDISKFULL  "Disk is Full"\r
+#define S_USLFILEINC   "File is Incomplete"\r
+#define S_PRESSKEY     "PRESS ANY KEY"\r
+#define S_PRESSKEY1    "Press any key"\r
+\r
+#define S_SBOXON       "SCORE BOX (ON)"\r
+#define S_SBOXOFF      "SCORE BOX (OFF)"\r
+#define S_SVGAON       "SVGA COMPATIBILITY (ON)"\r
+#define S_SVGAOFF      "SVGA COMPATIBILITY (OFF)"\r
+#define S_2BON         "TWO-BUTTON FIRING (ON)"\r
+#define S_2BOFF                "TWO-BUTTON FIRING (OFF)"\r
+\r
+#define S_SBOXNOWON    "Score box now on"\r
+#define S_SBOXNOWOFF   "Score box now off"\r
+#define S_SVGANOWON    "SVGA compatibility now on"\r
+#define S_SVGANOWOFF   "SVGA compatibility now off"\r
+#define S_2BNOWON      "Two-button firing now on"\r
+#define S_2BNOWOFF     "Two-button firing now off"\r
+\r
+#define S_KEYBOARD     "KEYBOARD"\r
+#define S_JOY1         "JOYSTICK #1"\r
+#define S_JOY2         "JOYSTICK #2"\r
+#define S_MOUSE                "MOUSE"\r
+#define S_CONTROL      "CONTROL: "\r
+#define S_KEYUSED      "Key already used"\r
+#define S_PB1          "and press button #1"\r
+#define S_PB2          "and press button #2"\r
+#define S_MJUL         "Move Joystick to upper left"\r
+#define S_MJLR         "Move Joystick to lower right"\r
+\r
+#define S_USINGJ1      "USING "S_JOY1\r
+#define S_USINGJ2      "USING "S_JOY2\r
+#define S_TYPENAME     "Type name"\r
+#define S_ENTERACC     "Enter accepts"\r
+#define S_UNTITLED     "Untitled"\r
+#define S_SAVING       "Saving"\r
+#define S_YOULOST      "You lost!"\r
+#define S_YOUWON       "You won!"\r
+#define S_ARRMOVE      "Arrows move"\r
+#define S_ENTERSEL     "Enter selects"\r
+\r
+#define S_RETGAME      "RETURN TO GAME"\r
+#define S_RETDEMO      "RETURN TO DEMO"\r
+#define S_CTRLPANEL    "Control Panel"\r
+#define S_QUITTING     "Quitting..."\r
+\r
+#define S_WHATNAME     "What is the name of this creature?"\r
+#define S_SORRY                "Sorry, that's not quite right."\r
+#define S_CHECKMAN     "Please check your manual and try again."\r
+\r
+#define S_BADCARD      "Improper video card!  If you really have an EGA/VGA card that I am not\n"\\r
+                       "detecting, use the -HIDDENCARD command line parameter!"\r
+#define S_BADCARD1     "Improper video card!  If you really have a CGA card that I am not\n"\\r
+                       "detecting, use the -HIDDENCARD command line parameter!"\r
+\r
diff --git a/16/cawat/ID_US.C b/16/cawat/ID_US.C
new file mode 100644 (file)
index 0000000..082b231
--- /dev/null
@@ -0,0 +1,3691 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+//      ID Engine\r
+//      ID_US.c - User Manager\r
+//      v1.0d1\r
+//      By Jason Blochowiak\r
+//\r
+\r
+//\r
+//      This module handles dealing with user input & feedback\r
+//\r
+//      Depends on: Input Mgr, View Mgr, some variables from the Sound, Caching,\r
+//              and Refresh Mgrs, Memory Mgr for background save/restore\r
+//\r
+//      Globals:\r
+//              ingame - Flag set by game indicating if a game is in progress\r
+//      abortgame - Flag set if the current game should be aborted (if a load\r
+//                      game fails)\r
+//              loadedgame - Flag set if a game was loaded\r
+//              abortprogram - Normally nil, this points to a terminal error message\r
+//                      if the program needs to abort\r
+//              restartgame - Normally set to gd_Continue, this is set to one of the\r
+//                      difficulty levels if a new game should be started\r
+//              PrintX, PrintY - Where the User Mgr will print (global coords)\r
+//              WindowX,WindowY,WindowW,WindowH - The dimensions of the current\r
+//                      window\r
+//\r
+\r
+// DEBUG - handle LPT3 for Sound Source\r
+\r
+#include "ID_HEADS.H"\r
+\r
+#define CTL_M_ADLIBUPPIC        CTL_S_ADLIBUPPIC\r
+#define CTL_M_ADLIBDNPIC        CTL_S_ADLIBDNPIC\r
+\r
+#pragma hdrstop\r
+\r
+#pragma warn    -pia\r
+\r
+#define MaxX    320\r
+#define MaxY    200\r
+\r
+#define MaxHelpLines    500\r
+\r
+#define MaxHighName     57\r
+#define MaxScores       10\r
+typedef struct\r
+               {\r
+                       char    name[MaxHighName + 1];\r
+                       long    score;\r
+                       word    completed;\r
+               } HighScore;\r
+\r
+#define MaxGameName             32\r
+#define MaxSaveGames    7\r
+typedef struct\r
+               {\r
+                       char    signature[4];\r
+                       boolean present;\r
+                       char    name[MaxGameName + 1];\r
+               } SaveGame;\r
+\r
+//      Hack import for TED launch support\r
+extern  boolean         tedlevel;\r
+extern  word            tedlevelnum;\r
+extern  void            TEDDeath(void);\r
+static  char            *ParmStrings[] = {"TEDLEVEL","NOWAIT",""};\r
+\r
+\r
+//      Global variables\r
+               boolean         ingame,abortgame,loadedgame;\r
+               char            *abortprogram;\r
+               GameDiff        restartgame = gd_Continue;\r
+               word            PrintX,PrintY;\r
+               word            WindowX,WindowY,WindowW,WindowH;\r
+\r
+//      Internal variables\r
+static  boolean         US_Started;\r
+static  boolean         GameIsDirty,\r
+                                       HighScoresDirty,\r
+                                       QuitToDos,\r
+                                       ResumeGame,\r
+                                       NoWait;\r
+\r
+static  memptr          LineOffsets;\r
+\r
+static  boolean         Button0,Button1,\r
+                                       CursorBad;\r
+static  int                     CursorX,CursorY;\r
+\r
+static  void            (*USL_MeasureString)(char far *,word *,word *) = VW_MeasurePropString,\r
+                                       (*USL_DrawString)(char far *) = VWB_DrawPropString;\r
+\r
+static  boolean         (*USL_SaveGame)(int),(*USL_LoadGame)(int);\r
+static  void            (*USL_ResetGame)(void);\r
+static  SaveGame        Games[MaxSaveGames];\r
+static  HighScore       Scores[MaxScores] =\r
+                                       {\r
+                                               {"Sir Lancelot",500},\r
+                                               {"",0},\r
+                                               {"",0},\r
+                                               {"",0},\r
+                                               {"",0},\r
+                                               {"",0},\r
+                                               {"",0},\r
+                                               {"",0},\r
+                                               {"",0},\r
+                                               {"",0}\r
+                                       };\r
+\r
+//      Internal routines\r
+\r
+//      Public routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_HardError() - Handles the Abort/Retry/Fail sort of errors passed\r
+//                      from DOS.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+#pragma warn    -par\r
+#pragma warn    -rch\r
+int\r
+USL_HardError(word errval,int ax,int bp,int si)\r
+{\r
+#define IGNORE  0\r
+#define RETRY   1\r
+#define ABORT   2\r
+extern  void    ShutdownId(void);\r
+\r
+static  char            buf[32];\r
+static  WindowRec       wr;\r
+static  boolean         oldleavedriveon;\r
+               int                     di;\r
+               char            c,*s,*t;\r
+\r
+\r
+       di = _DI;\r
+\r
+       oldleavedriveon = LeaveDriveOn;\r
+       LeaveDriveOn = false;\r
+\r
+       if (ax < 0)\r
+               s = "Device Error";\r
+       else\r
+       {\r
+               if ((di & 0x00ff) == 0)\r
+                       s = "Drive ~ is Write Protected";\r
+               else\r
+                       s = "Error on Drive ~";\r
+               for (t = buf;*s;s++,t++)        // Can't use sprintf()\r
+                       if ((*t = *s) == '~')\r
+                               *t = (ax & 0x00ff) + 'A';\r
+               *t = '\0';\r
+               s = buf;\r
+       }\r
+\r
+       c = peekb(0x40,0x49);   // Get the current screen mode\r
+       if ((c < 4) || (c == 7))\r
+               goto oh_kill_me;\r
+\r
+       // DEBUG - handle screen cleanup\r
+\r
+       US_SaveWindow(&wr);\r
+       US_CenterWindow(30,3);\r
+       US_CPrint(s);\r
+       US_CPrint("(R)etry or (A)bort?");\r
+       VW_UpdateScreen();\r
+       IN_ClearKeysDown();\r
+\r
+asm     sti     // Let the keyboard interrupts come through\r
+\r
+       while (true)\r
+       {\r
+               switch (IN_WaitForASCII())\r
+               {\r
+               case key_Escape:\r
+               case 'a':\r
+               case 'A':\r
+                       goto oh_kill_me;\r
+                       break;\r
+               case key_Return:\r
+               case key_Space:\r
+               case 'r':\r
+               case 'R':\r
+                       US_ClearWindow();\r
+                       VW_UpdateScreen();\r
+                       US_RestoreWindow(&wr);\r
+                       LeaveDriveOn = oldleavedriveon;\r
+                       return(RETRY);\r
+                       break;\r
+               }\r
+       }\r
+\r
+oh_kill_me:\r
+       abortprogram = s;\r
+       ShutdownId();\r
+       fprintf(stderr,"Terminal Error: %s\n",s);\r
+       if (tedlevel)\r
+               fprintf(stderr,"You launched from TED. I suggest that you reboot...\n");\r
+\r
+       return(ABORT);\r
+#undef  IGNORE\r
+#undef  RETRY\r
+#undef  ABORT\r
+}\r
+#pragma warn    +par\r
+#pragma warn    +rch\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_GiveSaveName() - Returns a pointer to a static buffer that contains\r
+//              the filename to use for the specified save game\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static char *\r
+USL_GiveSaveName(word game)\r
+{\r
+static  char    filename[32];\r
+               char    *s,*t;\r
+\r
+       for (s = "SAVEGM",t = filename;*s;)\r
+               *t++ = *s++;\r
+       *t++ = game + '0';\r
+       for (s = "."EXTENSION;*s;)\r
+               *t++ = *s++;\r
+       *t = '\0';\r
+\r
+       return(filename);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_SetLoadSaveHooks() - Sets the routines that the User Mgr calls after\r
+//              reading or writing the save game headers\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_SetLoadSaveHooks(boolean (*load)(int),boolean (*save)(int),void (*reset)(void))\r
+{\r
+       USL_LoadGame = load;\r
+       USL_SaveGame = save;\r
+       USL_ResetGame = reset;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_ReadConfig() - Reads the configuration file, if present, and sets\r
+//              things up accordingly. If it's not present, uses defaults. This file\r
+//              includes the high scores.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_ReadConfig(void)\r
+{\r
+       boolean         gotit;\r
+       int                     file;\r
+       SDMode          sd;\r
+       SMMode          sm;\r
+       ControlType     ctl;\r
+\r
+       if ((file = open("CONFIG."EXTENSION,O_BINARY | O_RDONLY)) != -1)\r
+       {\r
+               read(file,Scores,sizeof(HighScore) * MaxScores);\r
+               read(file,&sd,sizeof(sd));\r
+               read(file,&sm,sizeof(sm));\r
+               read(file,&ctl,sizeof(ctl));\r
+               read(file,&(KbdDefs[0]),sizeof(KbdDefs[0]));\r
+               close(file);\r
+\r
+               HighScoresDirty = false;\r
+               gotit = true;\r
+       }\r
+       else\r
+       {\r
+               sd = sdm_Off;\r
+               sm = smm_Off;\r
+               ctl = ctrl_Keyboard;\r
+\r
+               gotit = false;\r
+               HighScoresDirty = true;\r
+       }\r
+\r
+       SD_Default(gotit,sd,sm);\r
+       IN_Default(gotit,ctl);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_WriteConfig() - Writes out the current configuration, including the\r
+//              high scores.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_WriteConfig(void)\r
+{\r
+       int     file;\r
+\r
+       file = open("CONFIG."EXTENSION,O_CREAT | O_BINARY | O_WRONLY,\r
+                               S_IREAD | S_IWRITE | S_IFREG);\r
+       if (file != -1)\r
+       {\r
+               write(file,Scores,sizeof(HighScore) * MaxScores);\r
+               write(file,&SoundMode,sizeof(SoundMode));\r
+               write(file,&MusicMode,sizeof(MusicMode));\r
+               write(file,&(Controls[0]),sizeof(Controls[0]));\r
+               write(file,&(KbdDefs[0]),sizeof(KbdDefs[0]));\r
+               close(file);\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_CheckSavedGames() - Checks to see which saved games are present\r
+//              & valid\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_CheckSavedGames(void)\r
+{\r
+       boolean         ok;\r
+       char            *filename;\r
+       word            i;\r
+       int                     file;\r
+       SaveGame        *game;\r
+\r
+       USL_SaveGame = 0;\r
+       USL_LoadGame = 0;\r
+\r
+       for (i = 0,game = Games;i < MaxSaveGames;i++,game++)\r
+       {\r
+               filename = USL_GiveSaveName(i);\r
+               ok = false;\r
+               if ((file = open(filename,O_BINARY | O_RDONLY)) != -1)\r
+               {\r
+                       if\r
+                       (\r
+                               (read(file,game,sizeof(*game)) == sizeof(*game))\r
+                       &&      (!strcmp(game->signature,EXTENSION))\r
+                       )\r
+                               ok = true;\r
+\r
+                       close(file);\r
+               }\r
+\r
+               if (ok)\r
+                       game->present = true;\r
+               else\r
+               {\r
+                       strcpy(game->signature,EXTENSION);\r
+                       game->present = false;\r
+                       strcpy(game->name,"Empty");\r
+               }\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_Startup() - Starts the User Mgr\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_Startup(void)\r
+{\r
+       if (US_Started)\r
+               return;\r
+\r
+       harderr(USL_HardError); // Install the fatal error handler\r
+\r
+       US_InitRndT(true);              // Initialize the random number generator\r
+\r
+       USL_ReadConfig();               // Read config file\r
+\r
+       US_Started = true;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_Setup() - Does the disk access part of the User Mgr's startup\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_Setup(void)\r
+{\r
+       USL_CheckSavedGames();  // Check which saved games are present\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_Shutdown() - Shuts down the User Mgr\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_Shutdown(void)\r
+{\r
+       if (!US_Started)\r
+               return;\r
+\r
+       if (!abortprogram)\r
+               USL_WriteConfig();\r
+\r
+       US_Started = false;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_CheckParm() - checks to see if a string matches one of a set of\r
+//              strings. The check is case insensitive. The routine returns the\r
+//              index of the string that matched, or -1 if no matches were found\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+int\r
+US_CheckParm(char *parm,char **strings)\r
+{\r
+       char    cp,cs,\r
+                       *p,*s;\r
+       int             i;\r
+\r
+       while (!isalpha(*parm)) // Skip non-alphas\r
+               parm++;\r
+\r
+       for (i = 0;*strings && **strings;i++)\r
+       {\r
+               for (s = *strings++,p = parm,cs = cp = 0;cs == cp;)\r
+               {\r
+                       cs = *s++;\r
+                       if (!cs)\r
+                               return(i);\r
+                       cp = *p++;\r
+\r
+                       if (isupper(cs))\r
+                               cs = tolower(cs);\r
+                       if (isupper(cp))\r
+                               cp = tolower(cp);\r
+               }\r
+       }\r
+       return(-1);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_ScreenDraw() - Draws a chunk of the text screen (called only by\r
+//              US_TextScreen())\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_ScreenDraw(word x,word y,char *s,byte attr)\r
+{\r
+       byte    far *screen;\r
+\r
+       screen = MK_FP(0xb800,(x * 2) + (y * 80 * 2));\r
+       while (*s)\r
+       {\r
+               *screen++ = *s++;\r
+               *screen++ = attr;\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_ClearTextScreen() - Makes sure the screen is in text mode, clears it,\r
+//              and moves the cursor to the leftmost column of the bottom line\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_ClearTextScreen(void)\r
+{\r
+       // Set to 80x25 color text mode\r
+       _AL = 3;                                // Mode 3\r
+       _AH = 0x00;\r
+       geninterrupt(0x10);\r
+\r
+       // Use BIOS to move the cursor to the bottom of the screen\r
+       _AH = 0x0f;\r
+       geninterrupt(0x10);             // Get current video mode into _BH\r
+       _DL = 0;                                // Lefthand side of the screen\r
+       _DH = 24;                               // Bottom row\r
+       _AH = 0x02;\r
+       geninterrupt(0x10);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_TextScreen() - Puts up the startup text screen\r
+//      Note: These are the only User Manager functions that can be safely called\r
+//              before the User Mgr has been started up\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_TextScreen(void)\r
+{\r
+               word    i,n,\r
+                               sx,sy;\r
+extern  char    far introscn;\r
+\r
+       USL_ClearTextScreen();\r
+\r
+       _fmemcpy(MK_FP(0xb800,0),7 + &introscn,80 * 25 * 2);\r
+\r
+       // Check for TED launching here\r
+       for (i = 1;i < _argc;i++)\r
+       {\r
+               n = US_CheckParm(_argv[i],ParmStrings);\r
+               if (n == 0)\r
+               {\r
+                       tedlevelnum = atoi(_argv[i + 1]);\r
+                       if (tedlevelnum >= 0)\r
+                       {\r
+                               tedlevel = true;\r
+                               return;\r
+                       }\r
+                       else\r
+                               break;\r
+               }\r
+               else if (n == 1)\r
+               {\r
+                       NoWait = true;\r
+                       return;\r
+               }\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_Show() - Changes the appearance of one of the fields on the text\r
+//              screen. Possibly adds a checkmark in front of it and highlights it\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_Show(word x,word y,word w,boolean show,boolean hilight)\r
+{\r
+       byte    far *screen;\r
+\r
+       screen = MK_FP(0xb800,((x - 1) * 2) + (y * 80 * 2));\r
+       *screen++ = show? 251 : ' ';    // Checkmark char or space\r
+       *screen = 0x48;\r
+       if (show && hilight)\r
+       {\r
+               for (w++;w--;screen += 2)\r
+                       *screen = 0x4f;\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_ShowMem() - Right justifies a longword in one of the memory fields on\r
+//              the text screen\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_ShowMem(word x,word y,long mem)\r
+{\r
+       char    buf[16];\r
+       word    i;\r
+\r
+       for (i = strlen(ltoa(mem,buf,10));i < 5;i++)\r
+               USL_ScreenDraw(x++,y," ",0x48);\r
+       USL_ScreenDraw(x,y,buf,0x48);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_UpdateTextScreen() - Called after the ID libraries are started up.\r
+//              Displays what hardware is present.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_UpdateTextScreen(void)\r
+{\r
+       boolean         b;\r
+       byte            far *screen;\r
+       word            i;\r
+       longword        totalmem;\r
+\r
+       // Show video card info\r
+       b = (grmode == CGAGR);\r
+       USL_Show(21,7,4,(videocard >= CGAcard) && (videocard <= VGAcard),b);\r
+       b = (grmode == EGAGR);\r
+       USL_Show(21,8,4,(videocard >= EGAcard) && (videocard <= VGAcard),b);\r
+       b = (grmode == VGAGR);\r
+       USL_Show(21,9,4,videocard == VGAcard,b);\r
+       if (compatability)\r
+               USL_ScreenDraw(5,10,"SVGA Compatibility Mode Enabled.",0x4f);\r
+\r
+       // Show input device info\r
+       USL_Show(60,7,8,true,true);\r
+       USL_Show(60,8,11,JoysPresent[0],true);\r
+       USL_Show(60,9,11,JoysPresent[1],true);\r
+       USL_Show(60,10,5,MousePresent,true);\r
+\r
+       // Show sound hardware info\r
+       USL_Show(21,14,11,true,SoundMode == sdm_PC);\r
+       b = (SoundMode == sdm_AdLib) || (MusicMode == smm_AdLib);\r
+       USL_Show(21,15,5,AdLibPresent && !SoundBlasterPresent,\r
+                               b && !SoundBlasterPresent);\r
+       USL_Show(21,16,13,SoundBlasterPresent,\r
+                       SoundBlasterPresent && (b || (SoundMode == sdm_SoundBlaster)));\r
+       USL_Show(21,17,13,SoundSourcePresent,SoundMode == sdm_SoundSource);\r
+\r
+       // Show memory available/used\r
+       USL_ShowMem(63,15,mminfo.mainmem / 1024);\r
+       USL_Show(53,15,23,true,true);\r
+       USL_ShowMem(63,16,mminfo.EMSmem / 1024);\r
+       USL_Show(53,16,23,mminfo.EMSmem? true : false,true);\r
+       USL_ShowMem(63,17,mminfo.XMSmem / 1024);\r
+       USL_Show(53,17,23,mminfo.XMSmem? true : false,true);\r
+       totalmem = mminfo.mainmem + mminfo.EMSmem + mminfo.XMSmem;\r
+       USL_ShowMem(63,18,totalmem / 1024);\r
+       screen = MK_FP(0xb800,1 + (((63 - 1) * 2) + (18 * 80 * 2)));\r
+       for (i = 0;i < 13;i++,screen += 2)\r
+               *screen = 0x4f;\r
+\r
+       // Change Initializing... to Loading...\r
+       USL_ScreenDraw(27,22,"  Loading...   ",0x9c);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_FinishTextScreen() - After the main program has finished its initial\r
+//              loading, this routine waits for a keypress and then clears the screen\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_FinishTextScreen(void)\r
+{\r
+       // Change Loading... to Press a Key\r
+       USL_ScreenDraw(29,22," Ready - Press a Key     ",0x9a);\r
+\r
+       if (!(tedlevel || NoWait))\r
+       {\r
+               IN_ClearKeysDown();\r
+               IN_Ack();\r
+       }\r
+       IN_ClearKeysDown();\r
+\r
+       USL_ClearTextScreen();\r
+}\r
+\r
+//      Window/Printing routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_SetPrintRoutines() - Sets the routines used to measure and print\r
+//              from within the User Mgr. Primarily provided to allow switching\r
+//              between masked and non-masked fonts\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_SetPrintRoutines(void (*measure)(char far *,word *,word *),void (*print)(char far *))\r
+{\r
+       USL_MeasureString = measure;\r
+       USL_DrawString = print;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_Print() - Prints a string in the current window. Newlines are\r
+//              supported.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_Print(char *s)\r
+{\r
+       char    c,*se;\r
+       word    w,h;\r
+\r
+       while (*s)\r
+       {\r
+               se = s;\r
+               while ((c = *se) && (c != '\n'))\r
+                       se++;\r
+               *se = '\0';\r
+\r
+               USL_MeasureString(s,&w,&h);\r
+               px = PrintX;\r
+               py = PrintY;\r
+               USL_DrawString(s);\r
+\r
+               s = se;\r
+               if (c)\r
+               {\r
+                       *se = c;\r
+                       s++;\r
+\r
+                       PrintX = WindowX;\r
+                       PrintY += h;\r
+               }\r
+               else\r
+                       PrintX += w;\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_PrintUnsigned() - Prints an unsigned long\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_PrintUnsigned(longword n)\r
+{\r
+       char    buffer[32];\r
+\r
+       US_Print(ultoa(n,buffer,10));\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_PrintSigned() - Prints a signed long\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_PrintSigned(long n)\r
+{\r
+       char    buffer[32];\r
+\r
+       US_Print(ltoa(n,buffer,10));\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_PrintInCenter() - Prints a string in the center of the given rect\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_PrintInCenter(char *s,Rect r)\r
+{\r
+       word    w,h,\r
+                       rw,rh;\r
+\r
+       USL_MeasureString(s,&w,&h);\r
+       rw = r.lr.x - r.ul.x;\r
+       rh = r.lr.y - r.ul.y;\r
+\r
+       px = r.ul.x + ((rw - w) / 2);\r
+       py = r.ul.y + ((rh - h) / 2);\r
+       USL_DrawString(s);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_PrintCentered() - Prints a string centered in the current window.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_PrintCentered(char *s)\r
+{\r
+       Rect    r;\r
+\r
+       r.ul.x = WindowX;\r
+       r.ul.y = WindowY;\r
+       r.lr.x = r.ul.x + WindowW;\r
+       r.lr.y = r.ul.y + WindowH;\r
+\r
+       USL_PrintInCenter(s,r);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_CPrintLine() - Prints a string centered on the current line and\r
+//              advances to the next line. Newlines are not supported.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_CPrintLine(char *s)\r
+{\r
+       word    w,h;\r
+\r
+       USL_MeasureString(s,&w,&h);\r
+\r
+       if (w > WindowW)\r
+               Quit("US_CPrintLine() - String exceeds width");\r
+       px = WindowX + ((WindowW - w) / 2);\r
+       py = PrintY;\r
+       USL_DrawString(s);\r
+       PrintY += h;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_CPrint() - Prints a string in the current window. Newlines are\r
+//              supported.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_CPrint(char *s)\r
+{\r
+       char    c,*se;\r
+       word    w,h;\r
+\r
+       while (*s)\r
+       {\r
+               se = s;\r
+               while ((c = *se) && (c != '\n'))\r
+                       se++;\r
+               *se = '\0';\r
+\r
+               US_CPrintLine(s);\r
+\r
+               s = se;\r
+               if (c)\r
+               {\r
+                       *se = c;\r
+                       s++;\r
+               }\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_ClearWindow() - Clears the current window to white and homes the\r
+//              cursor\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_ClearWindow(void)\r
+{\r
+       VWB_Bar(WindowX,WindowY,WindowW,WindowH,WHITE);\r
+       PrintX = WindowX;\r
+       PrintY = WindowY;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_DrawWindow() - Draws a frame and sets the current window parms\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_DrawWindow(word x,word y,word w,word h)\r
+{\r
+       word    i,\r
+                       sx,sy,sw,sh;\r
+\r
+       WindowX = x * 8;\r
+       WindowY = y * 8;\r
+       WindowW = w * 8;\r
+       WindowH = h * 8;\r
+\r
+       PrintX = WindowX;\r
+       PrintY = WindowY;\r
+\r
+       sx = (x - 1) * 8;\r
+       sy = (y - 1) * 8;\r
+       sw = (w + 1) * 8;\r
+       sh = (h + 1) * 8;\r
+\r
+       US_ClearWindow();\r
+\r
+       VWB_DrawTile8M(sx,sy,0),VWB_DrawTile8M(sx,sy + sh,6);\r
+       for (i = sx + 8;i <= sx + sw - 8;i += 8)\r
+               VWB_DrawTile8M(i,sy,1),VWB_DrawTile8M(i,sy + sh,7);\r
+       VWB_DrawTile8M(i,sy,2),VWB_DrawTile8M(i,sy + sh,8);\r
+\r
+       for (i = sy + 8;i <= sy + sh - 8;i += 8)\r
+               VWB_DrawTile8M(sx,i,3),VWB_DrawTile8M(sx + sw,i,5);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_CenterWindow() - Generates a window of a given width & height in the\r
+//              middle of the screen\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_CenterWindow(word w,word h)\r
+{\r
+       US_DrawWindow(((MaxX / 8) - w) / 2,((MaxY / 8) - h) / 2,w,h);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_CenterSaveWindow() - Generates a window of a given width & height in\r
+//              the middle of the screen, saving the background\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_CenterSaveWindow(word w,word h,memptr *save)\r
+{\r
+       word    x,y,\r
+                       screen;\r
+\r
+       x = ((MaxX / 8) - w) / 2;\r
+       y = ((MaxY / 8) - h) / 2;\r
+       MM_GetPtr(save,(w * h) * CHARWIDTH);\r
+       screen = bufferofs + panadjust + ylookup[y] + (x * CHARWIDTH);\r
+       VW_ScreenToMem(screen,*save,w * CHARWIDTH,h);\r
+       US_DrawWindow(((MaxX / 8) - w) / 2,((MaxY / 8) - h) / 2,w,h);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_RestoreSaveWindow() - Restores the background of the size of the\r
+//              current window from the memory specified by save\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_RestoreSaveWindow(memptr *save)\r
+{\r
+       word    screen;\r
+\r
+       screen = bufferofs + panadjust + ylookup[WindowY] + (WindowX * CHARWIDTH);\r
+       VW_MemToScreen(*save,screen,WindowW * CHARWIDTH,WindowH);\r
+       MM_FreePtr(save);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_SaveWindow() - Saves the current window parms into a record for\r
+//              later restoration\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_SaveWindow(WindowRec *win)\r
+{\r
+       win->x = WindowX;\r
+       win->y = WindowY;\r
+       win->w = WindowW;\r
+       win->h = WindowH;\r
+\r
+       win->px = PrintX;\r
+       win->py = PrintY;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_RestoreWindow() - Sets the current window parms to those held in the\r
+//              record\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_RestoreWindow(WindowRec *win)\r
+{\r
+       WindowX = win->x;\r
+       WindowY = win->y;\r
+       WindowW = win->w;\r
+       WindowH = win->h;\r
+\r
+       PrintX = win->px;\r
+       PrintY = win->py;\r
+}\r
+\r
+//      Cursor routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_StartCursor() - Sets up the cursor for User Mgr use\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_StartCursor(void)\r
+{\r
+       CursorInfo      info;\r
+\r
+       VW_SetCursor(CURSORARROWSPR);\r
+       CursorX = MaxX / 2;\r
+       CursorY = MaxY / 2;\r
+       VW_MoveCursor(CursorX,CursorY);\r
+       VW_ShowCursor();\r
+\r
+       IN_ReadCursor(&info);   // Dispose of any accumulated movement\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_ShutCursor() - Cleans up after US_StartCursor()\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_ShutCursor(void)\r
+{\r
+       VW_HideCursor();\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_UpdateCursor() - Gets the new cursor position & button states from\r
+//              the Input Mgr and tells the View Mgr where the cursor is\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+US_UpdateCursor(void)\r
+{\r
+       CursorInfo      info;\r
+\r
+       IN_ReadCursor(&info);\r
+       if (info.x || info.y || CursorBad)\r
+       {\r
+               CursorX += info.x;\r
+               if (CursorX >= MaxX)\r
+                       CursorX = MaxX - 1;\r
+               else if (CursorX < 0)\r
+                       CursorX = 0;\r
+\r
+               CursorY += info.y;\r
+               if (CursorY >= MaxY)\r
+                       CursorY = MaxY - 1;\r
+               else if (CursorY < 0)\r
+                       CursorY = 0;\r
+\r
+               VW_MoveCursor(CursorX,CursorY);\r
+               CursorBad = false;\r
+       }\r
+       Button0 = info.button0;\r
+       Button1 = info.button1;\r
+       return(Button0 || Button1);\r
+}\r
+\r
+//      Input routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_XORICursor() - XORs the I-bar text cursor. Used by US_LineInput()\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_XORICursor(int x,int y,char *s,word cursor)\r
+{\r
+       char    buf[MaxString];\r
+       word    w,h;\r
+\r
+       strcpy(buf,s);\r
+       buf[cursor] = '\0';\r
+       USL_MeasureString(buf,&w,&h);\r
+\r
+       px = x + w - 1;\r
+       py = y;\r
+       USL_DrawString("\x80");\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_LineInput() - Gets a line of user input at (x,y), the string defaults\r
+//              to whatever is pointed at by def. Input is restricted to maxchars\r
+//              chars or maxwidth pixels wide. If the user hits escape (and escok is\r
+//              true), nothing is copied into buf, and false is returned. If the\r
+//              user hits return, the current string is copied into buf, and true is\r
+//              returned\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+US_LineInput(int x,int y,char *buf,char *def,boolean escok,\r
+                               int maxchars,int maxwidth)\r
+{\r
+       boolean         redraw,\r
+                               cursorvis,cursormoved,\r
+                               done,result;\r
+       ScanCode        sc;\r
+       char            c,\r
+                               s[MaxString],olds[MaxString];\r
+       word            i,\r
+                               cursor,\r
+                               w,h,\r
+                               len;\r
+       longword        lasttime;\r
+\r
+       VW_HideCursor();\r
+\r
+       if (def)\r
+               strcpy(s,def);\r
+       else\r
+               *s = '\0';\r
+       *olds = '\0';\r
+       cursor = strlen(s);\r
+       cursormoved = redraw = true;\r
+\r
+       cursorvis = done = false;\r
+       lasttime = TimeCount;\r
+       LastASCII = key_None;\r
+       LastScan = sc_None;\r
+\r
+       while (!done)\r
+       {\r
+               if (cursorvis)\r
+                       USL_XORICursor(x,y,s,cursor);\r
+\r
+       asm     pushf\r
+       asm     cli\r
+\r
+               sc = LastScan;\r
+               LastScan = sc_None;\r
+               c = LastASCII;\r
+               LastASCII = key_None;\r
+\r
+       asm     popf\r
+\r
+               switch (sc)\r
+               {\r
+               case sc_LeftArrow:\r
+                       if (cursor)\r
+                               cursor--;\r
+                       c = key_None;\r
+                       cursormoved = true;\r
+                       break;\r
+               case sc_RightArrow:\r
+                       if (s[cursor])\r
+                               cursor++;\r
+                       c = key_None;\r
+                       cursormoved = true;\r
+                       break;\r
+               case sc_Home:\r
+                       cursor = 0;\r
+                       c = key_None;\r
+                       cursormoved = true;\r
+                       break;\r
+               case sc_End:\r
+                       cursor = strlen(s);\r
+                       c = key_None;\r
+                       cursormoved = true;\r
+                       break;\r
+\r
+               case sc_Return:\r
+                       strcpy(buf,s);\r
+                       done = true;\r
+                       result = true;\r
+                       c = key_None;\r
+                       break;\r
+               case sc_Escape:\r
+                       if (escok)\r
+                       {\r
+                               done = true;\r
+                               result = false;\r
+                       }\r
+                       c = key_None;\r
+                       break;\r
+\r
+               case sc_BackSpace:\r
+                       if (cursor)\r
+                       {\r
+                               strcpy(s + cursor - 1,s + cursor);\r
+                               cursor--;\r
+                               redraw = true;\r
+                       }\r
+                       c = key_None;\r
+                       cursormoved = true;\r
+                       break;\r
+               case sc_Delete:\r
+                       if (s[cursor])\r
+                       {\r
+                               strcpy(s + cursor,s + cursor + 1);\r
+                               redraw = true;\r
+                       }\r
+                       c = key_None;\r
+                       cursormoved = true;\r
+                       break;\r
+\r
+               case 0x4c:      // Keypad 5\r
+               case sc_UpArrow:\r
+               case sc_DownArrow:\r
+               case sc_PgUp:\r
+               case sc_PgDn:\r
+               case sc_Insert:\r
+                       c = key_None;\r
+                       break;\r
+               }\r
+\r
+               if (c)\r
+               {\r
+                       len = strlen(s);\r
+                       USL_MeasureString(s,&w,&h);\r
+\r
+                       if\r
+                       (\r
+                               isprint(c)\r
+                       &&      (len < MaxString - 1)\r
+                       &&      ((!maxchars) || (len < maxchars))\r
+                       &&      ((!maxwidth) || (w < maxwidth))\r
+                       )\r
+                       {\r
+                               for (i = len + 1;i > cursor;i--)\r
+                                       s[i] = s[i - 1];\r
+                               s[cursor++] = c;\r
+                               redraw = true;\r
+                       }\r
+               }\r
+\r
+               if (redraw)\r
+               {\r
+                       px = x;\r
+                       py = y;\r
+                       USL_DrawString(olds);\r
+                       strcpy(olds,s);\r
+\r
+                       px = x;\r
+                       py = y;\r
+                       USL_DrawString(s);\r
+\r
+                       redraw = false;\r
+               }\r
+\r
+               if (cursormoved)\r
+               {\r
+                       cursorvis = false;\r
+                       lasttime = TimeCount - TickBase;\r
+\r
+                       cursormoved = false;\r
+               }\r
+               if (TimeCount - lasttime > TickBase / 2)\r
+               {\r
+                       lasttime = TimeCount;\r
+\r
+                       cursorvis ^= true;\r
+               }\r
+               if (cursorvis)\r
+                       USL_XORICursor(x,y,s,cursor);\r
+\r
+               VW_UpdateScreen();\r
+       }\r
+\r
+       if (cursorvis)\r
+               USL_XORICursor(x,y,s,cursor);\r
+       if (!result)\r
+       {\r
+               px = x;\r
+               py = y;\r
+               USL_DrawString(olds);\r
+       }\r
+       VW_ShowCursor();\r
+       VW_UpdateScreen();\r
+\r
+       IN_ClearKeysDown();\r
+       return(result);\r
+}\r
+\r
+//      Control panel routines\r
+\r
+static  boolean         FlushHelp;\r
+static  WindowRec       HelpWindow,BottomWindow;\r
+typedef enum\r
+               {\r
+                       uic_Draw,uic_Hit\r
+               } UserCall;\r
+typedef enum\r
+               {\r
+                       uii_Bad,uii_Button,uii_RadioButton,uii_CheckBox,uii_KeyCap\r
+               } UIType;\r
+#define ui_Normal       0\r
+#define ui_Selected     1\r
+#define ui_Disabled     2\r
+\r
+                                       // Prototype the custom routines\r
+static  boolean         USL_CtlButtonCustom(UserCall,word,word),\r
+                                       USL_CtlPButtonCustom(UserCall,word,word),\r
+                                       USL_CtlPSButtonCustom(UserCall,word,word),\r
+                                       USL_CtlPRButtonCustom(UserCall,word,word),\r
+                                       USL_CtlHButtonCustom(UserCall,word,word),\r
+                                       USL_CtlDButtonCustom(UserCall,word,word),\r
+                                       USL_CtlDEButtonCustom(UserCall,word,word),\r
+                                       USL_CtlDLButtonCustom(UserCall,word,word),\r
+                                       USL_CtlDSButtonCustom(UserCall,word,word),\r
+                                       USL_CtlSButtonCustom(UserCall,word,word),\r
+                                       USL_CtlCButtonCustom(UserCall,word,word),\r
+                                       USL_CtlCKbdButtonCustom(UserCall,word,word),\r
+                                       USL_CtlCJoyButtonCustom(UserCall,word,word);\r
+\r
+                                       // The structure of a user interaction item\r
+typedef struct  {\r
+                                       Rect            r;                              // The enclosing rectangle\r
+                                       UIType          type;                   // The type of item\r
+                                       int                     picup,picdown;  // What to draw when up/down\r
+                                       char            *help;                  // Floating help string\r
+                                       ScanCode        key;                    // Key equiv\r
+                                       word            sel;                    // Interaction flags (ui_XXX)\r
+                                       boolean         (*custom)(UserCall,word,word);  // Custom routine\r
+                                       char            *text;                  // Text for some items\r
+                               } UserItem;\r
+typedef struct  {\r
+                                       ScanCode        key;\r
+                                       word            i,n,            // Hit CtlPanels2[i][n]\r
+                                                               toi,ton;        // Move to CtlPanels2[toi][ton]\r
+                               } HotKey;       // MARK\r
+\r
+static  ScanCode        *KeyMaps[] =\r
+                                       {\r
+                                               &KbdDefs[0].button0,&KbdDefs[0].button1,\r
+                                               &KbdDefs[0].upleft,&KbdDefs[0].up,&KbdDefs[0].upright,\r
+                                               &KbdDefs[0].left, &KbdDefs[0].right,\r
+                                               &KbdDefs[0].downleft,&KbdDefs[0].down,&KbdDefs[0].downright,\r
+                                       };\r
+\r
+// Some macros to make rectangle definition quite a bit less unpleasant\r
+#define CtlPanelX       8\r
+#define CtlPanelY       4\r
+#define CtlPanel2X      (8*8)\r
+#define CtlPanel2Y      (2*8)\r
+#define CtlPanel3X      (8*8)\r
+#define CtlPanel3Y      (7*8)\r
+\r
+#define CtlPanelR(n)    {       CtlPanelX,CtlPanelY+(32 * (n)),\\r
+                                                       CtlPanelX+40,CtlPanelY+(32 * (n)) + 32}\r
+#define CtlPanel2R(x,y) {       CtlPanel2X+(x)*8,CtlPanel2Y+(y)*8,\\r
+                                                       CtlPanel2X+32+(x)*8,CtlPanel2Y+24+(y)*8}\r
+#define CtlPanel3R(x,y) {       CtlPanel3X+(x)*8,CtlPanel3Y+(y)*8,\\r
+                                                       CtlPanel3X+32+(x)*8,CtlPanel3Y+24+(y)*8}\r
+static  UserItem        CtlPanels[] =\r
+                                       {\r
+{CtlPanelR(0),uii_RadioButton,CTL_STARTUPPIC,CTL_STARTDNPIC,"Start or Resume a Game",sc_None,ui_Normal,USL_CtlButtonCustom},\r
+{CtlPanelR(1),uii_RadioButton,CTL_HELPUPPIC,CTL_HELPDNPIC,"Get Help With Commander Keen",sc_None,ui_Normal,USL_CtlButtonCustom},\r
+{CtlPanelR(2),uii_RadioButton,CTL_DISKUPPIC,CTL_DISKDNPIC,"Load / Save / Quit",sc_None,ui_Normal,USL_CtlButtonCustom},\r
+{CtlPanelR(3),uii_RadioButton,CTL_CONTROLSUPPIC,CTL_CONTROLSDNPIC,"Choose Controls",sc_C,ui_Normal,USL_CtlButtonCustom},\r
+{CtlPanelR(4),uii_RadioButton,CTL_SOUNDUPPIC,CTL_SOUNDDNPIC,"Select Sound Device",sc_F2,ui_Normal,USL_CtlButtonCustom},\r
+{CtlPanelR(5),uii_RadioButton,CTL_MUSICUPPIC,CTL_MUSICDNPIC,"Turn Music On / Off",sc_F7,ui_Normal,USL_CtlButtonCustom},\r
+{-1,-1,-1,-1,uii_Bad}\r
+                                       },\r
+                                       CtlPPanels[] =\r
+                                       {\r
+{CtlPanel2R(10,0),uii_RadioButton,CTL_P_NEWGAMEUPPIC,CTL_P_NEWGAMEDNPIC,"Choose Difficulty for the New Game",sc_F5,ui_Normal,USL_CtlPButtonCustom},\r
+{CtlPanel2R(15,0),uii_RadioButton,CTL_P_RESUMEUPPIC,CTL_P_RESUMEDNPIC,"Go Back to Current Game",sc_None,ui_Normal,USL_CtlPButtonCustom},\r
+{-1,-1,-1,-1,uii_Bad}\r
+                                       },\r
+                                       CtlPSPanels[] =\r
+                                       {\r
+{CtlPanel3R(13,5),uii_Button,CTL_P_MEDUPPIC,CTL_P_MEDDNPIC,"Start New Game in Normal Mode",sc_None,ui_Normal,USL_CtlPSButtonCustom},\r
+{CtlPanel3R(8,5),uii_Button,CTL_P_EASYUPPIC,CTL_P_EASYDNPIC,"Start New Game in Easy Mode",sc_None,ui_Normal,USL_CtlPSButtonCustom},\r
+{CtlPanel3R(18,5),uii_Button,CTL_P_HARDUPPIC,CTL_P_HARDDNPIC,"Start New Game in Hard Mode",sc_None,ui_Normal,USL_CtlPSButtonCustom},\r
+{-1,-1,-1,-1,uii_Bad}\r
+                                       },\r
+                                       CtlPRPanels[] =\r
+                                       {\r
+{CtlPanel3R(13,5),uii_Button,CTL_P_GORESUMEUPPIC,CTL_P_GORESUMEDNPIC,"Resume Current Game",sc_None,ui_Normal,USL_CtlPRButtonCustom},\r
+{-1,-1,-1,-1,uii_Bad}\r
+                                       },\r
+                                       CtlHPanels[] =\r
+                                       {\r
+{CtlPanel2R(8,0),uii_Button,CTL_H_LOSTUPPIC,CTL_H_LOSTDNPIC,"Help Me, I'm Lost!",sc_F1,ui_Normal,USL_CtlHButtonCustom},\r
+{CtlPanel2R(13,0),uii_Button,CTL_H_CTRLUPPIC,CTL_H_CTRLDNPIC,"Get Help with Controls",sc_None,ui_Normal,USL_CtlHButtonCustom},\r
+{CtlPanel2R(18,0),uii_Button,CTL_H_STORYUPPIC,CTL_H_STORYDNPIC,"Read Story & Game Tips",sc_None,ui_Normal,USL_CtlHButtonCustom},\r
+{-1,-1,-1,-1,uii_Bad}\r
+                                       },\r
+                                       CtlDPanels[] =\r
+                                       {\r
+{CtlPanel2R(9,0),uii_RadioButton,CTL_D_LSGAMEUPPIC,CTL_D_LSGAMEDNPIC,"Load or Save a Game",sc_F6,ui_Normal,USL_CtlDButtonCustom},\r
+{CtlPanel2R(15,0),uii_RadioButton,CTL_D_DOSUPPIC,CTL_D_DOSDNPIC,"Exit to DOS",sc_Q,ui_Normal,USL_CtlDButtonCustom},\r
+{-1,-1,-1,-1,uii_Bad}\r
+                                       },\r
+                                       CtlDLSPanels[] =\r
+                                       {\r
+#define CtlPanel3LSR(x,y)       {       CtlPanel3X+(x)*8,CtlPanel3Y+(y)*8,\\r
+                                                               CtlPanel3X+32+(x)*8,CtlPanel3Y+16+(y)*8}\r
+{CtlPanel3LSR(1,0),uii_Button,CTL_D_LOADUPPIC,CTL_D_LOADDNPIC,"Load This Game",sc_None,ui_Normal,USL_CtlDLButtonCustom},\r
+{CtlPanel3LSR(6,0),uii_Button,CTL_D_SAVEUPPIC,CTL_D_SAVEDNPIC,"Save Current Game Here",sc_None,ui_Normal,USL_CtlDSButtonCustom},\r
+{CtlPanel3LSR(1,2),uii_Button,CTL_D_LOADUPPIC,CTL_D_LOADDNPIC,"Load This Game",sc_None,ui_Normal,USL_CtlDLButtonCustom},\r
+{CtlPanel3LSR(6,2),uii_Button,CTL_D_SAVEUPPIC,CTL_D_SAVEDNPIC,"Save Current Game Here",sc_None,ui_Normal,USL_CtlDSButtonCustom},\r
+{CtlPanel3LSR(1,4),uii_Button,CTL_D_LOADUPPIC,CTL_D_LOADDNPIC,"Load This Game",sc_None,ui_Normal,USL_CtlDLButtonCustom},\r
+{CtlPanel3LSR(6,4),uii_Button,CTL_D_SAVEUPPIC,CTL_D_SAVEDNPIC,"Save Current Game Here",sc_None,ui_Normal,USL_CtlDSButtonCustom},\r
+{CtlPanel3LSR(1,6),uii_Button,CTL_D_LOADUPPIC,CTL_D_LOADDNPIC,"Load This Game",sc_None,ui_Normal,USL_CtlDLButtonCustom},\r
+{CtlPanel3LSR(6,6),uii_Button,CTL_D_SAVEUPPIC,CTL_D_SAVEDNPIC,"Save Current Game Here",sc_None,ui_Normal,USL_CtlDSButtonCustom},\r
+{CtlPanel3LSR(1,8),uii_Button,CTL_D_LOADUPPIC,CTL_D_LOADDNPIC,"Load This Game",sc_None,ui_Normal,USL_CtlDLButtonCustom},\r
+{CtlPanel3LSR(6,8),uii_Button,CTL_D_SAVEUPPIC,CTL_D_SAVEDNPIC,"Save Current Game Here",sc_None,ui_Normal,USL_CtlDSButtonCustom},\r
+{CtlPanel3LSR(1,10),uii_Button,CTL_D_LOADUPPIC,CTL_D_LOADDNPIC,"Load This Game",sc_None,ui_Normal,USL_CtlDLButtonCustom},\r
+{CtlPanel3LSR(6,10),uii_Button,CTL_D_SAVEUPPIC,CTL_D_SAVEDNPIC,"Save Current Game Here",sc_None,ui_Normal,USL_CtlDSButtonCustom},\r
+{CtlPanel3LSR(1,12),uii_Button,CTL_D_LOADUPPIC,CTL_D_LOADDNPIC,"Load This Game",sc_None,ui_Normal,USL_CtlDLButtonCustom},\r
+{CtlPanel3LSR(6,12),uii_Button,CTL_D_SAVEUPPIC,CTL_D_SAVEDNPIC,"Save Current Game Here",sc_None,ui_Normal,USL_CtlDSButtonCustom},\r
+{-1,-1,-1,-1,uii_Bad}\r
+                                       },\r
+                                       CtlDEPanels[] =\r
+                                       {\r
+#define CtlPanel3ER(x,y)        {       CtlPanel3X+(x)*8,CtlPanel3Y+(y)*8,\\r
+                                                               CtlPanel3X+40+(x)*8,CtlPanel3Y+24+(y)*8}\r
+{CtlPanel3ER(12,5),uii_Button,CTL_D_EXITUPPIC,CTL_D_EXITDNPIC,"Really Exit to DOS",sc_None,ui_Normal,USL_CtlDEButtonCustom},\r
+{-1,-1,-1,-1,uii_Bad}\r
+                                       },\r
+                                       CtlCPanels[] =\r
+                                       {\r
+{CtlPanel2R(8,0),uii_RadioButton,CTL_C_KBDUPPIC,CTL_C_KBDDNPIC,"Use / Configure Keyboard",sc_F3,ui_Normal,USL_CtlCButtonCustom},\r
+{CtlPanel2R(13,0),uii_RadioButton,CTL_C_JOY1UPPIC,CTL_C_JOY1DNPIC,"Use / Configure Joystick 1",sc_None,ui_Normal,USL_CtlCButtonCustom},\r
+{CtlPanel2R(18,0),uii_RadioButton,CTL_C_JOY2UPPIC,CTL_C_JOY2DNPIC,"Use / Configure Joystick 2",sc_None,ui_Normal,USL_CtlCButtonCustom},\r
+{-1,-1,-1,-1,uii_Bad}\r
+                                       },\r
+#define CtlPanelKC3R(x,y)       {       CtlPanel3X+(x)*8,CtlPanel3Y+(y)*8,\\r
+                                                               CtlPanel3X+56+(x)*8,CtlPanel3Y+32+(y)*8}\r
+                                       CtlCKbdPanels[] =\r
+                                       {\r
+{CtlPanelKC3R(1,2),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key for Jumping",sc_None,ui_Normal,USL_CtlCKbdButtonCustom},\r
+{CtlPanelKC3R(1,6),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key for Throwing",sc_None,ui_Normal,USL_CtlCKbdButtonCustom},\r
+{CtlPanelKC3R(8,0),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Up & Left",sc_None,ui_Normal,USL_CtlCKbdButtonCustom},\r
+{CtlPanelKC3R(15,0),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Up",sc_None,ui_Normal,USL_CtlCKbdButtonCustom},\r
+{CtlPanelKC3R(22,0),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Up & Right",sc_None,ui_Normal,USL_CtlCKbdButtonCustom},\r
+{CtlPanelKC3R(8,4),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Left",sc_None,ui_Normal,USL_CtlCKbdButtonCustom},\r
+{CtlPanelKC3R(22,4),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Right",sc_None,ui_Normal,USL_CtlCKbdButtonCustom},\r
+{CtlPanelKC3R(8,8),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Down & Left",sc_None,ui_Normal,USL_CtlCKbdButtonCustom},\r
+{CtlPanelKC3R(15,8),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Down",sc_None,ui_Normal,USL_CtlCKbdButtonCustom},\r
+{CtlPanelKC3R(22,8),uii_KeyCap,CTL_KEYCAPPIC,CTL_KEYCAPCURPIC,"Define Key to move Down & Right",sc_None,ui_Normal,USL_CtlCKbdButtonCustom},\r
+{-1,-1,-1,-1,uii_Bad}\r
+                                       },\r
+                                       CtlCJoyPanels[] =\r
+                                       {\r
+{CtlPanel3R(13,5),uii_Button,CTL_C_CALIBRATEUPPIC,CTL_C_CALIBRATEDNPIC,"Configure Joystick",sc_None,ui_Normal,USL_CtlCJoyButtonCustom},\r
+{-1,-1,-1,-1,uii_Bad}\r
+                                       },\r
+                                       CtlSPanels[] =\r
+                                       {\r
+{CtlPanel2R(3,0),uii_RadioButton,CTL_S_NOSNDUPPIC,CTL_S_NOSNDDNPIC,"Turn Sound Off",sc_None,ui_Normal,USL_CtlSButtonCustom},\r
+{CtlPanel2R(8,0),uii_RadioButton,CTL_S_PCSNDUPPIC,CTL_S_PCSNDDNPIC,"Use PC Speaker",sc_None,ui_Normal,USL_CtlSButtonCustom},\r
+{CtlPanel2R(13,0),uii_RadioButton,CTL_S_ADLIBUPPIC,CTL_S_ADLIBDNPIC,"Use AdLib Sound Effects",sc_None,ui_Normal,USL_CtlSButtonCustom},\r
+{CtlPanel2R(18,0),uii_RadioButton,CTL_S_SNDBLUPPIC,CTL_S_SNDBLDNPIC,"Use SoundBlaster Sound Effects",sc_None,ui_Normal,USL_CtlSButtonCustom},\r
+{CtlPanel2R(23,0),uii_RadioButton,CTL_S_SNDSRCUPPIC,CTL_S_SNDSRCDNPIC,"Use Sound Source Sound Effects",sc_None,ui_Normal,USL_CtlSButtonCustom},\r
+{-1,-1,-1,-1,uii_Bad}\r
+                                       },\r
+                                       CtlSSSPanels[] =\r
+                                       {\r
+{CtlPanel3R(7,2),uii_CheckBox,CTL_CHECKUPPIC,CTL_CHECKDNPIC,"Turn Tandy Mode On / Off",sc_None,ui_Normal,0,"Tandy Mode"},\r
+{CtlPanel3R(7,6),uii_CheckBox,CTL_CHECKUPPIC,CTL_CHECKDNPIC,"Switch between LPT1 & LPT2",sc_None,ui_Normal,0,"Use LPT2"},\r
+{-1,-1,-1,-1,uii_Bad}\r
+                                       },\r
+                                       CtlMPanels[] =\r
+                                       {\r
+{CtlPanel2R(9,0),uii_RadioButton,CTL_M_NOMUSUPPIC,CTL_M_NOMUSDNPIC,"Background Music Off"},\r
+{CtlPanel2R(15,0),uii_RadioButton,CTL_M_ADLIBUPPIC,CTL_M_ADLIBDNPIC,"Use AdLib/SoundBlaster Music"},\r
+{-1,-1,-1,-1,uii_Bad}\r
+                                       },\r
+                                       *CtlPanels2[] =\r
+                                       {\r
+                                               CtlPPanels,     // Start\r
+                                               CtlHPanels,     // Help\r
+                                               CtlDPanels,     // Disk\r
+                                               CtlCPanels,     // Controls\r
+                                               CtlSPanels,     // Sound\r
+                                               CtlMPanels      // Music\r
+                                       },\r
+                                       *TheItems[4] = {CtlPanels};\r
+static  int                     CtlPanelButton;\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_TurnOff() - Goes through a list of UserItems and sets them all to\r
+//              the normal state\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_TurnOff(UserItem *ip)\r
+{\r
+       while (ip->type != uii_Bad)\r
+       {\r
+               ip->sel = ui_Normal;\r
+               ip++;\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_FindDown() - Finds which UserItem, if any, is selected in the given\r
+//              list\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static int\r
+USL_FindDown(UserItem *ip)\r
+{\r
+       int     i;\r
+\r
+       for (i = 0;ip->type != uii_Bad;i++,ip++)\r
+               if (ip->sel & ui_Selected)\r
+                       return(i);\r
+       return(-1);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_ShowHelp() - Shows the specified string in the help window\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_ShowHelp(char *s)\r
+{\r
+       WindowRec       wr;\r
+\r
+       if (!s)\r
+               return;\r
+\r
+       US_SaveWindow(&wr);\r
+       US_RestoreWindow(&HelpWindow);\r
+\r
+       US_ClearWindow();\r
+       US_PrintCentered(s);\r
+\r
+       US_RestoreWindow(&wr);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_HandleError() - Handles telling the user that there's been an error\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_HandleError(int num)\r
+{\r
+       char    buf[64];\r
+\r
+       strcpy(buf,"Error: ");\r
+       if (num < 0)\r
+               strcat(buf,"Unknown");\r
+       else if (num == ENOMEM)\r
+               strcat(buf,"Disk is Full");\r
+       else if (num == EINVFMT)\r
+               strcat(buf,"File is Incomplete");\r
+       else\r
+               strcat(buf,sys_errlist[num]);\r
+\r
+       VW_HideCursor();\r
+\r
+       fontcolor = F_SECONDCOLOR;\r
+       USL_ShowHelp(buf);\r
+       fontcolor = F_BLACK;\r
+       VW_UpdateScreen();\r
+\r
+       IN_ClearKeysDown();\r
+       IN_Ack();\r
+\r
+       VW_ShowCursor();\r
+       VW_UpdateScreen();\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_DrawItem() - Draws a UserItem. If there's a custom routine, this will\r
+//              call it with a uic_Draw command. If the custom routine returns true,\r
+//              then the routine handled all of the drawing. If it returns false,\r
+//              then this routine does the default drawing.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_DrawItem(word hiti,word hitn)\r
+{\r
+       boolean         handled,centered;\r
+       char            *text;\r
+       word            w,h;\r
+       int                     picup,picdown;\r
+       Rect            r;\r
+       UserItem        *ip;\r
+\r
+       ip = &TheItems[hiti][hitn];\r
+       if (ip->custom)\r
+               handled = ip->custom(uic_Draw,hiti,hitn);\r
+       else\r
+               handled = false;\r
+\r
+       if (!handled)\r
+       {\r
+               picup = ip->picup;\r
+               picdown = ip->picdown;\r
+               switch (ip->type)\r
+               {\r
+               case uii_CheckBox:\r
+                       px = ip->r.lr.x + 8;\r
+                       py = ip->r.ul.y + 8;\r
+                       text = ip->text;\r
+                       centered = false;\r
+                       break;\r
+               case uii_KeyCap:\r
+                       if (!(ip->sel & ui_Selected))\r
+                       {\r
+                               text = ip->text;\r
+                               if (text)\r
+                               {\r
+                                       r = ip->r;\r
+                                       centered = true;\r
+                               }\r
+                       }\r
+                       else\r
+                               text = nil;\r
+                       break;\r
+               default:\r
+                       text = nil;\r
+                       break;\r
+               }\r
+\r
+               VWB_DrawPic(ip->r.ul.x,ip->r.ul.y,\r
+                                               (ip->sel & ui_Selected)? picdown : picup);\r
+               if (text)\r
+               {\r
+                       if (centered)\r
+                               USL_PrintInCenter(text,r);\r
+                       else\r
+                       {\r
+                               USL_MeasureString(text,&w,&h);\r
+                               VWB_Bar(px,py,w + 7,h,WHITE);\r
+                               USL_DrawString(text);\r
+                       }\r
+               }\r
+               if (ip->sel & ui_Disabled)\r
+               {\r
+                       if ((picup == CTL_D_LOADUPPIC) || (picup == CTL_D_SAVEUPPIC))\r
+                               VWB_DrawMPic(ip->r.ul.x,ip->r.ul.y,CTL_LSMASKPICM);\r
+                       else\r
+                               VWB_DrawMPic(ip->r.ul.x,ip->r.ul.y,CTL_LITTLEMASKPICM);\r
+               }\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_DoHit() - Handles a hit on a UserItem. If there's a custom routine,\r
+//                      it will be called. If it returns true, then don't do anything\r
+//                      more. If it returns false, then use the standard behaviour\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_DoHit(word hiti,word hitn)\r
+{\r
+       boolean         handled;\r
+       word            i;\r
+       UserItem        *ip;\r
+\r
+       ip = &TheItems[hiti][hitn];\r
+       if (ip->custom)\r
+               handled = ip->custom(uic_Hit,hiti,hitn);\r
+       else\r
+               handled = false;\r
+\r
+       if (!handled)\r
+       {\r
+               if (TheItems[hiti][hitn].sel & ui_Disabled)\r
+               {\r
+                       fontcolor = F_SECONDCOLOR;\r
+                       USL_ShowHelp("This Item is Disabled");\r
+                       fontcolor = F_BLACK;\r
+                       return;\r
+               }\r
+\r
+               FlushHelp = true;\r
+\r
+               switch (ip->type)\r
+               {\r
+               case uii_Button:\r
+                       // Must have a custom routine to handle hits - this just redraws\r
+                       ip->sel ^= ui_Selected;\r
+                       USL_DrawItem(hiti,hitn);\r
+               case uii_CheckBox:\r
+                       ip->sel ^= ui_Selected;\r
+                       USL_DrawItem(hiti,hitn);\r
+                       break;\r
+               case uii_RadioButton:\r
+                       for (i = 0,ip = TheItems[hiti];ip->type != uii_Bad;i++,ip++)\r
+                       {\r
+                               if\r
+                               (\r
+                                       (i != hitn)\r
+                               &&      (ip->type == uii_RadioButton)\r
+                               &&      (ip->sel & ui_Selected)\r
+                               )\r
+                               {\r
+                                       ip->sel &= ~ui_Selected;\r
+                                       USL_DrawItem(hiti,i);\r
+                               }\r
+                       }\r
+                       TheItems[hiti][hitn].sel |= ui_Selected;\r
+                       USL_DrawItem(hiti,hitn);\r
+                       break;\r
+               case uii_KeyCap:\r
+                       break;\r
+               }\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_IsInRect() - Checks to see if the coordinates given are within any\r
+//              of the Rects in the UserItem list. If so, returns true & sets the\r
+//              index & number for lookup. If not, returns false.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_IsInRect(word x,word y,word *index,word *number)\r
+{\r
+       UserItem        *item,**items;\r
+\r
+       items = TheItems;\r
+       *index = 0;\r
+       while (*items)\r
+       {\r
+               item = *items;\r
+               *number = 0;\r
+               while (item->type != uii_Bad)\r
+               {\r
+                       if\r
+                       (\r
+                               (x >= item->r.ul.x)\r
+                       &&      (x <  item->r.lr.x)\r
+                       &&      (y >= item->r.ul.y)\r
+                       &&      (y <  item->r.lr.y)\r
+                       )\r
+                               return(true);\r
+                       (*number)++;\r
+                       item++;\r
+               }\r
+\r
+               (*index)++;\r
+               items++;\r
+       }\r
+       return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_TrackItem() - Tracks the given item. If the cursor is inside of the\r
+//              item, it's redrawn as down. If the cursor is outside, the item is\r
+//              drawn in its original state. Returns true if the button was released\r
+//              while the cursor was inside the item, or false if it wasn't.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_TrackItem(word hiti,word hitn)\r
+{\r
+       boolean         inside,last;\r
+       word            ini,inn,\r
+                               on,\r
+                               sel,othersel;\r
+       UserItem        *ip,*op;\r
+\r
+       ip = &TheItems[hiti][hitn];\r
+       sel = ip->sel;\r
+       if (ip->type == uii_RadioButton)\r
+       {\r
+               inside = false;\r
+               for (op = TheItems[hiti],on = 0;op->type != uii_Bad;op++,on++)\r
+               {\r
+                       if (op->sel & ui_Selected)\r
+                       {\r
+                               inside = true;\r
+                               break;\r
+                       }\r
+               }\r
+               if (!inside)\r
+                       op = ip;\r
+               othersel = op->sel;\r
+       }\r
+       else\r
+               op = nil;\r
+\r
+       if (ip->sel & ui_Disabled)\r
+       {\r
+               fontcolor = F_SECONDCOLOR;\r
+               USL_ShowHelp("This item is disabled");\r
+               fontcolor = F_BLACK;\r
+\r
+               while (US_UpdateCursor())\r
+                       VW_UpdateScreen();\r
+\r
+               FlushHelp = true;\r
+               return(false);\r
+       }\r
+\r
+       last = false;\r
+       do\r
+       {\r
+               USL_IsInRect(CursorX,CursorY,&ini,&inn);\r
+               inside = (ini == hiti) && (inn == hitn);\r
+               if (inside != last)\r
+               {\r
+                       if (inside)\r
+                       {\r
+                               if (op)\r
+                               {\r
+                                       op->sel &= ~ui_Selected;\r
+                                       ip->sel |= ui_Selected;\r
+                               }\r
+                               else\r
+                                       ip->sel = sel ^ ui_Selected;\r
+                       }\r
+                       else\r
+                       {\r
+                               if (op && (op != ip))\r
+                               {\r
+                                       op->sel |= ui_Selected;\r
+                                       ip->sel &= ~ui_Selected;\r
+                               }\r
+                               else\r
+                                       ip->sel = sel;\r
+                       }\r
+\r
+                       USL_DrawItem(hiti,hitn);\r
+                       if (op && (op != ip))\r
+                               USL_DrawItem(hiti,on);\r
+\r
+                       last = inside;\r
+               }\r
+               VW_UpdateScreen();\r
+       } while (US_UpdateCursor());\r
+\r
+       if (op)\r
+               op->sel = othersel;\r
+       ip->sel = sel;\r
+       if (!inside)\r
+       {\r
+               if (op && (op != ip))\r
+                       USL_DrawItem(hiti,on);\r
+               USL_DrawItem(hiti,hitn);\r
+               VW_UpdateScreen();\r
+       }\r
+\r
+       return(inside);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_GlideCursor() - Smoothly moves the cursor to the given location\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_GlideCursor(long newx,long newy)\r
+{\r
+       word    steps;\r
+       long    x,y,\r
+                       dx,dy;\r
+\r
+       if (grmode == CGAGR)\r
+               steps = 1;\r
+       else\r
+               steps = 8;\r
+\r
+       x = (long)CursorX << 16;\r
+       dx = ((newx << 16) - x) / steps;\r
+       y = (long)CursorY << 16;\r
+       dy = ((newy << 16) - y) / steps;\r
+\r
+       while ((CursorX != newx) || (CursorY != newy))\r
+       {\r
+               x += dx;\r
+               y += dy;\r
+\r
+               CursorX = x >> 16;\r
+               CursorY = y >> 16;\r
+               VW_MoveCursor(CursorX,CursorY);\r
+               VW_UpdateScreen();\r
+       }\r
+       CursorBad = true;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_FindRect() - Code so ugly you'll puke! Given a Rect and direction,\r
+//      this routine will try to find a UserItem to move the cursor to\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_FindRect(Rect r,Motion xd,Motion yd)\r
+{\r
+       word            i,i1,i2,i3;\r
+       Motion          m1,m2;\r
+       Point           diffs[9],diff,*dp;\r
+       Rect            *rp,*good,*goods[9];\r
+       UserItem        *ip,**items;\r
+\r
+       for (m1 = motion_Up,dp = diffs;m1 <= motion_Down;m1++)\r
+       {\r
+               for (m2 = motion_Left;m2 <= motion_Right;m2++,dp++)\r
+               {\r
+                       dp->x = m2 * 1024;\r
+                       dp->y = m1 * 1024;\r
+               }\r
+       }\r
+       for (i = 0;i < 9;i++)\r
+               goods[i] = nil;\r
+\r
+       // Find out which octants all of the rects (except r) are in\r
+       for (items = TheItems;*items;items++)\r
+       {\r
+               for (ip = *items;ip->type != uii_Bad;ip++)\r
+               {\r
+                       rp = &ip->r;\r
+                       diff.x = rp->ul.x - r.ul.x;\r
+                       diff.y = rp->ul.y - r.ul.y;\r
+                       if (!(diff.x || diff.y))\r
+                               continue;\r
+\r
+                       if      // 1,4,7\r
+                       (\r
+                               ((rp->ul.x >= r.ul.x) && (rp->ul.x < r.lr.x))\r
+                       ||      ((rp->lr.x > r.ul.x) && (rp->lr.x <= r.lr.x))\r
+                       )\r
+                       {\r
+                               if (rp->lr.y <= r.ul.y)\r
+                               {\r
+                                       if (!(goods[1] && (diff.y < diffs[1].y)))\r
+                                       {\r
+                                               goods[1] = rp;\r
+                                               diffs[1] = diff;\r
+                                       }\r
+                               }\r
+                               else if (rp->ul.y >= r.lr.y)\r
+                               {\r
+                                       if (!(goods[7] && (diff.y > diffs[7].y)))\r
+                                       {\r
+                                               goods[7] = rp;\r
+                                               diffs[7] = diff;\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       if      // 3,4,5\r
+                       (\r
+                               ((rp->ul.y >= r.ul.y) && (rp->ul.y < r.lr.y))\r
+                       ||      ((rp->lr.y > r.ul.y) && (rp->lr.y <= r.lr.y))\r
+                       )\r
+                       {\r
+                               if (rp->lr.x <= r.ul.x)\r
+                               {\r
+                                       if (!(goods[3] && (diff.x < diffs[3].x)))\r
+                                       {\r
+                                               goods[3] = rp;\r
+                                               diffs[3] = diff;\r
+                                       }\r
+                               }\r
+                               else if (rp->ul.x >= r.lr.x)\r
+                               {\r
+                                       if (!(goods[5] && (diff.x > diffs[5].x)))\r
+                                       {\r
+                                               goods[5] = rp;\r
+                                               diffs[5] = diff;\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       if (rp->ul.x < r.ul.x)  // 0,6\r
+                       {\r
+                               if (rp->lr.y <= r.ul.y)\r
+                               {\r
+                                       if\r
+                                       (\r
+                                               (!goods[0])\r
+                                       ||      (diff.y > diffs[0].y)\r
+                                       ||      (diff.x > diffs[6].x)\r
+                                       )\r
+                                       {\r
+                                               goods[0] = rp;\r
+                                               diffs[0] = diff;\r
+                                       }\r
+                               }\r
+                               else if (rp->ul.y >= r.lr.y)\r
+                               {\r
+                                       if\r
+                                       (\r
+                                               (!goods[6])\r
+                                       ||      (diff.y < diffs[6].y)\r
+                                       ||      (diff.x > diffs[6].x)\r
+                                       )\r
+                                       {\r
+                                               goods[6] = rp;\r
+                                               diffs[6] = diff;\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       if (rp->lr.x > r.lr.x)  // 2,8\r
+                       {\r
+                               if (rp->lr.y <= r.ul.y)\r
+                               {\r
+                                       if\r
+                                       (\r
+                                               (!goods[2])\r
+                                       ||      (diff.y > diffs[2].y)\r
+                                       ||      (diff.x < diffs[2].x)\r
+                                       )\r
+                                       {\r
+                                               goods[2] = rp;\r
+                                               diffs[2] = diff;\r
+                                       }\r
+                               }\r
+                               else if (rp->ul.y >= r.lr.y)\r
+                               {\r
+                                       if\r
+                                       (\r
+                                               (!goods[8])\r
+                                       ||      (diff.y < diffs[8].y)\r
+                                       ||      (diff.x < diffs[8].x)\r
+                                       )\r
+                                       {\r
+                                               goods[8] = rp;\r
+                                               diffs[8] = diff;\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       switch (yd)\r
+       {\r
+       case motion_Up:\r
+               i1 = 1,i2 = 0,i3 = 2;\r
+               break;\r
+       case motion_None:\r
+               switch (xd)\r
+               {\r
+               case motion_Left:\r
+                       i1 = 3,i2 = 0,i3 = 6;\r
+                       break;\r
+               case motion_Right:\r
+                       i1 = 5,i2 = 8,i3 = 2;\r
+                       break;\r
+               }\r
+               break;\r
+       case motion_Down:\r
+               i1 = 7,i2 = 8,i3 = 6;\r
+               break;\r
+       }\r
+\r
+       (\r
+               (good = goods[i1])\r
+       ||      (good = goods[i2])\r
+       ||      (good = goods[i3])\r
+       ||      (good = &r)\r
+       );\r
+#if 0\r
+       CursorX = good->lr.x - 8;\r
+       CursorY = good->lr.y - 8;\r
+       CursorBad = true;\r
+       US_UpdateCursor();\r
+#endif\r
+       USL_GlideCursor(good->lr.x - 8,good->lr.y - 8);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_CtlButtonCustom() - The custom routine for all of the Control Panel\r
+//              (leftmost) buttons. Clears all of the other item lists, clears the\r
+//              large area, and draws the line dividing the top and bottom areas.\r
+//              Then it sets up and draws the appropriate top row of icons.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_CtlButtonCustom(UserCall call,word i,word n)\r
+{\r
+       word            j;\r
+       UserItem        *ip;\r
+\r
+       if (call != uic_Hit)\r
+               return(false);\r
+\r
+       if (n == CtlPanelButton)\r
+               return(true);\r
+\r
+       US_ClearWindow();\r
+       for (j = 8;j < 38;j++)\r
+       {\r
+               VWB_DrawTile8M(j * 8,6 * 8,10);\r
+               VWB_DrawTile8M(j * 8,21 * 8,10);\r
+       }\r
+       VWB_DrawTile8M(7 * 8,6 * 8,9);\r
+       VWB_DrawTile8M(38 * 8,6 * 8,11);\r
+       VWB_DrawTile8M(7 * 8,21 * 8,9);\r
+       VWB_DrawTile8M(38 * 8,21 * 8,11);\r
+\r
+       for (j = 1;j < 4;j++)\r
+               TheItems[j] = nil;\r
+\r
+       // Set to new button\r
+       CtlPanelButton = n;\r
+\r
+       // Draw new items\r
+       TheItems[1] = ip = CtlPanels2[CtlPanelButton];\r
+       j = 0;\r
+       while (ip && (ip->type != uii_Bad))\r
+       {\r
+               USL_DrawItem(i + 1,j);\r
+               if (ip->sel & ui_Selected)\r
+                       USL_DoHit(i + 1,j);\r
+               j++;\r
+               ip++;\r
+       }\r
+\r
+       return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_CtlCKbdButtonCustom() - The custom routine for the keyboard keycaps.\r
+//              This routine gets a scancode and puts it in the appropriate\r
+//              KbdDefs[0] member.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_CtlCKbdButtonCustom(UserCall call,word i,word n)\r
+{\r
+       boolean         state;\r
+       word            j;\r
+       ScanCode        scan;\r
+       longword        time;\r
+       UserItem        *ip;\r
+\r
+       if (call != uic_Hit)\r
+               return(false);\r
+\r
+       ip = &TheItems[i][n];\r
+\r
+       fontcolor = F_SECONDCOLOR;\r
+       USL_ShowHelp(ip->help);\r
+       fontcolor = F_BLACK;\r
+       VW_HideCursor();\r
+       VWB_DrawPic(ip->r.ul.x,ip->r.ul.y,ip->picdown);\r
+       VW_UpdateScreen();\r
+\r
+       LastScan = sc_None;\r
+       time = TimeCount;\r
+       state = true;\r
+       do\r
+       {\r
+               if (TimeCount - time > 35)      // Half-second delays\r
+               {\r
+                       state ^= true;\r
+                       VWB_DrawPic(ip->r.ul.x,ip->r.ul.y,state? ip->picdown : ip->picup);\r
+                       VW_UpdateScreen();\r
+                       time = TimeCount;\r
+               }\r
+               if (US_UpdateCursor())\r
+               {\r
+                       while (US_UpdateCursor())\r
+                               ;\r
+                       scan = sc_Escape;\r
+                       break;\r
+               }\r
+\r
+               asm     pushf\r
+               asm     cli\r
+               if (LastScan == sc_LShift)\r
+                       LastScan = sc_None;\r
+               asm     popf\r
+       } while (!(scan = LastScan));\r
+       IN_ClearKey(scan);\r
+       if (scan != sc_Escape)\r
+       {\r
+               for (j = 0,state = false;j < 10;j++)\r
+               {\r
+                       if (j == n)\r
+                               continue;\r
+                       if (*(KeyMaps[j]) == scan)\r
+                       {\r
+                               state = true;\r
+                               break;\r
+                       }\r
+               }\r
+               if (state)\r
+               {\r
+                       fontcolor = F_SECONDCOLOR;\r
+                       USL_ShowHelp("That Key is Already Used!");\r
+                       fontcolor = F_BLACK;\r
+               }\r
+               else\r
+               {\r
+                       ip->text = IN_GetScanName(scan);\r
+                       *(KeyMaps[n]) = scan;\r
+                       FlushHelp = true;\r
+               }\r
+       }\r
+\r
+       USL_DrawItem(i,n);\r
+       VW_ShowCursor();\r
+       VW_UpdateScreen();\r
+\r
+       return(true);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_CtlCJoyButtonCustom() - The custom button routine for joystick\r
+//              calibration\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_CtlCJoyButtonCustom(UserCall call,word i,word n)\r
+{\r
+       word    joy,\r
+                       minx,maxx,\r
+                       miny,maxy;\r
+\r
+       i++,n++;        // Shut the compiler up\r
+\r
+       if (call != uic_Hit)\r
+               return(false);\r
+\r
+       IN_ClearKeysDown();\r
+       joy = USL_FindDown(CtlCPanels) - 1;\r
+\r
+       VW_HideCursor();\r
+       FlushHelp = true;\r
+       fontcolor = F_SECONDCOLOR;\r
+\r
+       USL_ShowHelp("Move Joystick to the Upper-Left");\r
+       VW_UpdateScreen();\r
+       while ((LastScan != sc_Escape) && !IN_GetJoyButtonsDB(joy))\r
+               ;\r
+       if (LastScan != sc_Escape)\r
+       {\r
+               IN_GetJoyAbs(joy,&minx,&miny);\r
+               while (IN_GetJoyButtonsDB(joy))\r
+                       ;\r
+\r
+               USL_ShowHelp("Move Joystick to the Lower-Right");\r
+               VW_UpdateScreen();\r
+               while ((LastScan != sc_Escape) && !IN_GetJoyButtonsDB(joy))\r
+                       ;\r
+               if (LastScan != sc_Escape)\r
+               {\r
+                       IN_GetJoyAbs(0,&maxx,&maxy);\r
+                       IN_SetupJoy(joy,minx,maxx,miny,maxy);\r
+               }\r
+       }\r
+\r
+       if (LastScan != sc_Escape)\r
+               while (IN_GetJoyButtonsDB(joy))\r
+                       ;\r
+\r
+       if (LastScan)\r
+               IN_ClearKeysDown();\r
+\r
+       fontcolor = F_BLACK;\r
+       VW_ShowCursor();\r
+\r
+       return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_ClearBottom() - Clears the bottom part of the window\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_ClearBottom(void)\r
+{\r
+       WindowRec       wr;\r
+\r
+       US_SaveWindow(&wr);\r
+       US_RestoreWindow(&BottomWindow);\r
+\r
+       US_ClearWindow();\r
+\r
+       US_RestoreWindow(&wr);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_FormatHelp() - Formats helptext. Runs through and calculates the\r
+//              number of lines, and the offset for the start of each line. Stops\r
+//              after len bytes or when it hits a tilde ('~'). Munges the text.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static word\r
+USL_FormatHelp(char far *text,long len)\r
+{\r
+       word    line,\r
+                       w,h,\r
+                       far *off;\r
+       char    c,\r
+                       far *s,far *l,far *le;\r
+\r
+       WindowX += 4;\r
+       WindowW -= 4;\r
+\r
+       MM_GetPtr(&LineOffsets,MaxHelpLines * sizeof(word));\r
+       off = (word far *)LineOffsets;\r
+       for (line = 0,le = l = s = text;(s - text < len) && (*s != '~');s++)\r
+       {\r
+               if ((c = *s) == '\n')\r
+               {\r
+                       *s = '\0';\r
+                       *off++ = l - text;      // Save offset of start of line\r
+                       line++;                         // Bump line number\r
+                       le = l = s + 1;         // Set start of line ptr\r
+               }\r
+\r
+               if (c == '\r')\r
+                       c = *s = ' ';\r
+               if                                              // Strip orphaned spaces\r
+               (\r
+                       (c == ' ')\r
+               &&      (s == l)\r
+               &&      (*(s - 1) == '\0')\r
+               &&      (*(s + 1) != ' ')\r
+               &&      (s > text)\r
+               )\r
+                       le = l = s + 1;\r
+               else if (c == ' ')\r
+               {\r
+                       *s = '\0';\r
+                       USL_MeasureString(l,&w,&h);\r
+                       if (w >= WindowW)       // If string width exceeds window,\r
+                       {\r
+                               *s = c;                 // Replace null char with proper char\r
+                               *le = '\0';             // Go back to last line end\r
+                               *off++ = l - text;      // Save offset of start of line\r
+                               line++;                 // Bump line number\r
+                               l = s = le + 1; // Start next time through after last line end\r
+                       }\r
+                       else\r
+                       {\r
+                               *s = c;                 // Width still ok - put char back\r
+                               le = s;                 // And save ptr to last ok end of word\r
+                       }\r
+               }\r
+       }\r
+\r
+       WindowX -= 4;\r
+       WindowW += 4;\r
+\r
+       return(line);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_DrawHelp() - Draws helptext in the current window\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_DrawHelp(char far *text,word start,word end,word line,word h,word far *lp)\r
+{\r
+       px = WindowX + 4;\r
+       py = WindowY + (line * h);\r
+       for (lp += start;start < end;start++,px = WindowX + 4,py += h)\r
+               USL_DrawString(text + *lp++);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_DoHelp() - Formats and displays the specified help\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_DoHelp(memptr text,long len)\r
+{\r
+       boolean         done,\r
+                               moved;\r
+       int                     scroll;\r
+       word            i,\r
+                               pixdiv,\r
+                               w,h,\r
+                               lines,cur,page,\r
+                               top,num,loc,\r
+                               far *lp,\r
+                               base,srcbase,destbase;\r
+       ScanCode        waitkey;\r
+       longword        lasttime;\r
+       WindowRec       wr;\r
+       CursorInfo      info;\r
+\r
+       USL_ShowHelp("Arrow Keys Move / Escape Exits");\r
+       fontcolor = F_BLACK;\r
+\r
+       US_SaveWindow(&wr);\r
+       US_RestoreWindow(&BottomWindow);\r
+       US_ClearWindow();\r
+\r
+       VW_HideCursor();\r
+       VW_UpdateScreen();\r
+\r
+       lines = USL_FormatHelp((char far *)text,len);\r
+       USL_MeasureString("",&w,&h);\r
+       page = WindowH / h;\r
+       cur = 0;\r
+       lp = LineOffsets;\r
+\r
+       IN_ClearKeysDown();\r
+       moved = true;\r
+       lasttime = 0;\r
+       scroll = 0;\r
+       done = false;\r
+       waitkey = sc_None;\r
+       while (!done)\r
+       {\r
+               if (moved)\r
+               {\r
+                       while (TimeCount - lasttime < 5)\r
+                               ;\r
+                       lasttime = TimeCount;\r
+\r
+                       if (scroll == -1)\r
+                       {\r
+                               top = cur;\r
+                               num = 1;\r
+                               loc = 0;\r
+                       }\r
+                       else if (scroll == +1)\r
+                       {\r
+                               num = 1;\r
+                               loc = page - 1;\r
+                               top = cur + loc;\r
+                       }\r
+                       else\r
+                       {\r
+                               top = cur;\r
+                               num = (page < lines)? page : lines;\r
+                               loc = 0;\r
+                       }\r
+                       if (scroll)\r
+                       {\r
+                               if (grmode == CGAGR)\r
+                               {\r
+                                       pixdiv = 4;\r
+                                       base = bufferofs + panadjust + (WindowX / pixdiv);\r
+                               }\r
+                               else if (grmode == EGAGR)\r
+                               {\r
+                                       VWB_Bar(WindowX,WindowY + (loc * h),WindowW,num * h,WHITE);\r
+                                       USL_DrawHelp((char far *)text,top,top + num,loc,h,lp);\r
+\r
+                                       pixdiv = 8;\r
+                                       base = displayofs + panadjust + (WindowX / pixdiv);\r
+                               }\r
+                               else if (grmode == VGAGR)\r
+                                       pixdiv = 1;\r
+\r
+                               if (scroll == 1)\r
+                               {\r
+                                       srcbase = base + ylookup[WindowY + h];\r
+                                       destbase = base + ylookup[WindowY];\r
+                                       if (grmode == EGAGR)\r
+                                       {\r
+                                               EGAWRITEMODE(1);\r
+                                               VW_WaitVBL(1);\r
+                                       }\r
+                                       VW_ScreenToScreen(srcbase,destbase,WindowW / pixdiv,\r
+                                                                               WindowH - h);\r
+                               }\r
+                               else\r
+                               {\r
+                                       i = WindowY + (h * (page - 1));\r
+                                       srcbase = base + ylookup[i - h];\r
+                                       destbase = base + ylookup[i];\r
+                                       base = ylookup[h];\r
+                                       for (i = page - 1;i;i--,srcbase -= base,destbase -= base)\r
+                                               VW_ScreenToScreen(srcbase,destbase,WindowW / pixdiv,h);\r
+                               }\r
+                               if (grmode == CGAGR)\r
+                               {\r
+                                       VWB_Bar(WindowX,WindowY + (loc * h),WindowW,num * h,WHITE);\r
+                                       USL_DrawHelp((char far *)text,top,top + num,loc,h,lp);\r
+                                       VW_UpdateScreen();\r
+                               }\r
+                               else if (grmode == EGAGR)\r
+                               {\r
+                                       base = panadjust + (WindowX / pixdiv) +\r
+                                                       ylookup[WindowY + (loc * h)];\r
+                                       VW_ScreenToScreen(base + bufferofs,base + displayofs,\r
+                                                                               WindowW / pixdiv,h);\r
+                               }\r
+                       }\r
+                       else\r
+                       {\r
+                               US_ClearWindow();\r
+                               USL_DrawHelp((char far *)text,top,top + num,loc,h,lp);\r
+                               VW_UpdateScreen();\r
+                       }\r
+\r
+                       moved = false;\r
+                       scroll = 0;\r
+               }\r
+\r
+               if (waitkey)\r
+                       while (IN_KeyDown(waitkey))\r
+                               ;\r
+               waitkey = sc_None;\r
+\r
+               IN_ReadCursor(&info);\r
+               if (info.y < 0)\r
+               {\r
+                       if (cur > 0)\r
+                       {\r
+                               scroll = -1;\r
+                               cur--;\r
+                               moved = true;\r
+                       }\r
+               }\r
+               else if (info.y > 0)\r
+               {\r
+                       if (cur + page < lines)\r
+                       {\r
+                               scroll = +1;\r
+                               cur++;\r
+                               moved = true;\r
+                       }\r
+               }\r
+               else if (info.button0 || info.button1)\r
+                       done = true;\r
+               else if (IN_KeyDown(LastScan))\r
+               {\r
+                       switch (LastScan)\r
+                       {\r
+                       case sc_Escape:\r
+                               done = true;\r
+                               break;\r
+                       case sc_UpArrow:\r
+                               if (cur > 0)\r
+                               {\r
+                                       scroll = -1;\r
+                                       cur--;\r
+                                       moved = true;\r
+                               }\r
+                               break;\r
+                       case sc_DownArrow:\r
+                               if (cur + page < lines)\r
+                               {\r
+                                       scroll = +1;\r
+                                       cur++;\r
+                                       moved = true;\r
+                               }\r
+                               break;\r
+                       case sc_PgUp:\r
+                               if (cur > page)\r
+                                       cur -= page;\r
+                               else\r
+                                       cur = 0;\r
+                               moved = true;\r
+                               waitkey = sc_PgUp;\r
+                               break;\r
+                       case sc_PgDn:\r
+                               if (cur + page < lines)\r
+                               {\r
+                                       cur += page;\r
+                                       if (cur + page >= lines)\r
+                                               cur = lines - page;\r
+                                       moved = true;\r
+                               }\r
+                               waitkey = sc_PgDn;\r
+                               break;\r
+                       }\r
+               }\r
+       }\r
+       IN_ClearKeysDown();\r
+       do\r
+       {\r
+               IN_ReadCursor(&info);\r
+       } while (info.button0 || info.button1);\r
+\r
+       VW_ShowCursor();\r
+       US_ClearWindow();\r
+       VW_UpdateScreen();\r
+       US_RestoreWindow(&wr);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_CtlHButtonCustom() - The custom routine for all of the help buttons\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_CtlHButtonCustom(UserCall call,word i,word n)\r
+{\r
+       word            j;\r
+       UserItem        *ip;\r
+\r
+       if (call != uic_Hit)\r
+               return(false);\r
+\r
+       ip = &TheItems[i][n];\r
+       if (ip->sel & ui_Disabled)\r
+               return(false);\r
+\r
+       ip->sel |= ui_Selected;\r
+       USL_DrawItem(i,n);\r
+\r
+       USL_ClearBottom();\r
+\r
+       fontcolor = F_SECONDCOLOR;\r
+       USL_ShowHelp("Loading & Formatting Text...");\r
+       VW_UpdateScreen();\r
+\r
+#ifdef  HELPTEXTLINKED  // Ugly hack because of lack of disk space...\r
+       {\r
+extern  char    far gametext,far context,far story;\r
+               char    far *buf;\r
+               memptr  dupe;\r
+\r
+               switch (n)\r
+               {\r
+               case 0:\r
+                       buf = &gametext;\r
+                       break;\r
+               case 1:\r
+                       buf = &context;\r
+                       break;\r
+               case 2:\r
+                       buf = &story;\r
+                       break;\r
+               }\r
+\r
+               MM_GetPtr(&dupe,5000);\r
+               _fmemcpy((char far *)dupe,buf,5000);\r
+\r
+               USL_DoHelp(dupe,5000);\r
+\r
+               MM_FreePtr(&dupe);\r
+               if (LineOffsets)\r
+                       MM_FreePtr(&LineOffsets);\r
+       }\r
+#else\r
+       {\r
+               char    *name;\r
+               int             file;\r
+               long    len;\r
+               memptr  buf;\r
+\r
+               switch (n)\r
+               {\r
+               case 0:\r
+                       name = "GAMETEXT."EXTENSION;\r
+                       break;\r
+               case 1:\r
+                       name = "CONTEXT."EXTENSION;\r
+                       break;\r
+               case 2:\r
+                       name = "STORY."EXTENSION;\r
+                       break;\r
+               default:\r
+                       Quit("Bad help button number");\r
+               }\r
+\r
+               if ((file = open(name,O_RDONLY | O_TEXT)) == -1)\r
+                       USL_HandleError(errno);\r
+               else\r
+               {\r
+                       len = filelength(file);\r
+                       MM_GetPtr(&buf,len);\r
+\r
+                       if (CA_FarRead(file,(byte far *)buf,len))\r
+                               USL_DoHelp(buf,len);\r
+                       else\r
+                               USL_HandleError(errno);\r
+\r
+                       close(file);\r
+                       MM_FreePtr(&buf);\r
+               }\r
+\r
+               if (LineOffsets)\r
+                       MM_FreePtr(&LineOffsets);\r
+       }\r
+#endif\r
+\r
+       fontcolor = F_BLACK;\r
+\r
+       ip->sel &= ~ui_Selected;\r
+       USL_DrawItem(i,n);\r
+\r
+       return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_CtlDButtonCustom() - The custom routine for all of the disk buttons.\r
+//              Sets up the bottom area of the window with the appropriate buttons\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_CtlDButtonCustom(UserCall call,word i,word n)\r
+{\r
+       word            j;\r
+       UserItem        *ip;\r
+\r
+       if (call != uic_Hit)\r
+               return(false);\r
+\r
+       ip = &TheItems[i][n];\r
+       if (ip->sel & ui_Disabled)\r
+               return(false);\r
+\r
+       USL_ClearBottom();\r
+\r
+       j = 0;\r
+       TheItems[i + 1] = ip = n? CtlDEPanels : CtlDLSPanels;\r
+       while (ip && (ip->type != uii_Bad))\r
+       {\r
+               USL_DrawItem(i + 1,j++);\r
+               ip++;\r
+       }\r
+\r
+       return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_DLSRect() - Draw the rectangle for the save game names\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static Rect\r
+USL_DLSRect(UserItem *ip)\r
+{\r
+       Rect    r;\r
+\r
+       r.ul.x = ip->r.lr.x + 40 + 2;\r
+       r.ul.y = ip->r.ul.y + 2;\r
+       r.lr.x = WindowX + WindowW - 8 - 2;\r
+       r.lr.y = ip->r.lr.y - 2;\r
+\r
+       VWB_Bar(r.ul.x,r.ul.y,r.lr.x - r.ul.x,r.lr.y - r.ul.y,WHITE);\r
+\r
+       VWB_Hlin(r.ul.x,r.lr.x,r.ul.y,BLACK);\r
+       VWB_Hlin(r.ul.x,r.lr.x,r.lr.y,BLACK);\r
+       VWB_Vlin(r.ul.y,r.lr.y,r.ul.x,BLACK);\r
+       VWB_Vlin(r.ul.y,r.lr.y,r.lr.x,BLACK);\r
+\r
+       px = r.ul.x + 2;\r
+       py = r.ul.y + 2;\r
+\r
+       return(r);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_CtlDLButtonCustom() - The load game custom routine\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_CtlDLButtonCustom(UserCall call,word i,word n)\r
+{\r
+       char            *filename,\r
+                               msg[MaxGameName + 12];\r
+       word            err;\r
+       int                     file;\r
+       UserItem        *ip;\r
+       SaveGame        *game;\r
+       WindowRec       wr;\r
+\r
+       // DEBUG - deal with warning user about loading a game causing abort\r
+\r
+       game = &Games[n / 2];\r
+       ip = &TheItems[i][n];\r
+\r
+       switch (call)\r
+       {\r
+       case uic_Draw:\r
+               if (!loadedgame)\r
+               {\r
+                       USL_DLSRect(ip);\r
+                       fontcolor = game->present? F_BLACK : F_FIRSTCOLOR;\r
+                       USL_DrawString(game->present? game->name : "Empty");\r
+                       fontcolor = F_BLACK;\r
+               }\r
+               break;\r
+       case uic_Hit:\r
+               if (ip->sel & ui_Disabled)\r
+                       return(false);\r
+\r
+               LeaveDriveOn++;\r
+               filename = USL_GiveSaveName(n / 2);\r
+\r
+               US_SaveWindow(&wr);\r
+               US_CenterWindow(30,3);\r
+               strcpy(msg,"Loading `");\r
+               strcat(msg,game->name);\r
+               strcat(msg,"\'");\r
+               US_PrintCentered(msg);\r
+               VW_HideCursor();\r
+               VW_UpdateScreen();\r
+\r
+               err = 0;\r
+               if ((file = open(filename,O_BINARY | O_RDONLY)) != -1)\r
+               {\r
+                       if (read(file,game,sizeof(*game)) == sizeof(*game))\r
+                       {\r
+                               if (USL_LoadGame)\r
+                                       if (!USL_LoadGame(file))\r
+                                               USL_HandleError(err = errno);\r
+                       }\r
+                       else\r
+                               USL_HandleError(err = errno);\r
+                       close(file);\r
+               }\r
+               else\r
+                       USL_HandleError(err = errno);\r
+               if (err)\r
+                       abortgame = true;\r
+               else\r
+                       loadedgame = true;\r
+               game->present = true;\r
+\r
+               if (loadedgame)\r
+                       Paused = true;\r
+\r
+               VW_ShowCursor();\r
+               US_RestoreWindow(&wr);\r
+\r
+               LeaveDriveOn--;\r
+               break;\r
+       }\r
+       return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_CtlDSButtonCustom() - The save game custom routine\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_CtlDSButtonCustom(UserCall call,word i,word n)\r
+{\r
+       boolean         ok;\r
+       char            *filename;\r
+       word            err;\r
+       int                     file;\r
+       Rect            r;\r
+       UserItem        *ip;\r
+       SaveGame        *game;\r
+       WindowRec       wr;\r
+\r
+       if (call != uic_Hit)\r
+               return(false);\r
+\r
+       game = &Games[n / 2];\r
+       ip = &TheItems[i][n];\r
+       if (ip->sel & ui_Disabled)\r
+               return(false);\r
+\r
+       FlushHelp = true;\r
+       fontcolor = F_SECONDCOLOR;\r
+       USL_ShowHelp("Enter Game Name / Escape Aborts");\r
+       fontcolor = F_BLACK;\r
+\r
+       r = USL_DLSRect(ip - 1);\r
+       ok = US_LineInput(px,py,game->name,game->present? game->name : nil,true,\r
+                                               MaxGameName,r.lr.x - r.ul.x - 8);\r
+       if (!strlen(game->name))\r
+               strcpy(game->name,"Untitled");\r
+       if (ok)\r
+       {\r
+               US_SaveWindow(&wr);\r
+               US_CenterWindow(10,3);\r
+               US_PrintCentered("Saving");\r
+               VW_HideCursor();\r
+               VW_UpdateScreen();\r
+\r
+               LeaveDriveOn++;\r
+               filename = USL_GiveSaveName(n / 2);\r
+               err = 0;\r
+               file = open(filename,O_CREAT | O_BINARY | O_WRONLY,\r
+                                       S_IREAD | S_IWRITE | S_IFREG);\r
+               if (file != -1)\r
+               {\r
+                       if (write(file,game,sizeof(*game)) == sizeof(*game))\r
+                       {\r
+                               if (USL_SaveGame)\r
+                                       ok = USL_SaveGame(file);\r
+                               if (!ok)\r
+                                       USL_HandleError(err = errno);\r
+                       }\r
+                       else\r
+                               USL_HandleError(err = ((errno == ENOENT)? ENOMEM : errno));\r
+                       close(file);\r
+               }\r
+               else\r
+                       USL_HandleError(err = ((errno == ENOENT)? ENOMEM : errno));\r
+               if (err)\r
+               {\r
+                       remove(filename);\r
+                       ok = false;\r
+               }\r
+               LeaveDriveOn--;\r
+\r
+               VW_ShowCursor();\r
+               US_RestoreWindow(&wr);\r
+               USL_DoHit(i - 1,0);\r
+               VW_UpdateScreen();\r
+       }\r
+\r
+       if (!game->present)\r
+               game->present = ok;\r
+\r
+       if (ok)\r
+       {\r
+               GameIsDirty = false;\r
+               (ip - 1)->sel &= ~ui_Disabled;\r
+       }\r
+\r
+       USL_DrawItem(i,n - 1);\r
+//      USL_CtlDLButtonCustom(uic_Draw,i,n - 1);\r
+\r
+       return(true);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_CtlSButtonCustom() - The custom routine for all of the sound buttons\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_CtlSButtonCustom(UserCall call,word i,word n)\r
+{\r
+       word            j;\r
+       UserItem        *ip;\r
+\r
+       if (call != uic_Hit)\r
+               return(false);\r
+\r
+       ip = &TheItems[i][n];\r
+       if (ip->sel & ui_Disabled)\r
+               return(false);\r
+\r
+       USL_ClearBottom();\r
+\r
+       if (n == sdm_SoundSource)\r
+       {\r
+               j = 0;\r
+               TheItems[i + 1] = ip = CtlSSSPanels;\r
+               while (ip && (ip->type != uii_Bad))\r
+               {\r
+                       USL_DrawItem(i + 1,j++);\r
+                       ip++;\r
+               }\r
+       }\r
+       else\r
+               TheItems[i + 1] = nil;\r
+\r
+       return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_CtlPButtonCustom() - The custom routine for all of the start game btns\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_CtlPButtonCustom(UserCall call,word i,word n)\r
+{\r
+       word            j;\r
+       UserItem        *ip;\r
+\r
+       if (call != uic_Hit)\r
+               return(false);\r
+\r
+       ip = &TheItems[i][n];\r
+       if (ip->sel & ui_Disabled)\r
+               return(false);\r
+\r
+       USL_ClearBottom();\r
+\r
+       j = 0;\r
+       TheItems[i + 1] = ip = n? CtlPRPanels : CtlPSPanels;\r
+       while (ip && (ip->type != uii_Bad))\r
+       {\r
+               USL_DrawItem(i + 1,j++);\r
+               ip++;\r
+       }\r
+\r
+       return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_GiveAbortWarning() - Draws a string that warns the user that an\r
+//              action they're about to take will abort the game in progress\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_GiveAbortWarning(void)\r
+{\r
+       WindowRec       wr;\r
+\r
+       if (!GameIsDirty)\r
+               return;\r
+\r
+       US_SaveWindow(&wr);\r
+       US_RestoreWindow(&BottomWindow);\r
+       US_HomeWindow();\r
+       PrintY += 5;\r
+\r
+       VWB_Bar(WindowX,WindowY,WindowW,30,WHITE);\r
+       fontcolor = F_SECONDCOLOR;\r
+       US_CPrint("Warning! If you do this, you'll");\r
+       US_CPrint("abort the current game.");\r
+       fontcolor = F_BLACK;\r
+\r
+       US_RestoreWindow(&wr);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_CtlPSButtonCustom() - The custom routine for the start game button\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_CtlPSButtonCustom(UserCall call,word i,word n)\r
+{\r
+       boolean         result;\r
+       UserItem        *ip;\r
+\r
+       i++;    // Shut the compiler up\r
+\r
+       switch (call)\r
+       {\r
+       case uic_Hit:\r
+               switch (n)\r
+               {\r
+               case 0:\r
+                       restartgame = gd_Normal;\r
+                       break;\r
+               case 1:\r
+                       restartgame = gd_Easy;\r
+                       break;\r
+               case 2:\r
+                       restartgame = gd_Hard;\r
+                       break;\r
+               }\r
+               if (restartgame && ingame && USL_ResetGame)\r
+                       USL_ResetGame();\r
+               result = false;\r
+               break;\r
+       case uic_Draw:\r
+               USL_GiveAbortWarning();\r
+               result = false;\r
+               break;\r
+       default:\r
+               result = false;\r
+               break;\r
+       }\r
+       return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_CtlPRButtonCustom() - The custom routine for the resume game button\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_CtlPRButtonCustom(UserCall call,word i,word n)\r
+{\r
+       if (call != uic_Hit)\r
+               return(false);\r
+\r
+       i++,n++;        // Shut the compiler up\r
+       ResumeGame = true;\r
+       return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_CtlDEButtonCustom() - The custom routine for the exit to DOS button\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_CtlDEButtonCustom(UserCall call,word i,word n)\r
+{\r
+       boolean         result;\r
+       UserItem        *ip;\r
+\r
+       i++,n++;        // Shut the compiler up\r
+\r
+       switch (call)\r
+       {\r
+       case uic_Hit:\r
+               QuitToDos = true;\r
+               break;\r
+       case uic_Draw:\r
+               USL_GiveAbortWarning();\r
+       default:\r
+               result = false;\r
+               break;\r
+       }\r
+       return(result);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_CtlCButtonCustom() - The custom routine for all of the control\r
+//              buttons\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_CtlCButtonCustom(UserCall call,word i,word n)\r
+{\r
+       word            j;\r
+       Point           p;\r
+       UserItem        *ip;\r
+\r
+       if (call != uic_Hit)\r
+               return(false);\r
+\r
+       ip = &TheItems[i][n];\r
+       if (ip->sel & ui_Disabled)\r
+               return(false);\r
+\r
+       USL_ClearBottom();\r
+       if (n == 0)     // Keyboard\r
+       {\r
+               TheItems[i + 1] = ip = CtlCKbdPanels;\r
+               p = CtlCKbdPanels[2].r.lr;\r
+               VWB_DrawPic(p.x,p.y,CTL_DIRSPIC);\r
+       }\r
+       else\r
+               TheItems[i + 1] = ip = CtlCJoyPanels;\r
+\r
+       j = 0;\r
+       while (ip && (ip->type != uii_Bad))\r
+       {\r
+               USL_DrawItem(i + 1,j++);\r
+               ip++;\r
+       }\r
+\r
+       return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_HitHotKey() - After a hotkey was hit, move the cursor to the first\r
+//              selected item in the group after the group containing the item\r
+//              holding the hotkey\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_HitHotKey(int i,int n)\r
+{\r
+       UserItem        *ip;\r
+\r
+       if (ip = TheItems[++i])\r
+       {\r
+               if ((n = USL_FindDown(TheItems[i])) == -1)\r
+                       n = 0;\r
+               ip += n;\r
+               CursorX = ip->r.lr.x - 8;\r
+               CursorY = ip->r.lr.y - 8;\r
+               CursorBad = true;\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_CheckScan() - Checks to see if the scancode in LastScan corresponds\r
+//              to anything in the list of useritems. If so, selects the item.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static boolean\r
+USL_CheckScan(word *ci,word *cn)\r
+{\r
+       word            i,n;\r
+       UserItem        *ip;\r
+\r
+       if (!LastScan)\r
+               return(false);\r
+\r
+#if 1   // DEBUG - probably kill this code\r
+       // Use 1..? for the items across the top row\r
+       if (TheItems[1] && !IN_KeyDown(sc_RShift))\r
+       {\r
+               for (i = 0,ip = TheItems[1];(ip->type != uii_Bad) && (i < 9);i++,ip++)\r
+                       ;\r
+               for (n = 0;n < i;n++)\r
+               {\r
+                       if (LastScan == 2 + n)  // Numbers from 1..9\r
+                       {\r
+                               if (!(TheItems[1][n].sel & ui_Disabled))\r
+                               {\r
+                                       LastScan = sc_None;\r
+                                       USL_DoHit(1,n);\r
+                                       return(true);\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       // Use Alt-1..6 for the items in the leftmost column\r
+       if (IN_KeyDown(sc_RShift))\r
+       {\r
+               n = LastScan - 2;\r
+               if (n < 6)      // Numbers from 1..6\r
+               {\r
+                       USL_DoHit(0,n);\r
+                       LastScan = sc_None;\r
+                       return(true);\r
+               }\r
+       }\r
+#endif\r
+\r
+       // Check normal hotkeys for the leftmost column\r
+       for (i = 0;CtlPanels[i].type != uii_Bad;i++)\r
+       {\r
+               if (CtlPanels[i].key == LastScan)\r
+               {\r
+                       LastScan = sc_None;\r
+                       USL_DoHit(0,i);\r
+                       *ci = 0;\r
+                       *cn = i;\r
+                       USL_HitHotKey(0,i);\r
+                       return(true);\r
+               }\r
+       }\r
+\r
+       // Check normal hotkeys for the top row\r
+       for (i = 0;i < 6;i++)\r
+       {\r
+               for (n = 0,ip = CtlPanels2[i];ip && ip->type != uii_Bad;n++,ip++)\r
+               {\r
+                       if ((ip->key == LastScan) && !(ip->sel & ui_Disabled))\r
+                       {\r
+                               LastScan = sc_None;\r
+                               USL_DoHit(0,i);\r
+                               USL_DoHit(1,n);\r
+                               *ci = 1;\r
+                               *cn = n;\r
+                               USL_HitHotKey(1,n);\r
+                               return(true);\r
+                       }\r
+               }\r
+       }\r
+       return(false);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_SetUpCtlPanel() - Sets the states of the UserItems to reflect the\r
+//              values of all the appropriate variables\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_SetUpCtlPanel(void)\r
+{\r
+       word    i,j;\r
+\r
+       GameIsDirty = ingame;\r
+\r
+       // Set up restart game\r
+       USL_TurnOff(CtlPPanels);\r
+       CtlPPanels[0].sel = ingame? ui_Normal : ui_Selected;\r
+       CtlPPanels[1].sel = ingame? ui_Selected : ui_Disabled;\r
+\r
+       // Set up disk stuff - default to load/save game\r
+       USL_TurnOff(CtlDPanels);\r
+       CtlDPanels[0].sel = ui_Selected;\r
+\r
+       // Set up load/save buttons\r
+       USL_TurnOff(CtlDLSPanels);\r
+       for (i = 0;i < MaxSaveGames;i++)\r
+       {\r
+               if (!Games[i].present)\r
+                       CtlDLSPanels[i * 2].sel = ui_Disabled;\r
+               if (!ingame)\r
+                       CtlDLSPanels[(i * 2) + 1].sel = ui_Disabled;\r
+       }\r
+\r
+       // Set up Controls\r
+       USL_TurnOff(CtlCPanels);\r
+       CtlCPanels[1].sel = JoysPresent[0]? ui_Normal : ui_Disabled;\r
+       CtlCPanels[2].sel = JoysPresent[1]? ui_Normal : ui_Disabled;\r
+       if (Controls[0] == ctrl_Keyboard)\r
+               i = 0;\r
+       else\r
+               i = (Controls[0] == ctrl_Joystick1)? 1 : 2;\r
+       CtlCPanels[i].sel |= ui_Selected;\r
+       if (JoysPresent[1] && !JoysPresent[0])\r
+               CtlCPanels[2].key = sc_F4;\r
+       else\r
+               CtlCPanels[1].key = sc_F4;\r
+\r
+       // Set up Keyboard\r
+       for (i = 0;i < 10;i++)\r
+               CtlCKbdPanels[i].text = IN_GetScanName(*(KeyMaps[i]));\r
+\r
+       // Set up Sounds\r
+       USL_TurnOff(CtlSPanels);\r
+       CtlSPanels[sdm_AdLib].sel = AdLibPresent? ui_Normal : ui_Disabled;\r
+#if 0   // DEBUG - hack because no space for digitized sounds on Keen Dreams\r
+       CtlSPanels[sdm_SoundBlaster].sel =\r
+               SoundBlasterPresent? ui_Normal : ui_Disabled;\r
+       CtlSPanels[sdm_SoundSource].sel =\r
+               SoundSourcePresent? ui_Normal : ui_Disabled;\r
+#else\r
+       CtlSPanels[sdm_SoundBlaster].sel = ui_Disabled;\r
+       CtlSPanels[sdm_SoundSource].sel = ui_Disabled;\r
+#endif\r
+       CtlSPanels[SoundMode].sel |= ui_Selected;\r
+\r
+       // Set up SoundSource\r
+       USL_TurnOff(CtlSSSPanels);\r
+       CtlSSSPanels[0].sel = ssIsTandy? ui_Selected : ui_Normal;\r
+       CtlSSSPanels[1].sel = (ssPort == 2)? ui_Selected : ui_Normal;\r
+\r
+       // Set up Music\r
+       USL_TurnOff(CtlMPanels);\r
+       CtlMPanels[smm_AdLib].sel = AdLibPresent? ui_Normal : ui_Disabled;\r
+       CtlMPanels[MusicMode].sel |= ui_Selected;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_TearDownCtlPanel() - Given the state of the control panel, sets the\r
+//              modes and values as appropriate\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_TearDownCtlPanel(void)\r
+{\r
+       int     i;\r
+\r
+       i = USL_FindDown(CtlCPanels);\r
+       if (i != -1)\r
+       {\r
+               i = i? (i == 1? ctrl_Joystick1 : ctrl_Joystick2) : ctrl_Keyboard;\r
+               IN_SetControlType(0,i);\r
+       }\r
+\r
+       CtlCPanels[1].key = CtlCPanels[2].key = sc_None;\r
+\r
+       i = USL_FindDown(CtlSPanels);\r
+       if (i != -1)\r
+               SD_SetSoundMode(i);\r
+\r
+       ssIsTandy = CtlSSSPanels[0].sel & ui_Selected;\r
+       ssPort = (CtlSSSPanels[1].sel & ui_Selected)? 2 : 1;\r
+\r
+       i = USL_FindDown(CtlMPanels);\r
+       if (i != -1)\r
+       {\r
+               SD_SetMusicMode(i);\r
+\r
+               if (!QuitToDos)\r
+               {\r
+                       US_CenterWindow(20,8);\r
+                       US_CPrint("Loading");\r
+#if 0\r
+                       fontcolor = F_SECONDCOLOR;\r
+                       US_CPrint("Sounds");\r
+                       fontcolor = F_BLACK;\r
+#endif\r
+                       VW_UpdateScreen();\r
+\r
+                       CA_LoadAllSounds();\r
+               }\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_ControlPanel() - This is the main routine for the control panel\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_ControlPanel(void)\r
+{\r
+       char            gamename[MaxGameName + 10 + 1];\r
+       ScanCode        c;\r
+       boolean         done,\r
+                               buttondown,inrect;\r
+       word            hiti,hitn,\r
+                               i,n,\r
+                               lasti,lastn,\r
+                               lastx,lasty;\r
+       longword        lasttime;\r
+       Point           p;\r
+       Rect            userect;\r
+       UserItem        *ip;\r
+\r
+       c = LastScan;\r
+       if (c == sc_Escape)     // Map escape from game to Exit to DOS\r
+               c = sc_Q;\r
+\r
+       CA_UpLevel();\r
+       for (i = CONTROLS_LUMP_START;i <= CONTROLS_LUMP_END;i++)\r
+               CA_MarkGrChunk(i);\r
+       CA_MarkGrChunk(CTL_LITTLEMASKPICM);\r
+       CA_MarkGrChunk(CTL_LSMASKPICM);\r
+       CA_CacheMarks("Options Screen");\r
+\r
+       USL_SetUpCtlPanel();\r
+\r
+       US_SetPrintRoutines(VW_MeasurePropString,VWB_DrawPropString);\r
+       fontcolor = F_BLACK;\r
+\r
+       VW_InitDoubleBuffer();\r
+\r
+       VWB_Bar(0,0,MaxX,MaxY,FIRSTCOLOR);\r
+       US_DrawWindow(8,22,30,2);\r
+       US_SaveWindow(&HelpWindow);\r
+       US_DrawWindow(8,7,30,14);\r
+       US_SaveWindow(&BottomWindow);\r
+       US_DrawWindow(8,1,30,20);\r
+\r
+       for (ip = CtlPanels;ip->type != uii_Bad;ip++)\r
+               VWB_DrawPic(ip->r.ul.x,ip->r.ul.y,ip->picup);\r
+\r
+       US_StartCursor();\r
+       CursorX = (8 * 8) + ((MaxX - (8 * 8)) / 2);\r
+       CursorBad = true;\r
+\r
+       CtlPanelButton = -1;\r
+       LastScan = c;\r
+       USL_CheckScan(&i,&n);\r
+       if (CtlPanelButton == -1)\r
+               USL_DoHit(0,0);\r
+\r
+       ResumeGame = false;\r
+       done = false;\r
+       FlushHelp = true;\r
+       lastx = lasty = -1;\r
+       while\r
+       (\r
+               (restartgame == gd_Continue)\r
+       &&      !(done || loadedgame || ResumeGame)\r
+       )\r
+       {\r
+               VW_UpdateScreen();\r
+\r
+               buttondown = US_UpdateCursor();\r
+               inrect = USL_IsInRect(CursorX,CursorY,&i,&n);\r
+\r
+               if (FlushHelp)\r
+               {\r
+                       lasti = -2;\r
+                       lasttime = TimeCount;\r
+                       FlushHelp = false;\r
+               }\r
+               if (inrect)\r
+               {\r
+                       if ((lasti != i) || (lastn != n))\r
+                       {\r
+                               // If over a Load button\r
+                               if\r
+                               (\r
+                                       (CtlPanelButton == 2)\r
+                               &&      (i == 2)\r
+                               &&      (TheItems[1][0].sel & ui_Selected)\r
+                               &&      (Games[n / 2].present)\r
+                               &&      !(n & 1)\r
+                               )\r
+                               {\r
+                                       strcpy(gamename,"Load `");\r
+                                       strcat(gamename,Games[n / 2].name);\r
+                                       strcat(gamename,"'");\r
+                                       USL_ShowHelp(gamename);\r
+                               }\r
+                               else\r
+                                       USL_ShowHelp(TheItems[i][n].help);\r
+                               lasti = i;\r
+                               lastn = n;\r
+                       }\r
+               }\r
+               else if (lasti != (word)-1)\r
+               {\r
+                       USL_ShowHelp("Select a Button");\r
+                       lasti = -1;\r
+               }\r
+\r
+               hiti = i;\r
+               hitn = n;\r
+\r
+               if (inrect)\r
+                       userect = TheItems[i][n].r;\r
+               else\r
+               {\r
+                       userect.ul.x = CursorX;\r
+                       userect.ul.y = CursorY;\r
+                       userect.lr = userect.ul;\r
+               }\r
+\r
+               if (IN_KeyDown(sc_UpArrow))\r
+               {\r
+                       USL_FindRect(userect,motion_None,motion_Up);\r
+                       buttondown = false;\r
+                       IN_ClearKey(sc_UpArrow);\r
+               }\r
+               else if (IN_KeyDown(sc_DownArrow))\r
+               {\r
+                       USL_FindRect(userect,motion_None,motion_Down);\r
+                       buttondown = false;\r
+                       IN_ClearKey(sc_DownArrow);\r
+               }\r
+               else if (IN_KeyDown(sc_LeftArrow))\r
+               {\r
+                       USL_FindRect(userect,motion_Left,motion_None);\r
+                       buttondown = false;\r
+                       IN_ClearKey(sc_LeftArrow);\r
+               }\r
+               else if (IN_KeyDown(sc_RightArrow))\r
+               {\r
+                       USL_FindRect(userect,motion_Right,motion_None);\r
+                       buttondown = false;\r
+                       IN_ClearKey(sc_RightArrow);\r
+               }\r
+               else if\r
+               (\r
+                       IN_KeyDown(c = sc_Return)\r
+               ||      IN_KeyDown(c = KbdDefs[0].button0)\r
+               ||      IN_KeyDown(c = KbdDefs[0].button1)\r
+               )\r
+               {\r
+                       IN_ClearKey(c);\r
+                       if (inrect)\r
+                       {\r
+                               ip = &TheItems[hiti][hitn];\r
+\r
+                               if ((ip->type == uii_Button) && !(ip->sel & ui_Disabled))\r
+                               {\r
+                                       lasttime = TimeCount;\r
+\r
+                                       ip->sel |= ui_Selected;\r
+                                       USL_DrawItem(hiti,hitn);\r
+                                       VW_UpdateScreen();\r
+\r
+                                       while (TimeCount - lasttime < TickBase / 4)\r
+                                               ;\r
+                                       lasttime = TimeCount;\r
+\r
+                                       ip->sel &= ~ui_Selected;\r
+                                       USL_DrawItem(hiti,hitn);\r
+                                       VW_UpdateScreen();\r
+\r
+                                       while (TimeCount - lasttime < TickBase / 4)\r
+                                               ;\r
+                               }\r
+\r
+                               USL_DoHit(hiti,hitn);\r
+                       }\r
+               }\r
+               else if (USL_CheckScan(&i,&n))\r
+                       ;\r
+               else if (buttondown && inrect && USL_TrackItem(hiti,hitn))\r
+                       USL_DoHit(hiti,hitn);\r
+\r
+               if (LastScan == sc_Escape)\r
+               {\r
+                       IN_ClearKey(sc_Escape);\r
+                       done = true;\r
+               }\r
+\r
+               if (QuitToDos)\r
+                       done = true;\r
+\r
+               if ((lastx != CursorX) || (lasty != CursorY))\r
+               {\r
+                       lastx = CursorX;\r
+                       lasty = CursorY;\r
+                       lasttime = TimeCount;\r
+               }\r
+               if (TimeCount - lasttime > TickBase * 10)\r
+               {\r
+                       if (((TimeCount - lasttime) / TickBase) & 2)\r
+                               fontcolor = F_SECONDCOLOR;\r
+                       USL_ShowHelp("Press F1 for Help");\r
+                       fontcolor = F_BLACK;\r
+               }\r
+       }\r
+\r
+       US_ShutCursor();\r
+\r
+       USL_TearDownCtlPanel();\r
+\r
+       if (restartgame && USL_ResetGame)\r
+               USL_ResetGame();\r
+\r
+       if (QuitToDos)\r
+       {\r
+               if (tedlevel)\r
+                       TEDDeath();\r
+               else\r
+               {\r
+                       US_CenterWindow(20,3);\r
+                       fontcolor = F_SECONDCOLOR;\r
+                       US_PrintCentered("Now Exiting to DOS...");\r
+                       fontcolor = F_BLACK;\r
+                       VW_UpdateScreen();\r
+                       Quit(nil);\r
+               }\r
+       }\r
+\r
+       CA_DownLevel();\r
+}\r
+\r
+//      High score routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_DisplayHighScores() - Assumes that double buffering has been started.\r
+//              If passed a -1 will just display the high scores, but if passed\r
+//              a non-negative number will display that entry in red and let the\r
+//              user type in a name\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_DisplayHighScores(int which)\r
+{\r
+       char            buffer[16],*str;\r
+       word            i,\r
+                               w,h,\r
+                               x,y;\r
+       HighScore       *s;\r
+\r
+       US_CenterWindow(30,MaxScores + (MaxScores / 2));\r
+\r
+       x = WindowX + (WindowW / 2);\r
+       US_Print(" Name");\r
+       PrintX = x + 20;\r
+       US_Print("Score");\r
+       PrintX = x + 60;\r
+       US_Print("Done\n\n");\r
+       PrintY -= 3;\r
+\r
+       for (i = WindowX;i < WindowX + WindowW;i += 8)\r
+               VWB_DrawTile8M(i,WindowY + 8,10);\r
+       VWB_DrawTile8M(WindowX - 8,WindowY + 8,9);\r
+       VWB_DrawTile8M(WindowX + WindowW,WindowY + 8,11);\r
+\r
+       for (i = 0,s = Scores;i < MaxScores;i++,s++)\r
+       {\r
+               fontcolor = (i == which)? F_SECONDCOLOR : F_BLACK;\r
+\r
+               if (i != which)\r
+               {\r
+                       US_Print(" ");\r
+                       if (strlen(s->name))\r
+                               US_Print(s->name);\r
+                       else\r
+                               US_Print("-");\r
+               }\r
+               else\r
+                       y = PrintY;\r
+\r
+               PrintX = x + (7 * 8);\r
+               ultoa(s->score,buffer,10);\r
+               for (str = buffer;*str;str++)\r
+                       *str = *str + (129 - '0');      // Used fixed-width numbers (129...)\r
+               USL_MeasureString(buffer,&w,&h);\r
+               PrintX -= w;\r
+               US_Print(buffer);\r
+\r
+               PrintX = x + 60;\r
+               if (s->completed)\r
+                       US_PrintUnsigned(s->completed);\r
+               else\r
+                       US_Print("-");\r
+\r
+               US_Print("\n");\r
+       }\r
+\r
+       if (which != -1)\r
+       {\r
+               fontcolor = F_SECONDCOLOR;\r
+               PrintY = y;\r
+               PrintX = WindowX;\r
+               US_Print(" ");\r
+               strcpy(Scores[which].name,"");\r
+               US_LineInput(PrintX,PrintY,Scores[which].name,nil,true,MaxHighName,\r
+                                               (WindowW / 2) - 8);\r
+       }\r
+       fontcolor = F_BLACK;\r
+\r
+       VW_UpdateScreen();\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_CheckHighScore() - Checks gamestate to see if the just-ended game\r
+//              should be entered in the high score list. If so, lets the user\r
+//              enter their name\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_CheckHighScore(long score,word other)\r
+{\r
+       word            i,j,\r
+                               n;\r
+       HighScore       myscore;\r
+\r
+       strcpy(myscore.name,"");\r
+       myscore.score = score;\r
+       myscore.completed = other;\r
+\r
+       for (i = 0,n = -1;i < MaxScores;i++)\r
+       {\r
+               if\r
+               (\r
+                       (myscore.score > Scores[i].score)\r
+               ||      (\r
+                               (myscore.score == Scores[i].score)\r
+                       &&      (myscore.completed > Scores[i].completed)\r
+                       )\r
+               )\r
+               {\r
+                       for (j = MaxScores;--j > i;)\r
+                               Scores[j] = Scores[j - 1];\r
+                       Scores[i] = myscore;\r
+\r
+                       n = i;\r
+                       HighScoresDirty = true;\r
+                       break;\r
+               }\r
+       }\r
+\r
+       VW_InitDoubleBuffer();\r
+       VWB_Bar(0,0,MaxX,MaxY,FIRSTCOLOR);\r
+\r
+       US_DisplayHighScores(n);\r
+       IN_UserInput(5 * TickBase,false);\r
+}\r
diff --git a/16/cawat/ID_US.H b/16/cawat/ID_US.H
new file mode 100644 (file)
index 0000000..7acc243
--- /dev/null
@@ -0,0 +1,146 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+//     ID Engine\r
+//     ID_US.h - Header file for the User Manager\r
+//     v1.0d1\r
+//     By Jason Blochowiak\r
+//\r
+\r
+#ifndef        __TYPES__\r
+#include "ID_Types.h"\r
+#endif\r
+\r
+#ifndef        __ID_US__\r
+#define        __ID_US__\r
+\r
+#ifdef __DEBUG__\r
+#define        __DEBUG_UserMgr__\r
+#endif\r
+\r
+//#define      HELPTEXTLINKED\r
+\r
+#define        MaxHelpLines    500\r
+\r
+#define        MaxHighName     57\r
+#define        MaxScores       7\r
+typedef        struct\r
+               {\r
+                       char    name[MaxHighName + 1];\r
+                       long    score;\r
+                       word    completed;\r
+               } HighScore;\r
+\r
+#define        MaxGameName             32\r
+#define        MaxSaveGames    6\r
+typedef        struct\r
+               {\r
+                       char    signature[4];\r
+                       word    *oldtest;\r
+                       boolean present;\r
+                       char    name[MaxGameName + 1];\r
+               } SaveGame;\r
+\r
+#define        MaxString       128     // Maximum input string size\r
+\r
+typedef        struct\r
+               {\r
+                       int     x,y,\r
+                               w,h,\r
+                               px,py;\r
+               } WindowRec;    // Record used to save & restore screen windows\r
+\r
+typedef        enum\r
+               {\r
+                       gd_Continue,\r
+                       gd_Easy,\r
+                       gd_Normal,\r
+                       gd_Hard\r
+               } GameDiff;\r
+\r
+//     Hack import for TED launch support\r
+extern boolean         tedlevel;\r
+extern word            tedlevelnum;\r
+extern void            TEDDeath(void);\r
+\r
+extern word MaxX,MaxY;         // MDM (GAMERS EDGE)\r
+\r
+extern boolean         ingame,         // Set by game code if a game is in progress\r
+                                       abortgame,      // Set if a game load failed\r
+                                       loadedgame,     // Set if the current game was loaded\r
+                                       NoWait,\r
+                                       HighScoresDirty;\r
+extern char            *abortprogram;  // Set to error msg if program is dying\r
+extern GameDiff        restartgame;    // Normally gd_Continue, else starts game\r
+extern word            PrintX,PrintY;  // Current printing location in the window\r
+extern word            WindowX,WindowY,// Current location of window\r
+                                       WindowW,WindowH;// Current size of window\r
+\r
+extern boolean         Button0,Button1,\r
+                                       CursorBad;\r
+extern int                     CursorX,CursorY;\r
+\r
+extern void            (*USL_MeasureString)(char far *,word *,word *),\r
+                                       (*USL_DrawString)(char far *);\r
+\r
+extern boolean         (*USL_SaveGame)(int),(*USL_LoadGame)(int);\r
+extern void            (*USL_ResetGame)(void);\r
+extern SaveGame        Games[MaxSaveGames];\r
+extern HighScore       Scores[];\r
+\r
+#define        US_HomeWindow() {PrintX = WindowX; PrintY = WindowY;}\r
+\r
+extern void    US_Startup(void),\r
+                               US_Setup(void),\r
+                               US_Shutdown(void),\r
+                               US_InitRndT(boolean randomize),\r
+                               US_SetLoadSaveHooks(boolean (*load)(int),\r
+                                                                       boolean (*save)(int),\r
+                                                                       void (*reset)(void)),\r
+                               US_TextScreen(void),\r
+                               US_UpdateTextScreen(void),\r
+                               US_FinishTextScreen(void),\r
+                               US_ControlPanel(void),\r
+                               US_DrawWindow(word x,word y,word w,word h),\r
+                               US_CenterWindow(word,word),\r
+                               US_SaveWindow(WindowRec *win),\r
+                               US_RestoreWindow(WindowRec *win),\r
+                               US_ClearWindow(void),\r
+                               US_SetPrintRoutines(void (*measure)(char far *,word *,word *),\r
+                                                                       void (*print)(char far *)),\r
+                               US_PrintCentered(char *s),\r
+                               US_CPrint(char *s),\r
+                               US_CPrintLine(char *s),\r
+                               US_Print(char *s),\r
+                               US_PrintUnsigned(longword n),\r
+                               US_PrintSigned(long n),\r
+                               US_StartCursor(void),\r
+                               US_ShutCursor(void),\r
+                               US_ControlPanel(void),\r
+                               US_CheckHighScore(long score,word other),\r
+                               US_DisplayHighScores(int which);\r
+extern boolean US_UpdateCursor(void),\r
+                               US_LineInput(int x,int y,char *buf,char *def,boolean escok,\r
+                                                               int maxchars,int maxwidth);\r
+extern int             US_CheckParm(char *parm,char **strings),\r
+                               US_RndT(void);\r
+\r
+               void    USL_PrintInCenter(char *s,Rect r);\r
+               char    *USL_GiveSaveName(word game);\r
+#endif\r
diff --git a/16/cawat/ID_US_1.C b/16/cawat/ID_US_1.C
new file mode 100644 (file)
index 0000000..789229c
--- /dev/null
@@ -0,0 +1,1317 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+//      ID Engine\r
+//      ID_US_1.c - User Manager - General routines\r
+//      v1.1d1\r
+//      By Jason Blochowiak\r
+//      Hacked up for Catacomb 3D\r
+//\r
+\r
+//\r
+//      This module handles dealing with user input & feedback\r
+//\r
+//      Depends on: Input Mgr, View Mgr, some variables from the Sound, Caching,\r
+//              and Refresh Mgrs, Memory Mgr for background save/restore\r
+//\r
+//      Globals:\r
+//              ingame - Flag set by game indicating if a game is in progress\r
+//      abortgame - Flag set if the current game should be aborted (if a load\r
+//                      game fails)\r
+//              loadedgame - Flag set if a game was loaded\r
+//              abortprogram - Normally nil, this points to a terminal error message\r
+//                      if the program needs to abort\r
+//              restartgame - Normally set to gd_Continue, this is set to one of the\r
+//                      difficulty levels if a new game should be started\r
+//              PrintX, PrintY - Where the User Mgr will print (global coords)\r
+//              WindowX,WindowY,WindowW,WindowH - The dimensions of the current\r
+//                      window\r
+//\r
+\r
+#include "ID_HEADS.H"\r
+\r
+#pragma hdrstop\r
+\r
+#pragma warn    -pia\r
+\r
+\r
+//      Special imports\r
+extern  boolean         showscorebox;\r
+#ifdef  KEEN\r
+extern  boolean         oldshooting;\r
+extern  ScanCode        firescan;\r
+#else\r
+               ScanCode        firescan;\r
+#endif\r
+\r
+//      Global variables\r
+               char            *abortprogram;\r
+               boolean         NoWait,\r
+                                       HighScoresDirty;\r
+               word            PrintX,PrintY;\r
+               word            WindowX,WindowY,WindowW,WindowH;\r
+\r
+               word    MaxX=320,MaxY=200;      // MDM (GAMERS EDGE)\r
+\r
+//      Internal variables\r
+#define ConfigVersion   1\r
+\r
+static  char            *ParmStrings[] = {"TEDLEVEL","NOWAIT"},\r
+                                       *ParmStrings2[] = {"COMP","NOCOMP"};\r
+static  boolean         US_Started;\r
+\r
+               boolean         Button0,Button1,\r
+                                       CursorBad;\r
+               int                     CursorX,CursorY;\r
+\r
+               void            (*USL_MeasureString)(char far *,word *,word *) = VW_MeasurePropString,\r
+                                       (*USL_DrawString)(char far *) = VWB_DrawPropString;\r
+\r
+               boolean         (*USL_SaveGame)(int),(*USL_LoadGame)(int);\r
+               void            (*USL_ResetGame)(void);\r
+               SaveGame        Games[MaxSaveGames];\r
+               HighScore       Scores[MaxScores] =\r
+                                       {\r
+                                               {"Sir Lancelot",500,3},\r
+                                               {"",0},\r
+                                               {"",0},\r
+                                               {"",0},\r
+                                               {"",0},\r
+                                               {"",0},\r
+                                               {"",0},\r
+                                       };\r
+\r
+//      Internal routines\r
+\r
+//      Public routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_HardError() - Handles the Abort/Retry/Fail sort of errors passed\r
+//                      from DOS.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+#pragma warn    -par\r
+#pragma warn    -rch\r
+int\r
+USL_HardError(word errval,int ax,int bp,int si)\r
+{\r
+#define IGNORE  0\r
+#define RETRY   1\r
+#define ABORT   2\r
+extern  void    ShutdownId(void);\r
+\r
+static  char            buf[32];\r
+static  WindowRec       wr;\r
+               int                     di;\r
+               char            c,*s,*t;\r
+\r
+\r
+       di = _DI;\r
+\r
+       if (ax < 0)\r
+               s = "Device Error";\r
+       else\r
+       {\r
+               if ((di & 0x00ff) == 0)\r
+                       s = "Drive ~ is Write Protected";\r
+               else\r
+                       s = "Error on Drive ~";\r
+               for (t = buf;*s;s++,t++)        // Can't use sprintf()\r
+                       if ((*t = *s) == '~')\r
+                               *t = (ax & 0x00ff) + 'A';\r
+               *t = '\0';\r
+               s = buf;\r
+       }\r
+\r
+       c = peekb(0x40,0x49);   // Get the current screen mode\r
+       if ((c < 4) || (c == 7))\r
+               goto oh_kill_me;\r
+\r
+       // DEBUG - handle screen cleanup\r
+\r
+       US_SaveWindow(&wr);\r
+       US_CenterWindow(30,3);\r
+       US_CPrint(s);\r
+       US_CPrint("(R)etry or (A)bort?");\r
+       VW_UpdateScreen();\r
+       IN_ClearKeysDown();\r
+\r
+asm     sti     // Let the keyboard interrupts come through\r
+\r
+       while (true)\r
+       {\r
+               switch (IN_WaitForASCII())\r
+               {\r
+               case key_Escape:\r
+               case 'a':\r
+               case 'A':\r
+                       goto oh_kill_me;\r
+                       break;\r
+               case key_Return:\r
+               case key_Space:\r
+               case 'r':\r
+               case 'R':\r
+                       US_ClearWindow();\r
+                       VW_UpdateScreen();\r
+                       US_RestoreWindow(&wr);\r
+                       return(RETRY);\r
+                       break;\r
+               }\r
+       }\r
+\r
+oh_kill_me:\r
+       abortprogram = s;\r
+       ShutdownId();\r
+       fprintf(stderr,"Terminal Error: %s\n",s);\r
+       if (tedlevel)\r
+               fprintf(stderr,"You launched from TED. I suggest that you reboot...\n");\r
+\r
+       return(ABORT);\r
+#undef  IGNORE\r
+#undef  RETRY\r
+#undef  ABORT\r
+}\r
+#pragma warn    +par\r
+#pragma warn    +rch\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_GiveSaveName() - Returns a pointer to a static buffer that contains\r
+//              the filename to use for the specified save game\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+char *\r
+USL_GiveSaveName(word game)\r
+{\r
+static  char    name[] = "SAVEGAMx."EXT;\r
+\r
+       name[7] = '0' + game;\r
+       return(name);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_SetLoadSaveHooks() - Sets the routines that the User Mgr calls after\r
+//              reading or writing the save game headers\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_SetLoadSaveHooks(boolean (*load)(int),boolean (*save)(int),void (*reset)(void))\r
+{\r
+       USL_LoadGame = load;\r
+       USL_SaveGame = save;\r
+       USL_ResetGame = reset;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_ReadConfig() - Reads the configuration file, if present, and sets\r
+//              things up accordingly. If it's not present, uses defaults. This file\r
+//              includes the high scores.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_ReadConfig(void)\r
+{\r
+       boolean         gotit;\r
+       char            sig[sizeof(EXT)];\r
+       word            version;\r
+       int                     file;\r
+       SDMode          sd;\r
+       SMMode          sm;\r
+       ControlType     ctl;\r
+\r
+       if ((file = open("CONFIG."EXT,O_BINARY | O_RDONLY)) != -1)\r
+       {\r
+               read(file,sig,sizeof(EXT));\r
+               read(file,&version,sizeof(version));\r
+               if (strcmp(sig,EXT) || (version != ConfigVersion))\r
+               {\r
+                       close(file);\r
+                       goto rcfailed;\r
+               }\r
+               read(file,Scores,sizeof(HighScore) * MaxScores);\r
+               read(file,&sd,sizeof(sd));\r
+               read(file,&sm,sizeof(sm));\r
+               read(file,&ctl,sizeof(ctl));\r
+               read(file,&(KbdDefs[0]),sizeof(KbdDefs[0]));\r
+               read(file,&showscorebox,sizeof(showscorebox));\r
+               read(file,&compatability,sizeof(compatability));\r
+#ifdef KEEN\r
+               read(file,&oldshooting,sizeof(oldshooting));\r
+               read(file,&firescan,sizeof(firescan));\r
+#endif\r
+               close(file);\r
+\r
+               HighScoresDirty = false;\r
+               gotit = true;\r
+       }\r
+       else\r
+       {\r
+rcfailed:\r
+               sd = sdm_Off;\r
+               sm = smm_Off;\r
+               ctl = ctrl_Keyboard;\r
+               showscorebox = true;\r
+#ifdef KEEN\r
+               oldshooting = false;\r
+#endif\r
+\r
+               gotit = false;\r
+               HighScoresDirty = true;\r
+       }\r
+\r
+       SD_Default(gotit,sd,sm);\r
+       IN_Default(gotit,ctl);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_WriteConfig() - Writes out the current configuration, including the\r
+//              high scores.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_WriteConfig(void)\r
+{\r
+       word    version;\r
+       int             file;\r
+\r
+       version = ConfigVersion;\r
+       file = open("CONFIG."EXT,O_CREAT | O_BINARY | O_WRONLY,\r
+                               S_IREAD | S_IWRITE | S_IFREG);\r
+       if (file != -1)\r
+       {\r
+               write(file,EXT,sizeof(EXT));\r
+               write(file,&version,sizeof(version));\r
+               write(file,Scores,sizeof(HighScore) * MaxScores);\r
+               write(file,&SoundMode,sizeof(SoundMode));\r
+               write(file,&MusicMode,sizeof(MusicMode));\r
+               if      // Hack\r
+               (\r
+                       (Controls[0] == ctrl_Joystick1)\r
+               ||      (Controls[0] == ctrl_Joystick2)\r
+               )\r
+                       Controls[0] = ctrl_Keyboard;\r
+               write(file,&(Controls[0]),sizeof(Controls[0]));\r
+               write(file,&(KbdDefs[0]),sizeof(KbdDefs[0]));\r
+               write(file,&showscorebox,sizeof(showscorebox));\r
+               write(file,&compatability,sizeof(compatability));\r
+#ifdef KEEN\r
+               write(file,&oldshooting,sizeof(oldshooting));\r
+               write(file,&firescan,sizeof(firescan));\r
+#endif\r
+               close(file);\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_CheckSavedGames() - Checks to see which saved games are present\r
+//              & valid\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_CheckSavedGames(void)\r
+{\r
+       boolean         ok;\r
+       char            *filename;\r
+       word            i;\r
+       int                     file;\r
+       SaveGame        *game;\r
+\r
+       USL_SaveGame = 0;\r
+       USL_LoadGame = 0;\r
+\r
+       for (i = 0,game = Games;i < MaxSaveGames;i++,game++)\r
+       {\r
+               filename = USL_GiveSaveName(i);\r
+               ok = false;\r
+               if ((file = open(filename,O_BINARY | O_RDONLY)) != -1)\r
+               {\r
+                       if\r
+                       (\r
+                               (read(file,game,sizeof(*game)) == sizeof(*game))\r
+                       &&      (!strcmp(game->signature,EXT))\r
+                       &&      (game->oldtest == &PrintX)\r
+                       )\r
+                               ok = true;\r
+\r
+                       close(file);\r
+               }\r
+\r
+               if (ok)\r
+                       game->present = true;\r
+               else\r
+               {\r
+                       strcpy(game->signature,EXT);\r
+                       game->present = false;\r
+                       strcpy(game->name,"Empty");\r
+               }\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_Startup() - Starts the User Mgr\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_Startup(void)\r
+{\r
+       int     i;\r
+\r
+       if (US_Started)\r
+               return;\r
+\r
+       harderr(USL_HardError); // Install the fatal error handler\r
+\r
+       US_InitRndT(true);              // Initialize the random number generator\r
+\r
+       USL_ReadConfig();               // Read config file\r
+\r
+       for (i = 1;i < _argc;i++)\r
+       {\r
+               switch (US_CheckParm(_argv[i],ParmStrings2))\r
+               {\r
+               case 0:\r
+                       if (grmode == EGAGR)\r
+                               compatability = true;\r
+                       break;\r
+               case 1:\r
+                       compatability = false;\r
+                       break;\r
+               }\r
+       }\r
+\r
+       US_Started = true;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_Setup() - Does the disk access part of the User Mgr's startup\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_Setup(void)\r
+{\r
+       USL_CheckSavedGames();  // Check which saved games are present\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_Shutdown() - Shuts down the User Mgr\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_Shutdown(void)\r
+{\r
+       if (!US_Started)\r
+               return;\r
+\r
+       if (!abortprogram)\r
+               USL_WriteConfig();\r
+\r
+       US_Started = false;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_CheckParm() - checks to see if a string matches one of a set of\r
+//              strings. The check is case insensitive. The routine returns the\r
+//              index of the string that matched, or -1 if no matches were found\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+int\r
+US_CheckParm(char *parm,char **strings)\r
+{\r
+       char    cp,cs,\r
+                       *p,*s;\r
+       int             i;\r
+\r
+       while (!isalpha(*parm)) // Skip non-alphas\r
+               parm++;\r
+\r
+       for (i = 0;*strings && **strings;i++)\r
+       {\r
+               for (s = *strings++,p = parm,cs = cp = 0;cs == cp;)\r
+               {\r
+                       cs = *s++;\r
+                       if (!cs)\r
+                               return(i);\r
+                       cp = *p++;\r
+\r
+                       if (isupper(cs))\r
+                               cs = tolower(cs);\r
+                       if (isupper(cp))\r
+                               cp = tolower(cp);\r
+               }\r
+       }\r
+       return(-1);\r
+}\r
+\r
+#if 0\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_ScreenDraw() - Draws a chunk of the text screen (called only by\r
+//              US_TextScreen())\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_ScreenDraw(word x,word y,char *s,byte attr)\r
+{\r
+       byte    far *screen,far *oscreen;\r
+\r
+       screen = MK_FP(0xb800,(x * 2) + (y * 80 * 2));\r
+       oscreen = (&introscn + 7) + ((x - 1) * 2) + (y * 80 * 2) + 1;\r
+       while (*s)\r
+       {\r
+               *screen++ = *s++;\r
+               if (attr != 0xff)\r
+               {\r
+                       *screen++ = (attr & 0x8f) | (*oscreen & 0x70);\r
+                       oscreen += 2;\r
+               }\r
+               else\r
+                       screen++;\r
+       }\r
+}\r
+#endif\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_ClearTextScreen() - Makes sure the screen is in text mode, clears it,\r
+//              and moves the cursor to the leftmost column of the bottom line\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_ClearTextScreen(void)\r
+{\r
+       // Set to 80x25 color text mode\r
+       _AL = 3;                                // Mode 3\r
+       _AH = 0x00;\r
+       geninterrupt(0x10);\r
+\r
+       // Use BIOS to move the cursor to the bottom of the screen\r
+       _AH = 0x0f;\r
+       geninterrupt(0x10);             // Get current video mode into _BH\r
+       _DL = 0;                                // Lefthand side of the screen\r
+       _DH = 24;                               // Bottom row\r
+       _AH = 0x02;\r
+       geninterrupt(0x10);\r
+}\r
+\r
+#if 0\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_TextScreen() - Puts up the startup text screen\r
+//      Note: These are the only User Manager functions that can be safely called\r
+//              before the User Mgr has been started up\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_TextScreen(void)\r
+{\r
+       word    i,n;\r
+\r
+       USL_ClearTextScreen();\r
+\r
+       _fmemcpy(MK_FP(0xb800,0),7 + &introscn,80 * 25 * 2);\r
+\r
+       // Check for TED launching here\r
+       for (i = 1;i < _argc;i++)\r
+       {\r
+               n = US_CheckParm(_argv[i],ParmStrings);\r
+               if (n == 0)\r
+               {\r
+                       tedlevelnum = atoi(_argv[i + 1]);\r
+                       if (tedlevelnum >= 0)\r
+                       {\r
+                               tedlevel = true;\r
+                               return;\r
+                       }\r
+                       else\r
+                               break;\r
+               }\r
+               else if (n == 1)\r
+               {\r
+                       NoWait = true;\r
+                       return;\r
+               }\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_Show() - Changes the appearance of one of the fields on the text\r
+//              screen. Possibly adds a checkmark in front of it and highlights it\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_Show(word x,word y,word w,boolean show,boolean hilight)\r
+{\r
+       byte    far *screen,far *oscreen;\r
+\r
+       screen = MK_FP(0xb800,((x - 1) * 2) + (y * 80 * 2));\r
+       oscreen = (&introscn + 7) + ((x - 1) * 2) + (y * 80 * 2) - 1;\r
+       *screen++ = show? 251 : ' ';    // Checkmark char or space\r
+//      *screen = 0x48;\r
+//      *screen = (*oscreen & 0xf0) | 8;\r
+       oscreen += 2;\r
+       if (show && hilight)\r
+       {\r
+               for (w++;w--;screen += 2,oscreen += 2)\r
+                       *screen = (*oscreen & 0xf0) | 0x0f;\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_ShowMem() - Right justifies a longword in one of the memory fields on\r
+//              the text screen\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_ShowMem(word x,word y,long mem)\r
+{\r
+       char    buf[16];\r
+       word    i;\r
+\r
+       for (i = strlen(ltoa(mem,buf,10));i < 5;i++)\r
+               USL_ScreenDraw(x++,y," ",0xff);\r
+       USL_ScreenDraw(x,y,buf,0xff);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_UpdateTextScreen() - Called after the ID libraries are started up.\r
+//              Displays what hardware is present.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_UpdateTextScreen(void)\r
+{\r
+       boolean         b;\r
+       longword        totalmem;\r
+\r
+       // Show video card info\r
+       b = (grmode == CGAGR);\r
+       USL_Show(21,7,4,(videocard >= CGAcard) && (videocard <= VGAcard),b);\r
+       b = (grmode == EGAGR);\r
+       USL_Show(21,8,4,(videocard >= EGAcard) && (videocard <= VGAcard),b);\r
+       b = (grmode == VGAGR);\r
+       USL_Show(21,9,4,videocard == VGAcard,b);\r
+       if (compatability)\r
+               USL_ScreenDraw(5,10,"SVGA Compatibility Mode Enabled.",0x4f);\r
+\r
+       // Show input device info\r
+       USL_Show(60,7,8,true,true);\r
+       USL_Show(60,8,11,JoysPresent[0],true);\r
+       USL_Show(60,9,11,JoysPresent[1],true);\r
+       USL_Show(60,10,5,MousePresent,true);\r
+\r
+       // Show sound hardware info\r
+       USL_Show(21,14,11,true,SoundMode == sdm_PC);\r
+       b = (SoundMode == sdm_AdLib) || (MusicMode == smm_AdLib);\r
+       USL_Show(21,15,14,AdLibPresent,b);\r
+       if (b && AdLibPresent)  // Hack because of two lines\r
+       {\r
+               byte    far *screen,far *oscreen;\r
+               word    x,y,w;\r
+\r
+               x = 21;\r
+               y = 16;\r
+               w = 14;\r
+               screen = MK_FP(0xb800,(x * 2) + (y * 80 * 2) - 1);\r
+               oscreen = (&introscn + 7) + (x * 2) + (y * 80 * 2) - 1;\r
+               oscreen += 2;\r
+               for (w++;w--;screen += 2,oscreen += 2)\r
+                       *screen = (*oscreen & 0xf0) | 0x0f;\r
+       }\r
+\r
+       // Show memory available/used\r
+       USL_ShowMem(63,15,mminfo.mainmem / 1024);\r
+       USL_Show(53,15,23,true,true);\r
+       USL_ShowMem(63,16,mminfo.EMSmem / 1024);\r
+       USL_Show(53,16,23,mminfo.EMSmem? true : false,true);\r
+       USL_ShowMem(63,17,mminfo.XMSmem / 1024);\r
+       USL_Show(53,17,23,mminfo.XMSmem? true : false,true);\r
+       totalmem = mminfo.mainmem + mminfo.EMSmem + mminfo.XMSmem;\r
+       USL_ShowMem(63,18,totalmem / 1024);\r
+       USL_Show(53,18,23,true,true);   // DEBUG\r
+       USL_ScreenDraw(52,18," ",0xff);\r
+\r
+       // Change Initializing... to Loading...\r
+       USL_ScreenDraw(27,22,"  Loading...   ",0x9c);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_FinishTextScreen() - After the main program has finished its initial\r
+//              loading, this routine waits for a keypress and then clears the screen\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_FinishTextScreen(void)\r
+{\r
+static  byte    colors[] = {4,6,13,15,15,15,15,15,15};\r
+               boolean up;\r
+               int             i,c;\r
+\r
+       // Change Loading... to Press a Key\r
+\r
+       if (!(tedlevel || NoWait))\r
+       {\r
+               IN_ClearKeysDown();\r
+               for (i = 0,up = true;!IN_UserInput(4,true);)\r
+               {\r
+                       c = colors[i];\r
+                       if (up)\r
+                       {\r
+                               if (++i == 9)\r
+                                       i = 8,up = false;\r
+                       }\r
+                       else\r
+                       {\r
+                               if (--i < 0)\r
+                                       i = 1,up = true;\r
+                       }\r
+\r
+                       USL_ScreenDraw(29,22," Ready - Press a Key     ",0x00 + c);\r
+               }\r
+       }\r
+       else\r
+               USL_ScreenDraw(29,22," Ready - Press a Key     ",0x9a);\r
+       IN_ClearKeysDown();\r
+\r
+       USL_ClearTextScreen();\r
+}\r
+#endif\r
+\r
+//      Window/Printing routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_SetPrintRoutines() - Sets the routines used to measure and print\r
+//              from within the User Mgr. Primarily provided to allow switching\r
+//              between masked and non-masked fonts\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_SetPrintRoutines(void (*measure)(char far *,word *,word *),void (*print)(char far *))\r
+{\r
+       USL_MeasureString = measure;\r
+       USL_DrawString = print;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_Print() - Prints a string in the current window. Newlines are\r
+//              supported.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_Print(char *s)\r
+{\r
+       char    c,*se;\r
+       word    w,h;\r
+\r
+       while (*s)\r
+       {\r
+               se = s;\r
+               while ((c = *se) && (c != '\n'))\r
+                       se++;\r
+               *se = '\0';\r
+\r
+               USL_MeasureString(s,&w,&h);\r
+               px = PrintX;\r
+               py = PrintY;\r
+               USL_DrawString(s);\r
+\r
+               s = se;\r
+               if (c)\r
+               {\r
+                       *se = c;\r
+                       s++;\r
+\r
+                       PrintX = WindowX;\r
+                       PrintY += h;\r
+               }\r
+               else\r
+                       PrintX += w;\r
+       }\r
+}\r
+\r
+// MDM - (GAMERS EDGE) begin\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+// US_Printxy()\r
+//\r
+void US_Printxy(word x, word y, char *text)\r
+{\r
+       word orgx, orgy;\r
+\r
+       orgx = PrintX;\r
+       orgy = PrintY;\r
+\r
+//     PrintX = WindowX+x;\r
+//     PrintY = WindowY+y;\r
+       PrintX = x;\r
+       PrintY = y;\r
+       US_Print(text);\r
+\r
+       PrintX = orgx;\r
+       PrintY = orgy;\r
+}\r
+\r
+// MDM - (GAMERS EDGE) end\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_PrintUnsigned() - Prints an unsigned long\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_PrintUnsigned(longword n)\r
+{\r
+       char    buffer[32];\r
+\r
+       US_Print(ultoa(n,buffer,10));\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_PrintSigned() - Prints a signed long\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_PrintSigned(long n)\r
+{\r
+       char    buffer[32];\r
+\r
+       US_Print(ltoa(n,buffer,10));\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_PrintInCenter() - Prints a string in the center of the given rect\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+USL_PrintInCenter(char *s,Rect r)\r
+{\r
+       word    w,h,\r
+                       rw,rh;\r
+\r
+       USL_MeasureString(s,&w,&h);\r
+       rw = r.lr.x - r.ul.x;\r
+       rh = r.lr.y - r.ul.y;\r
+\r
+       px = r.ul.x + ((rw - w) / 2);\r
+       py = r.ul.y + ((rh - h) / 2);\r
+       USL_DrawString(s);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_PrintCentered() - Prints a string centered in the current window.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_PrintCentered(char *s)\r
+{\r
+       Rect    r;\r
+\r
+       r.ul.x = WindowX;\r
+       r.ul.y = WindowY;\r
+       r.lr.x = r.ul.x + WindowW;\r
+       r.lr.y = r.ul.y + WindowH;\r
+\r
+       USL_PrintInCenter(s,r);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_CPrintLine() - Prints a string centered on the current line and\r
+//              advances to the next line. Newlines are not supported.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_CPrintLine(char *s)\r
+{\r
+       word    w,h;\r
+\r
+       USL_MeasureString(s,&w,&h);\r
+\r
+       if (w > WindowW)\r
+               Quit("US_CPrintLine() - String exceeds width");\r
+       px = WindowX + ((WindowW - w) / 2);\r
+       py = PrintY;\r
+       USL_DrawString(s);\r
+       PrintY += h;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_CPrint() - Prints a string in the current window. Newlines are\r
+//              supported.\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_CPrint(char *s)\r
+{\r
+       char    c,*se;\r
+\r
+       while (*s)\r
+       {\r
+               se = s;\r
+               while ((c = *se) && (c != '\n'))\r
+                       se++;\r
+               *se = '\0';\r
+\r
+               US_CPrintLine(s);\r
+\r
+               s = se;\r
+               if (c)\r
+               {\r
+                       *se = c;\r
+                       s++;\r
+               }\r
+       }\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_ClearWindow() - Clears the current window to white and homes the\r
+//              cursor\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_ClearWindow(void)\r
+{\r
+       VWB_Bar(WindowX,WindowY,WindowW,WindowH,LT_GREY);\r
+       PrintX = WindowX;\r
+       PrintY = WindowY;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_DrawWindow() - Draws a frame and sets the current window parms\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_DrawWindow(word x,word y,word w,word h)\r
+{\r
+       word    i,\r
+                       sx,sy,sw,sh;\r
+\r
+       WindowX = x * 8;\r
+       WindowY = y * 8;\r
+       WindowW = w * 8;\r
+       WindowH = h * 8;\r
+\r
+       PrintX = WindowX;\r
+       PrintY = WindowY;\r
+\r
+       sx = (x - 1) * 8;\r
+       sy = (y - 1) * 8;\r
+       sw = (w + 1) * 8;\r
+       sh = (h + 1) * 8;\r
+\r
+       US_ClearWindow();\r
+\r
+       VWB_DrawTile8M(sx,sy,0),VWB_DrawTile8M(sx,sy + sh,6);\r
+       for (i = sx + 8;i <= sx + sw - 8;i += 8)\r
+               VWB_DrawTile8M(i,sy,1),VWB_DrawTile8M(i,sy + sh,7);\r
+       VWB_DrawTile8M(i,sy,2),VWB_DrawTile8M(i,sy + sh,8);\r
+\r
+       for (i = sy + 8;i <= sy + sh - 8;i += 8)\r
+               VWB_DrawTile8M(sx,i,3),VWB_DrawTile8M(sx + sw,i,5);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_CenterWindow() - Generates a window of a given width & height in the\r
+//              middle of the screen\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_CenterWindow(word w,word h)\r
+{\r
+       US_DrawWindow(((MaxX / 8) - w) / 2,((MaxY / 8) - h) / 2,w,h);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_CenterSaveWindow() - Generates a window of a given width & height in\r
+//              the middle of the screen, saving the background\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_CenterSaveWindow(word w,word h,memptr *save)\r
+{\r
+       word    x,y,\r
+                       screen;\r
+\r
+       x = ((MaxX / 8) - w) / 2;\r
+       y = ((MaxY / 8) - h) / 2;\r
+       MM_GetPtr(save,(w * h) * CHARWIDTH);\r
+       screen = bufferofs + panadjust + ylookup[y] + (x * CHARWIDTH);\r
+       VW_ScreenToMem(screen,*save,w * CHARWIDTH,h);\r
+       US_DrawWindow(((MaxX / 8) - w) / 2,((MaxY / 8) - h) / 2,w,h);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_RestoreSaveWindow() - Restores the background of the size of the\r
+//              current window from the memory specified by save\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_RestoreSaveWindow(memptr *save)\r
+{\r
+       word    screen;\r
+\r
+       screen = bufferofs + panadjust + ylookup[WindowY] + (WindowX * CHARWIDTH);\r
+       VW_MemToScreen(*save,screen,WindowW * CHARWIDTH,WindowH);\r
+       MM_FreePtr(save);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_SaveWindow() - Saves the current window parms into a record for\r
+//              later restoration\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_SaveWindow(WindowRec *win)\r
+{\r
+       win->x = WindowX;\r
+       win->y = WindowY;\r
+       win->w = WindowW;\r
+       win->h = WindowH;\r
+\r
+       win->px = PrintX;\r
+       win->py = PrintY;\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_RestoreWindow() - Sets the current window parms to those held in the\r
+//              record\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_RestoreWindow(WindowRec *win)\r
+{\r
+       WindowX = win->x;\r
+       WindowY = win->y;\r
+       WindowW = win->w;\r
+       WindowH = win->h;\r
+\r
+       PrintX = win->px;\r
+       PrintY = win->py;\r
+}\r
+\r
+//      Cursor routines\r
+\r
+#if 0\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_StartCursor() - Sets up the cursor for User Mgr use\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_StartCursor(void)\r
+{\r
+       CursorInfo      info;\r
+\r
+       VW_SetCursor(CURSORARROWSPR);\r
+       CursorX = MaxX / 2;\r
+       CursorY = MaxY / 2;\r
+       VW_MoveCursor(CursorX,CursorY);\r
+       VW_ShowCursor();\r
+\r
+       IN_ReadCursor(&info);   // Dispose of any accumulated movement\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_ShutCursor() - Cleans up after US_StartCursor()\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+void\r
+US_ShutCursor(void)\r
+{\r
+       VW_HideCursor();\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_UpdateCursor() - Gets the new cursor position & button states from\r
+//              the Input Mgr and tells the View Mgr where the cursor is\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+US_UpdateCursor(void)\r
+{\r
+       CursorInfo      info;\r
+\r
+       IN_ReadCursor(&info);\r
+       if (info.x || info.y || CursorBad)\r
+       {\r
+               CursorX += info.x;\r
+               if (CursorX >= MaxX)\r
+                       CursorX = MaxX - 1;\r
+               else if (CursorX < 0)\r
+                       CursorX = 0;\r
+\r
+               CursorY += info.y;\r
+               if (CursorY >= MaxY)\r
+                       CursorY = MaxY - 1;\r
+               else if (CursorY < 0)\r
+                       CursorY = 0;\r
+\r
+               VW_MoveCursor(CursorX,CursorY);\r
+               CursorBad = false;\r
+       }\r
+       Button0 = info.button0;\r
+       Button1 = info.button1;\r
+       return(Button0 || Button1);\r
+}\r
+#endif\r
+\r
+//      Input routines\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_XORICursor() - XORs the I-bar text cursor. Used by US_LineInput()\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_XORICursor(int x,int y,char *s,word cursor)\r
+{\r
+       char    buf[MaxString];\r
+       word    w,h;\r
+\r
+       strcpy(buf,s);\r
+       buf[cursor] = '\0';\r
+       USL_MeasureString(buf,&w,&h);\r
+\r
+       px = x + w - 1;\r
+       py = y;\r
+       USL_DrawString("\x80");\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_LineInput() - Gets a line of user input at (x,y), the string defaults\r
+//              to whatever is pointed at by def. Input is restricted to maxchars\r
+//              chars or maxwidth pixels wide. If the user hits escape (and escok is\r
+//              true), nothing is copied into buf, and false is returned. If the\r
+//              user hits return, the current string is copied into buf, and true is\r
+//              returned\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+boolean\r
+US_LineInput(int x,int y,char *buf,char *def,boolean escok,\r
+                               int maxchars,int maxwidth)\r
+{\r
+       boolean         redraw,\r
+                               cursorvis,cursormoved,\r
+                               done,result;\r
+       ScanCode        sc;\r
+       char            c,\r
+                               s[MaxString],olds[MaxString];\r
+       word            i,\r
+                               cursor,\r
+                               w,h,\r
+                               len;\r
+       longword        lasttime;\r
+\r
+       VW_HideCursor();\r
+\r
+       if (def)\r
+               strcpy(s,def);\r
+       else\r
+               *s = '\0';\r
+       *olds = '\0';\r
+       cursor = strlen(s);\r
+       cursormoved = redraw = true;\r
+\r
+       cursorvis = done = false;\r
+       lasttime = TimeCount;\r
+       LastASCII = key_None;\r
+       LastScan = sc_None;\r
+\r
+       while (!done)\r
+       {\r
+               if (cursorvis)\r
+                       USL_XORICursor(x,y,s,cursor);\r
+\r
+       asm     pushf\r
+       asm     cli\r
+\r
+               sc = LastScan;\r
+               LastScan = sc_None;\r
+               c = LastASCII;\r
+               LastASCII = key_None;\r
+\r
+       asm     popf\r
+\r
+               switch (sc)\r
+               {\r
+               case sc_LeftArrow:\r
+                       if (cursor)\r
+                               cursor--;\r
+                       c = key_None;\r
+                       cursormoved = true;\r
+                       break;\r
+               case sc_RightArrow:\r
+                       if (s[cursor])\r
+                               cursor++;\r
+                       c = key_None;\r
+                       cursormoved = true;\r
+                       break;\r
+               case sc_Home:\r
+                       cursor = 0;\r
+                       c = key_None;\r
+                       cursormoved = true;\r
+                       break;\r
+               case sc_End:\r
+                       cursor = strlen(s);\r
+                       c = key_None;\r
+                       cursormoved = true;\r
+                       break;\r
+\r
+               case sc_Return:\r
+                       strcpy(buf,s);\r
+                       done = true;\r
+                       result = true;\r
+                       c = key_None;\r
+                       break;\r
+               case sc_Escape:\r
+                       if (escok)\r
+                       {\r
+                               done = true;\r
+                               result = false;\r
+                       }\r
+                       c = key_None;\r
+                       break;\r
+\r
+               case sc_BackSpace:\r
+                       if (cursor)\r
+                       {\r
+                               strcpy(s + cursor - 1,s + cursor);\r
+                               cursor--;\r
+                               redraw = true;\r
+                       }\r
+                       c = key_None;\r
+                       cursormoved = true;\r
+                       break;\r
+               case sc_Delete:\r
+                       if (s[cursor])\r
+                       {\r
+                               strcpy(s + cursor,s + cursor + 1);\r
+                               redraw = true;\r
+                       }\r
+                       c = key_None;\r
+                       cursormoved = true;\r
+                       break;\r
+\r
+               case 0x4c:      // Keypad 5\r
+               case sc_UpArrow:\r
+               case sc_DownArrow:\r
+               case sc_PgUp:\r
+               case sc_PgDn:\r
+               case sc_Insert:\r
+                       c = key_None;\r
+                       break;\r
+               }\r
+\r
+               if (c)\r
+               {\r
+                       len = strlen(s);\r
+                       USL_MeasureString(s,&w,&h);\r
+\r
+                       if\r
+                       (\r
+                               isprint(c)\r
+                       &&      (len < MaxString - 1)\r
+                       &&      ((!maxchars) || (len < maxchars))\r
+                       &&      ((!maxwidth) || (w < maxwidth))\r
+                       )\r
+                       {\r
+                               for (i = len + 1;i > cursor;i--)\r
+                                       s[i] = s[i - 1];\r
+                               s[cursor++] = c;\r
+                               redraw = true;\r
+                       }\r
+               }\r
+\r
+               if (redraw)\r
+               {\r
+                       px = x;\r
+                       py = y;\r
+                       USL_DrawString(olds);\r
+                       strcpy(olds,s);\r
+\r
+                       px = x;\r
+                       py = y;\r
+                       USL_DrawString(s);\r
+\r
+                       redraw = false;\r
+               }\r
+\r
+               if (cursormoved)\r
+               {\r
+                       cursorvis = false;\r
+                       lasttime = TimeCount - TickBase;\r
+\r
+                       cursormoved = false;\r
+               }\r
+               if (TimeCount - lasttime > TickBase / 2)\r
+               {\r
+                       lasttime = TimeCount;\r
+\r
+                       cursorvis ^= true;\r
+               }\r
+               if (cursorvis)\r
+                       USL_XORICursor(x,y,s,cursor);\r
+\r
+               VW_UpdateScreen();\r
+       }\r
+\r
+       if (cursorvis)\r
+               USL_XORICursor(x,y,s,cursor);\r
+       if (!result)\r
+       {\r
+               px = x;\r
+               py = y;\r
+               USL_DrawString(olds);\r
+       }\r
+       VW_ShowCursor();\r
+       VW_UpdateScreen();\r
+\r
+       IN_ClearKeysDown();\r
+       return(result);\r
+}\r
+\r
diff --git a/16/cawat/ID_US_2.C b/16/cawat/ID_US_2.C
new file mode 100644 (file)
index 0000000..3582803
--- /dev/null
@@ -0,0 +1,1822 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+//      ID Engine\r
+//      ID_US.c - User Manager - User interface\r
+//      v1.1d1\r
+//      By Jason Blochowiak\r
+//      Hacked up for Catacomb 3D\r
+//\r
+\r
+#include "ID_HEADS.H"\r
+#pragma hdrstop\r
+\r
+#pragma warn    -pia\r
+\r
+//      Special imports\r
+extern  boolean         showscorebox;\r
+#ifdef  KEEN\r
+extern  boolean         oldshooting;\r
+extern  ScanCode        firescan;\r
+#else\r
+               ScanCode        firescan;\r
+#endif\r
+\r
+//      Global variables\r
+               boolean         ingame,abortgame,loadedgame;\r
+               GameDiff        restartgame = gd_Continue;\r
+\r
+//      Internal variables\r
+static  boolean         GameIsDirty,\r
+                                       QuitToDos,\r
+                                       CtlPanelDone;\r
+\r
+//      Forward reference prototypes\r
+static void     USL_SetupCard(void);\r
+\r
+//      Control panel data\r
+\r
+#define CtlPanelSX      74\r
+#define CtlPanelSY      48\r
+#define CtlPanelEX      234\r
+#define CtlPanelEY      150\r
+#define CtlPanelW       (CtlPanelEX - CtlPanelSX)\r
+#define CtlPanelH       (CtlPanelEY - CtlPanelSY)\r
+\r
+#define TileBase        92\r
+\r
+// DEBUG - CGA\r
+#define BackColor               0\r
+#define HiliteColor             (BackColor ^ 12)\r
+#define NohiliteColor   (BackColor ^ 4)\r
+\r
+typedef enum\r
+               {\r
+                       uc_None,\r
+                       uc_Return,\r
+                       uc_Abort,\r
+                       uc_Quit,\r
+                       uc_Loaded,\r
+                       uc_SEasy,\r
+                       uc_SNormal,\r
+                       uc_SHard,\r
+               } UComm;\r
+typedef enum\r
+               {\r
+                       uii_Bad,\r
+                       uii_Button,uii_RadioButton,uii_Folder\r
+               } UIType;\r
+typedef enum\r
+               {\r
+                       ui_Normal = 0,\r
+                       ui_Pushed = 1,\r
+                       ui_Selected = 2,\r
+                       ui_Disabled = 4,\r
+                       ui_Separated = 8\r
+               } UIFlags;\r
+#define UISelectFlags (ui_Pushed | ui_Selected | ui_Disabled)\r
+\r
+typedef enum\r
+               {\r
+                       uic_SetupCard,uic_DrawCard,uic_TouchupCard,\r
+                       uic_DrawIcon,uic_Draw,uic_Hit\r
+               } UserCall;\r
+\r
+typedef struct  UserItem\r
+               {\r
+                               UIType                  type;\r
+                               UIFlags                 flags;\r
+                               ScanCode                hotkey;\r
+                               char                    *text;\r
+                               UComm                   comm;\r
+                               void                    far *child;     // Should be (UserItemGroup *)\r
+\r
+                               word                    x,y;\r
+               } UserItem;\r
+typedef struct  UserItemGroup\r
+               {\r
+                               word                    x,y;\r
+                               graphicnums             title;\r
+                               ScanCode                hotkey;\r
+                               UserItem                far *items;\r
+                               boolean                 (*custom)(UserCall,struct UserItem far *);      // Custom routine\r
+\r
+                               word                    cursor;\r
+               struct  UserItemGroup   far *parent;\r
+               } UserItemGroup;\r
+\r
+static  char            *BottomS1,*BottomS2,*BottomS3;\r
+static  UComm           Communication;\r
+static  ScanCode        *KeyMaps[] =\r
+                                       {\r
+                                               &KbdDefs[0].button0,\r
+                                               &KbdDefs[0].button1,\r
+                                               &firescan,\r
+                                               &KbdDefs[0].upleft,\r
+                                               &KbdDefs[0].up,\r
+                                               &KbdDefs[0].upright,\r
+                                               &KbdDefs[0].right,\r
+                                               &KbdDefs[0].downright,\r
+                                               &KbdDefs[0].down,\r
+                                               &KbdDefs[0].downleft,\r
+                                               &KbdDefs[0].left\r
+                                       };\r
+\r
+// Custom routine prototypes\r
+static  boolean USL_ConfigCustom(UserCall call,struct UserItem far *item),\r
+                               USL_KeyCustom(UserCall call,struct UserItem far *item),\r
+                               USL_KeySCustom(UserCall call,struct UserItem far *item),\r
+                               USL_Joy1Custom(UserCall call,struct UserItem far *item),\r
+                               USL_Joy2Custom(UserCall call,struct UserItem far *item),\r
+                               USL_LoadCustom(UserCall call,struct UserItem far *item),\r
+                               USL_SaveCustom(UserCall call,struct UserItem far *item),\r
+                               USL_ScoreCustom(UserCall call,struct UserItem far *item),\r
+                               USL_CompCustom(UserCall call,struct UserItem far *item);\r
+#ifdef KEEN\r
+                               USL_TwoCustom(UserCall call,struct UserItem far *item),\r
+#endif\r
+//                             USL_PongCustom(UserCall call,struct UserItem far *item);\r
+\r
+#define DefButton(key,text)                             uii_Button,ui_Normal,key,text\r
+#define DefRButton(key,text)                    uii_RadioButton,ui_Normal,key,text\r
+#define DefFolder(key,text,child)               uii_Folder,ui_Normal,key,text,uc_None,child\r
+#define CustomGroup(title,key,custom)   0,0,title,key,0,custom\r
+       UserItem far holder[] =\r
+       {\r
+               {DefButton(sc_None,"DEBUG")},\r
+               {uii_Bad}\r
+       };\r
+       UserItemGroup   far holdergroup = {0,0,CP_MAINMENUPIC,sc_None,holder};\r
+\r
+       // Sound menu\r
+       UserItem far soundi[] =\r
+       {\r
+               {DefRButton(sc_N,"NO SOUND EFFECTS")},\r
+               {DefRButton(sc_P,"PC SPEAKER")},\r
+               {DefRButton(sc_A,"ADLIB/SOUNDBLASTER")},\r
+               {uii_Bad}\r
+       };\r
+       UserItemGroup   far soundgroup = {8,0,CP_SOUNDMENUPIC,sc_None,soundi};\r
+\r
+       // Music menu\r
+       UserItem far musici[] =\r
+       {\r
+               {DefRButton(sc_N,"NO MUSIC")},\r
+               {DefRButton(sc_A,"ADLIB/SOUNDBLASTER")},\r
+               {uii_Bad}\r
+       };\r
+       UserItemGroup   far musicgroup = {8,0,CP_MUSICMENUPIC,sc_None,musici};\r
+\r
+       // New game menu\r
+       UserItem far newgamei[] =\r
+       {\r
+               {DefButton(sc_E,"BEGIN EASY GAME"),uc_SEasy},\r
+               {DefButton(sc_N,"BEGIN NORMAL GAME"),uc_SNormal},\r
+               {DefButton(sc_H,"BEGIN HARD GAME"),uc_SHard},\r
+               {uii_Bad}\r
+       };\r
+       UserItemGroup   far newgamegroup = {8,0,CP_NEWGAMEMENUPIC,sc_None,newgamei,0,1};\r
+\r
+       // Load/Save game menu\r
+       UserItem far loadsavegamei[] =\r
+       {\r
+               {uii_Button,ui_Normal,sc_None},\r
+               {uii_Button,ui_Normal,sc_None},\r
+               {uii_Button,ui_Normal,sc_None},\r
+               {uii_Button,ui_Normal,sc_None},\r
+               {uii_Button,ui_Normal,sc_None},\r
+               {uii_Button,ui_Normal,sc_None},\r
+               {uii_Bad}\r
+       };\r
+       UserItemGroup   far loadgamegroup = {4,3,CP_LOADMENUPIC,sc_None,loadsavegamei,USL_LoadCustom};\r
+       UserItemGroup   far savegamegroup = {4,3,CP_SAVEMENUPIC,sc_None,loadsavegamei,USL_SaveCustom};\r
+\r
+       // Options menu\r
+       UserItemGroup   far scoregroup = {0,0,0,sc_None,0,USL_ScoreCustom};\r
+       UserItemGroup   far compgroup = {0,0,0,sc_None,0,USL_CompCustom};\r
+#ifdef KEEN\r
+       UserItemGroup   far twogroup = {0,0,0,sc_None,0,USL_TwoCustom};\r
+#endif\r
+       UserItem far optionsi[] =\r
+       {\r
+               {DefFolder(sc_S,"",&scoregroup)},\r
+               {DefFolder(sc_C,"",&compgroup)},\r
+#ifdef KEEN\r
+               {DefFolder(sc_T,"",&twogroup)},\r
+#endif\r
+               {uii_Bad}\r
+       };\r
+       UserItemGroup   far optionsgroup = {8,0,CP_OPTIONSMENUPIC,sc_None,optionsi};\r
+\r
+       // Keyboard menu\r
+       UserItem far keyi[] =\r
+       {\r
+               {DefButton(sc_None,"UP & LEFT")},\r
+               {DefButton(sc_None,"UP")},\r
+               {DefButton(sc_None,"UP & RIGHT")},\r
+               {DefButton(sc_None,"RIGHT")},\r
+               {DefButton(sc_None,"DOWN & RIGHT")},\r
+               {DefButton(sc_None,"DOWN")},\r
+               {DefButton(sc_None,"DOWN & LEFT")},\r
+               {DefButton(sc_None,"LEFT")},\r
+               {uii_Bad}\r
+       };\r
+       UserItemGroup   far keygroup = {0,0,CP_KEYMOVEMENTPIC,sc_None,keyi,USL_KeyCustom};\r
+       UserItem far keybi[] =\r
+       {\r
+#ifdef  KEEN\r
+               {DefButton(sc_J,"JUMP")},\r
+               {DefButton(sc_P,"POGO")},\r
+               {DefButton(sc_F,"FIRE")},\r
+#endif\r
+#ifdef  CAT3D\r
+               {DefButton(sc_J,"FIRE")},\r
+               {DefButton(sc_P,"STRAFE")},\r
+#endif\r
+#ifdef  CPD\r
+               {DefButton(sc_J,"SHOOT")},\r
+               {DefButton(sc_P,"BOMB")},\r
+#endif\r
+               {uii_Bad}\r
+       };\r
+       UserItemGroup   far keybgroup = {0,0,CP_KEYBUTTONPIC,sc_None,keybi,USL_KeyCustom};\r
+       UserItem far keysi[] =\r
+       {\r
+               {DefFolder(sc_M,"MOVEMENT",&keygroup)},\r
+               {DefFolder(sc_B,"BUTTONS",&keybgroup)},\r
+               {uii_Bad}\r
+       };\r
+       UserItemGroup   far keysgroup = {8,0,CP_KEYBOARDMENUPIC,sc_None,keysi,USL_KeySCustom};\r
+\r
+       // Joystick #1 & #2\r
+       UserItemGroup   far joy1group = {CustomGroup(CP_JOYSTICKMENUPIC,sc_None,USL_Joy1Custom)};\r
+       UserItemGroup   far joy2group = {CustomGroup(CP_JOYSTICKMENUPIC,sc_None,USL_Joy2Custom)};\r
+\r
+       // Config menu\r
+       UserItem far configi[] =\r
+       {\r
+               {DefFolder(sc_S,"SOUND",&soundgroup)},\r
+               {DefFolder(sc_M,"MUSIC",&musicgroup)},\r
+               {uii_Folder,ui_Separated,sc_K,"USE KEYBOARD",uc_None,&keysgroup},\r
+               {DefFolder(sc_None,"USE JOYSTICK #1",&joy1group)},\r
+               {DefFolder(sc_None,"USE JOYSTICK #2",&joy2group)},\r
+               {uii_Bad}\r
+       };\r
+       UserItemGroup   far configgroup = {8,0,CP_CONFIGMENUPIC,sc_None,configi,USL_ConfigCustom};\r
+\r
+       // Main menu\r
+//     UserItemGroup   far ponggroup = {0,0,0,sc_None,0,USL_PongCustom};\r
+       UserItem far rooti[] =\r
+       {\r
+               {DefFolder(sc_N,"NEW GAME",&newgamegroup)},\r
+               {DefFolder(sc_L,"LOAD GAME",&loadgamegroup)},\r
+               {DefFolder(sc_S,"SAVE GAME",&savegamegroup)},\r
+               {DefFolder(sc_C,"CONFIGURE",&configgroup)},\r
+               {DefButton(sc_R,nil),uc_Return},        // Return to Game/Demo\r
+               {DefButton(sc_E,"END GAME"),uc_Abort},\r
+//             {DefFolder(sc_B,"SKULL 'N' BONES",&ponggroup)},\r
+               {DefButton(sc_Q,"QUIT"),uc_Quit},\r
+               {uii_Bad}\r
+       };\r
+       UserItemGroup   far rootgroup = {32,4,CP_MAINMENUPIC,sc_None,rooti};\r
+#undef  DefButton\r
+#undef  DefFolder\r
+\r
+#define MaxCards        7\r
+       word                    cstackptr;\r
+       UserItemGroup   far *cardstack[MaxCards],\r
+                                       far *topcard;\r
+\r
+//      Card stack code\r
+static void\r
+USL_SetupStack(void)\r
+{\r
+       cstackptr = 0;\r
+       cardstack[0] = topcard = &rootgroup;\r
+}\r
+\r
+static void\r
+USL_PopCard(void)\r
+{\r
+       if (!cstackptr)\r
+               return;\r
+\r
+       topcard = cardstack[--cstackptr];\r
+}\r
+\r
+static void\r
+USL_PushCard(UserItemGroup far *card)\r
+{\r
+       if (cstackptr == MaxCards - 1)\r
+               return;\r
+\r
+       topcard = cardstack[++cstackptr] = card;\r
+}\r
+\r
+static void\r
+USL_DrawItemIcon(UserItem far *item)\r
+{\r
+       word    flags,tile;\r
+\r
+       if (topcard->custom && topcard->custom(uic_DrawIcon,item))\r
+               return;\r
+\r
+       flags = item->flags;\r
+       if (flags & ui_Disabled)\r
+               tile = TileBase + ((flags & ui_Selected)? 5 : 4);\r
+       else if ((item->type == uii_RadioButton) && (!(flags & ui_Pushed)))\r
+               tile = TileBase + ((flags & ui_Selected)? 3 : 2);\r
+       else\r
+               tile = TileBase + ((flags & ui_Selected)? 1 : 0);\r
+       VWB_DrawTile8(item->x,item->y,tile);\r
+}\r
+\r
+static void\r
+USL_DrawItem(UserItem far *item)\r
+{\r
+       if (topcard->custom && topcard->custom(uic_Draw,item))\r
+               return;\r
+\r
+       VWB_Bar(CtlPanelSX + 1,item->y,\r
+                       CtlPanelEX - CtlPanelSX - 1,8,BackColor);       // Clear out background\r
+       USL_DrawItemIcon(item);\r
+       if ((item->flags & ui_Selected) && !(item->flags & ui_Disabled))\r
+               fontcolor = HiliteColor;\r
+       else\r
+               fontcolor = NohiliteColor;\r
+       px = item->x + 8;\r
+       py = item->y + 1;\r
+       USL_DrawString(item->text);\r
+       fontcolor = F_BLACK;\r
+}\r
+\r
+#define MyLine(y)       VWB_Hlin(CtlPanelSX + 3,CtlPanelEX - 3,y,12);\r
+\r
+static void\r
+USL_DrawBottom(void)\r
+{\r
+       word    w,h;\r
+\r
+       fontcolor = NohiliteColor;\r
+\r
+       px = CtlPanelSX + 4;\r
+       py = CtlPanelEY - 15;\r
+       USL_DrawString(BottomS1);\r
+\r
+       USL_MeasureString(BottomS2,&w,&h);\r
+       px = CtlPanelEX - 4 - w;\r
+       USL_DrawString(BottomS2);\r
+\r
+       USL_MeasureString(BottomS3,&w,&h);\r
+       px = CtlPanelSX + ((CtlPanelEX - CtlPanelSX - w) / 2);\r
+       py += h + 1;\r
+       USL_DrawString(BottomS3);\r
+\r
+       fontcolor = F_WHITE;\r
+       MyLine(CtlPanelEY - 17);\r
+}\r
+\r
+static void\r
+USL_DrawCtlPanelContents(void)\r
+{\r
+       int                             x,y;\r
+       UserItem                far *item;\r
+\r
+       if (topcard->custom && topcard->custom(uic_DrawCard,nil))\r
+               return;\r
+\r
+       if (topcard->title)\r
+       {\r
+               // Draw the title\r
+               MyLine(CtlPanelSY + 7);\r
+               VWB_DrawPic(CtlPanelSX + 6,CtlPanelSY,topcard->title);\r
+       }\r
+\r
+       USL_DrawBottom();\r
+\r
+       if (!topcard->items)\r
+               return;\r
+\r
+       x = topcard->x + CtlPanelSX;\r
+       if (x % 8)\r
+               x += 8 - (x % 8);\r
+       y = topcard->y + CtlPanelSY + 12;\r
+       for (item = topcard->items;item->type != uii_Bad;item++)\r
+       {\r
+               if (item->flags & ui_Separated)\r
+                       y += 8;\r
+\r
+               item->x = x;\r
+               item->y = y;\r
+               USL_DrawItem(item);\r
+               y += 8;\r
+       }\r
+       if (topcard->custom)\r
+               topcard->custom(uic_TouchupCard,nil);\r
+}\r
+\r
+static void\r
+USL_DrawCtlPanel(void)\r
+{\r
+       if (topcard->items || topcard->title)\r
+       {\r
+               // Draw the backdrop\r
+               VWB_DrawPic(0,0,CP_MENUSCREENPIC);\r
+\r
+               // Draw the contents\r
+               USL_DrawCtlPanelContents();\r
+       }\r
+\r
+       // Refresh the screen\r
+       VW_UpdateScreen();\r
+}\r
+\r
+static void\r
+USL_DialogSetup(word w,word h,word *x,word *y)\r
+{\r
+       VWB_DrawMPic(CtlPanelSX,CtlPanelSY,CP_MENUMASKPICM);\r
+\r
+       *x = CtlPanelSX + ((CtlPanelW - w) / 2);\r
+       *y = CtlPanelSY + ((CtlPanelH - h) / 2);\r
+       VWB_Bar(*x,*y,w + 1,h + 1,BackColor);\r
+       VWB_Hlin(*x - 1,*x + w + 1,*y - 1,NohiliteColor);\r
+       VWB_Hlin(*x - 1,*x + w + 1,*y + h + 1,NohiliteColor);\r
+       VWB_Vlin(*y - 1,*y + h + 1,*x - 1,NohiliteColor);\r
+       VWB_Vlin(*y - 1,*y + h + 1,*x + w + 1,NohiliteColor);\r
+}\r
+\r
+static void\r
+USL_ShowLoadSave(char *s,char *name)\r
+{\r
+       word    x,y,\r
+                       w,h,\r
+                       tw,sw;\r
+       char    msg[MaxGameName + 4];\r
+\r
+       strcpy(msg,"'");\r
+       strcat(msg,name);\r
+       strcat(msg,"'");\r
+       USL_MeasureString(s,&sw,&h);\r
+       USL_MeasureString(msg,&w,&h);\r
+       tw = ((sw > w)? sw : w) + 6;\r
+       USL_DialogSetup(tw,(h * 2) + 2,&x,&y);\r
+       py = y + 2;\r
+       px = x + ((tw - sw) / 2);\r
+       USL_DrawString(s);\r
+       py += h;\r
+       px = x + ((tw - w) / 2);\r
+       USL_DrawString(msg);\r
+\r
+       VW_UpdateScreen();\r
+\r
+       IN_UserInput(100, true);\r
+}\r
+\r
+static boolean\r
+USL_CtlDialog(char *s1,char *s2,char *s3)\r
+{\r
+       word            w,h,sh,\r
+                               w1,w2,w3,\r
+                               x,y;\r
+       ScanCode        c;\r
+       CursorInfo      cursorinfo;\r
+\r
+       USL_MeasureString(s1,&w1,&h);\r
+       USL_MeasureString(s2,&w2,&h);\r
+       if (s3)\r
+               USL_MeasureString(s3,&w3,&h);\r
+       else\r
+               w3 = 0;\r
+       w = (w1 > w2)? ((w1 > w3)? w1 : w3) : ((w2 > w3)? w2 : w3);\r
+       w += 7;\r
+       sh = h;\r
+       h *= s3? 5 : 4;\r
+\r
+       USL_DialogSetup(w,h,&x,&y);\r
+\r
+       fontcolor = HiliteColor;\r
+       px = x + ((w - w1) / 2);\r
+       py = y + sh + 1;\r
+       USL_DrawString(s1);\r
+       py += (sh * 2) - 1;\r
+\r
+       VWB_Hlin(x + 3,x + w - 3,py,NohiliteColor);\r
+       py += 2;\r
+\r
+       fontcolor = NohiliteColor;\r
+       px = x + ((w - w2) / 2);\r
+       USL_DrawString(s2);\r
+       py += sh;\r
+\r
+       if (s3)\r
+       {\r
+               px = x + ((w - w3) / 2);\r
+               USL_DrawString(s3);\r
+       }\r
+\r
+       VW_UpdateScreen();\r
+\r
+       IN_ClearKeysDown();\r
+       do\r
+       {\r
+               IN_ReadCursor(&cursorinfo);\r
+               if (cursorinfo.button0)\r
+                       c = sc_Y;\r
+               else if (cursorinfo.button1)\r
+                       c = sc_Escape;\r
+               else\r
+                       c = LastScan;\r
+       } while (c == sc_None);\r
+       do\r
+       {\r
+               IN_ReadCursor(&cursorinfo);\r
+       } while (cursorinfo.button0 || cursorinfo.button1);\r
+\r
+       IN_ClearKeysDown();\r
+       USL_DrawCtlPanel();\r
+       return(c == sc_Y);\r
+}\r
+\r
+static boolean\r
+USL_ConfirmComm(UComm comm)\r
+{\r
+       boolean confirm,dialog;\r
+       char    *s1,*s2,*s3;\r
+\r
+       if (!comm)\r
+               Quit("USL_ConfirmComm() - empty comm");\r
+\r
+       confirm = true;\r
+       dialog = false;\r
+       s3 = "ESC TO BACK OUT";\r
+       switch (comm)\r
+       {\r
+       case uc_Abort:\r
+               s1 = "REALLY END CURRENT GAME?";\r
+               s2 = "PRESS Y TO END IT";\r
+               if (ingame && GameIsDirty)\r
+                       dialog = true;\r
+               break;\r
+       case uc_Quit:\r
+               s1 = "REALLY QUIT?";\r
+               s2 = "PRESS Y TO QUIT";\r
+               dialog = true;\r
+               break;\r
+       case uc_Loaded:\r
+               s1 = "YOU'RE IN A GAME";\r
+               s2 = "PRESS Y TO LOAD GAME";\r
+               if (ingame && GameIsDirty)\r
+                       dialog = true;\r
+               break;\r
+       case uc_SEasy:\r
+       case uc_SNormal:\r
+       case uc_SHard:\r
+               s1 = "YOU'RE IN A GAME";\r
+               s2 = "PRESS Y FOR NEW GAME";\r
+               if (ingame && GameIsDirty)\r
+                       dialog = true;\r
+               break;\r
+       }\r
+\r
+       confirm = dialog? USL_CtlDialog(s1,s2,s3) : true;\r
+       if (confirm)\r
+       {\r
+               Communication = comm;\r
+               CtlPanelDone = true;\r
+       }\r
+       return(confirm);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_HandleError() - Handles telling the user that there's been an error\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_HandleError(int num)\r
+{\r
+       char    buf[64];\r
+\r
+       strcpy(buf,"Error: ");\r
+       if (num < 0)\r
+               strcat(buf,"Unknown");\r
+       else if (num == ENOMEM)\r
+               strcat(buf,"Disk is Full");\r
+       else if (num == EINVFMT)\r
+               strcat(buf,"File is Incomplete");\r
+       else\r
+               strcat(buf,sys_errlist[num]);\r
+\r
+       VW_HideCursor();\r
+\r
+       USL_CtlDialog(buf,"PRESS ANY KEY",nil);\r
+       VW_UpdateScreen();\r
+\r
+       IN_ClearKeysDown();\r
+       IN_Ack();\r
+\r
+       VW_ShowCursor();\r
+       VW_UpdateScreen();\r
+}\r
+\r
+//      Custom routines\r
+#if 0\r
+static boolean\r
+USL_GenericCustom(UserCall call,UserItem far *item)\r
+{\r
+       boolean result;\r
+\r
+       result = false;\r
+       switch (call)\r
+       {\r
+       }\r
+       return(result);\r
+}\r
+#endif\r
+\r
+static void\r
+USL_SetOptionsText(void)\r
+{\r
+       optionsi[0].text = showscorebox? "SCORE BOX (ON)" : "SCORE BOX (OFF)";\r
+       optionsi[1].text = compatability? "SVGA COMPATIBILITY (ON)" : "SVGA COMPATIBILITY (OFF)";\r
+#ifdef KEEN\r
+       optionsi[2].text = oldshooting? "TWO-BUTTON FIRING (ON)" : "TWO-BUTTON FIRING (OFF)";\r
+\r
+       keybi[2].flags &= ~ui_Disabled;\r
+       if (oldshooting)\r
+               keybi[2].flags |= ui_Disabled;\r
+#endif\r
+}\r
+\r
+#pragma argsused\r
+static boolean\r
+USL_ScoreCustom(UserCall call,UserItem far *item)\r
+{\r
+       if (call != uic_SetupCard)\r
+               return(false);\r
+\r
+       showscorebox ^= true;\r
+       USL_CtlDialog(showscorebox? "Score box now on" : "Score box now off",\r
+                                       "Press any key",nil);\r
+       USL_SetOptionsText();\r
+       return(true);\r
+}\r
+\r
+#pragma argsused\r
+static boolean\r
+USL_CompCustom(UserCall call,UserItem far *item)\r
+{\r
+       if (call != uic_SetupCard)\r
+               return(false);\r
+\r
+       compatability ^= true;\r
+       USL_CtlDialog(compatability? "SVGA compatibility now on" : "SVGA compatibility now off",\r
+                                       "Press any key",nil);\r
+       USL_SetOptionsText();\r
+       return(true);\r
+}\r
+\r
+#ifdef  KEEN\r
+#pragma argsused\r
+static boolean\r
+USL_TwoCustom(UserCall call,UserItem far *item)\r
+{\r
+       if (call != uic_SetupCard)\r
+               return(false);\r
+\r
+       oldshooting ^= true;\r
+       USL_CtlDialog(oldshooting? "Two-button firing now on" : "Two-button firing now off",\r
+                                       "Press any key",nil);\r
+       USL_SetOptionsText();\r
+       return(true);\r
+}\r
+#endif\r
+\r
+static boolean\r
+USL_ConfigCustom(UserCall call,UserItem far *item)\r
+{\r
+static  char    *CtlNames[] = {"KEYBOARD","KEYBOARD","JOYSTICK #1","JOYSTICK #2","MOUSE"};\r
+               char    *s;\r
+               word    w,h,\r
+                               tw;\r
+\r
+       if (call == uic_TouchupCard)\r
+       {\r
+               s = "CONTROL: ";\r
+               USL_MeasureString(s,&w,&h);\r
+               tw = w;\r
+               USL_MeasureString(CtlNames[Controls[0]],&w,&h);\r
+               tw += w;\r
+               py = CtlPanelEY - 18 - h;\r
+               px = CtlPanelSX + ((CtlPanelW - tw) / 2);\r
+               fontcolor = NohiliteColor;\r
+               USL_DrawString(s);\r
+               USL_DrawString(CtlNames[Controls[0]]);\r
+       }\r
+       item++; // Shut the compiler up\r
+       return(false);\r
+}\r
+\r
+static void\r
+USL_CKSetKey(UserItem far *item,word i)\r
+{\r
+       boolean         on;\r
+       word            j;\r
+       ScanCode        scan;\r
+       longword        time;\r
+       CursorInfo      cursorinfo;\r
+\r
+       on = false;\r
+       time = 0;\r
+       LastScan = sc_None;\r
+       fontcolor = HiliteColor;\r
+       do\r
+       {\r
+               if (TimeCount >= time)\r
+               {\r
+                       on ^= true;\r
+                       VWB_Bar(item->x + 90,item->y,40,8,fontcolor ^ BackColor);\r
+                       VWB_Bar(item->x + 90 + 1,item->y + 1,40 - 2,8 - 2,BackColor);\r
+                       if (on)\r
+                               VWB_DrawTile8(item->x + 90 + 16,item->y,TileBase + 8);\r
+                       VW_UpdateScreen();\r
+\r
+                       time = TimeCount + (TickBase / 2);\r
+               }\r
+\r
+               IN_ReadCursor(&cursorinfo);\r
+               while (cursorinfo.button0 || cursorinfo.button1)\r
+               {\r
+                       IN_ReadCursor(&cursorinfo);\r
+                       LastScan = sc_Escape;\r
+               }\r
+\r
+       asm     pushf\r
+       asm     cli\r
+               if (LastScan == sc_LShift)\r
+                       LastScan = sc_None;\r
+       asm     popf\r
+       } while (!(scan = LastScan));\r
+\r
+       if (scan != sc_Escape)\r
+       {\r
+               for (j = 0,on = false;j < 11;j++)\r
+               {\r
+                       if (j == i)\r
+                               continue;\r
+                       if (*(KeyMaps[j]) == scan)\r
+                       {\r
+                               on = true;\r
+                               break;\r
+                       }\r
+               }\r
+               if (on)\r
+                       USL_CtlDialog("Key already used","Press a key",nil);\r
+               else\r
+                       *(KeyMaps[i]) = scan;\r
+       }\r
+       IN_ClearKeysDown();\r
+}\r
+\r
+#pragma argsused\r
+static boolean\r
+USL_KeySCustom(UserCall call,UserItem far *item)\r
+{\r
+       if (call == uic_SetupCard)\r
+               Controls[0] = ctrl_Keyboard;\r
+       return(false);\r
+}\r
+\r
+#pragma argsused\r
+static boolean\r
+USL_KeyCustom(UserCall call,UserItem far *item)\r
+{\r
+       boolean result;\r
+       word    i;\r
+\r
+       result = false;\r
+       i = (topcard == &keygroup)? (3 + (item - keyi)) : (item - keybi);\r
+       switch (call)\r
+       {\r
+       case uic_SetupCard:\r
+               Controls[0] = ctrl_Keyboard;\r
+               break;\r
+       case uic_Draw:\r
+               VWB_Bar(CtlPanelSX + 1,item->y,\r
+                               CtlPanelEX - CtlPanelSX - 1,8,BackColor);       // Clear out background\r
+               USL_DrawItemIcon(item);\r
+               fontcolor = (item->flags & ui_Selected)? HiliteColor : NohiliteColor;\r
+               px = item->x + 8;\r
+               py = item->y + 1;\r
+               USL_DrawString(item->text);\r
+               VWB_Bar(item->x + 90,item->y,40,8,fontcolor ^ BackColor);\r
+               VWB_Bar(item->x + 90 + 1,item->y + 1,40 - 2,8 - 2,BackColor);\r
+               px = item->x + 90 + 6;\r
+               py = item->y + 1;\r
+               USL_DrawString(IN_GetScanName(*KeyMaps[i]));\r
+               result = true;\r
+               break;\r
+       case uic_Hit:\r
+               USL_KeyCustom(uic_Draw,item);\r
+               USL_CKSetKey(item,i);\r
+               USL_DrawCtlPanel();\r
+               result = true;\r
+               break;\r
+       }\r
+       return(result);\r
+}\r
+\r
+static void\r
+USL_CJDraw(char *s1,char *s2)\r
+{\r
+       word    w,h;\r
+\r
+       USL_MeasureString(s1,&w,&h);\r
+       px = CtlPanelSX + ((CtlPanelW - w) / 2);\r
+       py = CtlPanelEY - 34;\r
+       VWB_Bar(CtlPanelSX + 1,py,CtlPanelW - 2,h * 2,BackColor);\r
+       fontcolor = HiliteColor;\r
+       USL_DrawString(s1);\r
+       py += h;\r
+       USL_MeasureString(s2,&w,&h);\r
+       px = CtlPanelSX + ((CtlPanelW - w) / 2);\r
+       USL_DrawString(s2);\r
+}\r
+\r
+static boolean\r
+USL_CJGet(word joy,word button,word x,word y,word *xaxis,word *yaxis)\r
+{\r
+       boolean         on;\r
+       longword        time;\r
+\r
+       while (IN_GetJoyButtonsDB(joy))\r
+               if (LastScan == sc_Escape)\r
+                       return(false);\r
+\r
+       on = false;\r
+       time = 0;\r
+       while (!(IN_GetJoyButtonsDB(joy) & (1 << button)))\r
+       {\r
+               if (TimeCount >= time)\r
+               {\r
+                       on ^= true;\r
+                       time = TimeCount + (TickBase / 2);\r
+                       VWB_DrawTile8(x,y,TileBase + on);\r
+                       VW_UpdateScreen();\r
+               }\r
+\r
+               if (LastScan == sc_Escape)\r
+                       return(false);\r
+       }\r
+       IN_GetJoyAbs(joy,xaxis,yaxis);\r
+       return(true);\r
+}\r
+\r
+static boolean\r
+USL_ConfigJoystick(word joy)\r
+{\r
+       word    x,y,\r
+                       minx,miny,\r
+                       maxx,maxy;\r
+\r
+       BottomS1 = BottomS2 = "";\r
+       BottomS3 = "Esc to back out";\r
+       USL_DrawCtlPanel();\r
+       x = CtlPanelSX + 60;\r
+       y = CtlPanelSY + 19;\r
+       VWB_DrawPic(x,y,CP_JOYSTICKPIC);\r
+\r
+       USL_CJDraw("Move Joystick to upper left","and press button #1");\r
+       VWB_DrawTile8(x + 24,y + 8,TileBase + 6);\r
+       VWB_DrawTile8(x + 8,y + 8,TileBase + 1);\r
+       VWB_DrawTile8(x + 8,y + 24,TileBase + 0);\r
+       VW_UpdateScreen();\r
+       if (!USL_CJGet(joy,0,x + 8,y + 8,&minx,&miny))\r
+               return(false);\r
+\r
+       USL_CJDraw("Move Joystick to lower right","and press button #2");\r
+       VWB_DrawTile8(x + 24,y + 8,TileBase - 25);\r
+       VWB_DrawTile8(x + 40,y + 24,TileBase + 7);\r
+       VWB_DrawTile8(x + 8,y + 8,TileBase + 0);\r
+       VWB_DrawTile8(x + 8,y + 24,TileBase + 1);\r
+       VW_UpdateScreen();\r
+       if (!USL_CJGet(joy,1,x + 8,y + 24,&maxx,&maxy))\r
+               return(false);\r
+\r
+       while (IN_GetJoyButtonsDB(joy))\r
+               ;\r
+\r
+       IN_SetupJoy(joy,minx,maxx,miny,maxy);\r
+       return(true);\r
+}\r
+\r
+#pragma argsused\r
+static boolean\r
+USL_Joy1Custom(UserCall call,UserItem far *item)\r
+{\r
+       if (call == uic_SetupCard)\r
+       {\r
+               if (USL_ConfigJoystick(0))\r
+               {\r
+                       Controls[0] = ctrl_Joystick1;\r
+                       USL_CtlDialog("USING JOYSTICK #1","PRESS ANY KEY",nil);\r
+               }\r
+               return(true);\r
+       }\r
+       else\r
+               return(false);\r
+}\r
+\r
+#pragma argsused\r
+static boolean\r
+USL_Joy2Custom(UserCall call,UserItem far *item)\r
+{\r
+       if (call == uic_SetupCard)\r
+       {\r
+               if (USL_ConfigJoystick(1))\r
+               {\r
+                       Controls[0] = ctrl_Joystick2;\r
+                       USL_CtlDialog("USING JOYSTICK #2","PRESS ANY KEY",nil);\r
+               }\r
+               return(true);\r
+       }\r
+       else\r
+               return(false);\r
+}\r
+\r
+static void\r
+USL_DrawFileIcon(UserItem far *item)\r
+{\r
+       word    color;\r
+\r
+       item->y = topcard->y + CtlPanelSY + 12;\r
+       item->y += (item - loadsavegamei) * 11;\r
+\r
+       fontcolor = (item->flags & ui_Selected)? HiliteColor : NohiliteColor;\r
+       color = fontcolor ^ BackColor;  // Blech!\r
+       VWB_Hlin(item->x,item->x + (CtlPanelW - 12),item->y,color);\r
+       VWB_Hlin(item->x,item->x + (CtlPanelW - 12),item->y + 9,color);\r
+       VWB_Vlin(item->y,item->y + 9,item->x,color);\r
+       VWB_Vlin(item->y,item->y + 9,item->x + (CtlPanelW - 12),color);\r
+}\r
+\r
+static void\r
+USL_DoLoadGame(UserItem far *item)\r
+{\r
+       char            *filename;\r
+       word            n,\r
+                               err;\r
+       int                     file;\r
+       SaveGame        *game;\r
+\r
+       if (!USL_ConfirmComm(uc_Loaded))\r
+               return;\r
+\r
+       n = item - loadsavegamei;\r
+       game = &Games[n];\r
+\r
+       USL_ShowLoadSave("Loading",game->name);\r
+\r
+       err = 0;\r
+       filename = USL_GiveSaveName(n);\r
+       if ((file = open(filename,O_BINARY | O_RDONLY)) != -1)\r
+       {\r
+               if (read(file,game,sizeof(*game)) == sizeof(*game))\r
+               {\r
+                       if (USL_LoadGame)\r
+                               if (!USL_LoadGame(file))\r
+                                       USL_HandleError(err = errno);\r
+               }\r
+               else\r
+                       USL_HandleError(err = errno);\r
+               close(file);\r
+       }\r
+       else\r
+               USL_HandleError(err = errno);\r
+       if (err)\r
+       {\r
+               abortgame = true;\r
+               Communication = uc_None;\r
+               CtlPanelDone = false;\r
+       }\r
+       else\r
+               loadedgame = true;\r
+       game->present = true;\r
+\r
+       if (loadedgame)\r
+               Paused = true;\r
+\r
+       USL_DrawCtlPanel();\r
+}\r
+\r
+static boolean\r
+USL_LoadCustom(UserCall call,UserItem far *item)\r
+{\r
+       boolean result;\r
+       word    i;\r
+\r
+       result = false;\r
+       switch (call)\r
+       {\r
+       case uic_SetupCard:\r
+               for (i = 0;i < MaxSaveGames;i++)\r
+               {\r
+                       if (Games[i].present)\r
+                               loadsavegamei[i].flags &= ~ui_Disabled;\r
+                       else\r
+                               loadsavegamei[i].flags |= ui_Disabled;\r
+               }\r
+               break;\r
+       case uic_DrawIcon:\r
+               USL_DrawFileIcon(item);\r
+               result = true;\r
+               break;\r
+       case uic_Draw:\r
+               USL_DrawFileIcon(item);\r
+               VWB_Bar(item->x + 1,item->y + 2,CtlPanelW - 12 - 2,7,BackColor);\r
+\r
+               i = item - loadsavegamei;\r
+               if (Games[i].present)\r
+                       px = item->x + 2;\r
+               else\r
+                       px = item->x + 60;\r
+               py = item->y + 2;\r
+               USL_DrawString(Games[i].present? Games[i].name : "Empty");\r
+               result = true;\r
+               break;\r
+       case uic_Hit:\r
+               USL_DoLoadGame(item);\r
+               result = true;\r
+               break;\r
+       }\r
+       return(result);\r
+}\r
+\r
+static void\r
+USL_DoSaveGame(UserItem far *item)\r
+{\r
+       boolean         ok;\r
+       char            *filename;\r
+       word            n,err;\r
+       int         file;\r
+       SaveGame        *game;\r
+\r
+       BottomS1 = "Type name";\r
+       BottomS2 = "Enter accepts";\r
+       USL_DrawCtlPanel();\r
+\r
+       n = item - loadsavegamei;\r
+       game = &Games[n];\r
+       fontcolor = HiliteColor;\r
+       VWB_Bar(item->x + 1,item->y + 2,CtlPanelW - 12 - 2,7,BackColor);\r
+       game->oldtest = &PrintX;\r
+       ok = US_LineInput(item->x + 2,item->y + 2,\r
+                                               game->name,game->present? game->name : nil,\r
+                                               true,MaxGameName,\r
+                                               CtlPanelW - 22);\r
+       if (!strlen(game->name))\r
+               strcpy(game->name,"Untitled");\r
+       if (ok)\r
+       {\r
+               USL_ShowLoadSave("Saving",game->name);\r
+\r
+               filename = USL_GiveSaveName(n);\r
+               err = 0;\r
+               file = open(filename,O_CREAT | O_BINARY | O_WRONLY,\r
+                                       S_IREAD | S_IWRITE | S_IFREG);\r
+               if (file != -1)\r
+               {\r
+                       if (write(file,game,sizeof(*game)) == sizeof(*game))\r
+                       {\r
+                               if (USL_SaveGame)\r
+                                       ok = USL_SaveGame(file);\r
+                               if (!ok)\r
+                                       USL_HandleError(err = errno);\r
+                       }\r
+                       else\r
+                               USL_HandleError(err = ((errno == ENOENT)? ENOMEM : errno));\r
+                       close(file);\r
+               }\r
+               else\r
+                       USL_HandleError(err = ((errno == ENOENT)? ENOMEM : errno));\r
+               if (err)\r
+               {\r
+                       remove(filename);\r
+                       ok = false;\r
+               }\r
+\r
+       }\r
+\r
+       if (!game->present)\r
+               game->present = ok;\r
+\r
+       if (ok)\r
+               GameIsDirty = false;\r
+       USL_SetupCard();\r
+}\r
+\r
+static boolean\r
+USL_SaveCustom(UserCall call,UserItem far *item)\r
+{\r
+       word    i;\r
+\r
+       switch (call)\r
+       {\r
+       case uic_SetupCard:\r
+               for (i = 0;i < MaxSaveGames;i++)\r
+                       loadsavegamei[i].flags &= ~ui_Disabled;\r
+               return(false);\r
+       case uic_Hit:\r
+               USL_DoSaveGame(item);\r
+               return(true);\r
+//              break;\r
+       }\r
+       return(USL_LoadCustom(call,item));\r
+}\r
+\r
+#if 0\r
+\r
+#define PaddleMinX      (CtlPanelSX + 3)\r
+#define PaddleMaxX      (CtlPanelEX - 15)\r
+#define BallMinX        (CtlPanelSX + 2)\r
+#define BallMinY        (CtlPanelSY + 12 + 2)\r
+#define BallMaxX        (CtlPanelEX - 6)\r
+#define BallMaxY        (CtlPanelEY - 13)\r
+#define CPaddleY        (BallMinY + 4)\r
+#define KPaddleY        (BallMaxY - 2)\r
+void\r
+USL_DrawPongScore(word k,word c)\r
+{\r
+       fontcolor = HiliteColor;\r
+       PrintY = py = CtlPanelSY + 4;\r
+       px = CtlPanelSX + 6;\r
+       VWB_Bar(px,py,42,6,BackColor);\r
+       USL_DrawString("YOU:");\r
+       PrintX = px;\r
+       US_PrintUnsigned(k);\r
+       px = CtlPanelSX + 108;\r
+       VWB_Bar(px,py,50,6,BackColor);\r
+       USL_DrawString("COMP:");\r
+       PrintX = px;\r
+       US_PrintUnsigned(c);\r
+}\r
+\r
+void\r
+USL_PlayPong(void)\r
+{\r
+       boolean         ball,killball,revdir,done,lastscore;\r
+       word            cycle,\r
+                               x,y,\r
+                               kx,cx,\r
+                               rx,\r
+                               bx,by,\r
+                               kscore,cscore,\r
+                               speedup;\r
+       int                     bdx,bdy;\r
+       longword        balltime,waittime;\r
+       CursorInfo      cursorinfo;\r
+\r
+       kx = cx = PaddleMinX + ((PaddleMaxX - PaddleMinX) / 2);\r
+       bx = by = bdx = bdy = 0;\r
+       kscore = cscore = 0;\r
+       USL_DrawPongScore(0,0);\r
+       cycle = 0;\r
+       revdir = false;\r
+       killball = true;\r
+       done = false;\r
+       lastscore = false;\r
+       do\r
+       {\r
+               waittime = TimeCount;\r
+\r
+               IN_ReadCursor(&cursorinfo);\r
+               if (((cursorinfo.x < 0) || IN_KeyDown(sc_LeftArrow)) && (kx > PaddleMinX))\r
+                       kx -= 2;\r
+               else if (((cursorinfo.x > 0) || IN_KeyDown(sc_RightArrow)) && (kx < PaddleMaxX))\r
+                       kx += 2;\r
+\r
+               if (killball)\r
+               {\r
+                       ball = false;\r
+                       balltime = TimeCount + TickBase;\r
+                       speedup = 10;\r
+                       killball = false;\r
+               }\r
+\r
+               if (ball && (cycle++ % 3))\r
+               {\r
+                       x = (bx >> 2);\r
+                       if (!(x & 1))\r
+                               x += (US_RndT() & 1);\r
+\r
+                       if ((cx + 6 < x) && (cx < PaddleMaxX))\r
+                               cx += 1;\r
+                       else if ((cx + 6 > x) && (cx > PaddleMinX))\r
+                               cx -= 1;\r
+               }\r
+\r
+               VWB_Bar(BallMinX,BallMinY - 1,\r
+                               BallMaxX - BallMinX + 5,BallMaxY - BallMinY + 7,\r
+                               BackColor);\r
+               VWB_DrawSprite(cx,CPaddleY,PADDLESPR);\r
+               VWB_DrawSprite(kx,KPaddleY,PADDLESPR);\r
+               if (ball)\r
+               {\r
+                       if\r
+                       (\r
+                               (((bx + bdx) >> 2) > BallMaxX)\r
+                       ||      (((bx + bdx) >> 2) < BallMinX)\r
+                       )\r
+                       {\r
+                               SD_PlaySound(BALLBOUNCESND);\r
+                               bdx = -bdx;\r
+                       }\r
+                       bx += bdx;\r
+\r
+                       if (((by + bdy) >> 2) > BallMaxY)\r
+                       {\r
+                               killball = true;\r
+                               lastscore = false;\r
+                               cscore++;\r
+                               SD_PlaySound(COMPSCOREDSND);\r
+                               USL_DrawPongScore(kscore,cscore);\r
+                               if (cscore == 21)\r
+                               {\r
+                                       USL_CtlDialog("You lost!","Press any key",nil);\r
+                                       done = true;\r
+                                       continue;\r
+                               }\r
+                       }\r
+                       else if (((by + bdy) >> 2) < BallMinY)\r
+                       {\r
+                               killball = true;\r
+                               lastscore = true;\r
+                               kscore++;\r
+                               SD_PlaySound(KEENSCOREDSND);\r
+                               USL_DrawPongScore(kscore,cscore);\r
+                               if (kscore == 21)\r
+                               {\r
+                                       USL_CtlDialog("You won!","Press any key",nil);\r
+                                       done = true;\r
+                                       continue;\r
+                               }\r
+                       }\r
+                       by += bdy;\r
+\r
+                       x = bx >> 2;\r
+                       y = by >> 2;\r
+                       if (!killball)\r
+                       {\r
+                               if\r
+                               (\r
+                                       (bdy < 0)\r
+                               &&      ((y >= CPaddleY) && (y < CPaddleY + 3))\r
+                               &&      ((x >= (cx - 5)) && (x < (cx + 11)))\r
+                               )\r
+                               {\r
+                                       rx = cx;\r
+                                       revdir = true;\r
+                                       SD_PlaySound(COMPPADDLESND);\r
+                               }\r
+                               else if\r
+                               (\r
+                                       (bdy > 0)\r
+                               &&      ((y >= (KPaddleY - 3)) && (y < KPaddleY))\r
+                               &&      ((x >= (kx - 5)) && (x < (kx + 11)))\r
+                               )\r
+                               {\r
+                                       if (((bdy >> 2) < 3) && !(--speedup))\r
+                                       {\r
+                                               bdy++;\r
+                                               speedup = 10;\r
+                                       }\r
+                                       rx = kx;\r
+                                       revdir = true;\r
+                                       SD_PlaySound(KEENPADDLESND);\r
+                               }\r
+                               if (revdir)\r
+                               {\r
+                                       bdy = -bdy;\r
+                                       bdx = ((x + 5 - rx) >> 1) - (1 << 2);\r
+                                       if (!bdx)\r
+                                               bdx--;\r
+                                       revdir = false;\r
+                               }\r
+                       }\r
+                       VWB_DrawSprite(x,y,(x & 1)? BALL1PIXELTOTHERIGHTSPR : BALLSPR);\r
+               }\r
+               else if (TimeCount >= balltime)\r
+               {\r
+                       ball = true;\r
+                       bdx = 1 - (US_RndT() % 3);\r
+                       bdy = 2;\r
+                       if (lastscore)\r
+                               bdy = -bdy;\r
+                       bx = (BallMinX + ((BallMaxX - BallMinX) / 2)) << 2;\r
+                       by = (BallMinY + ((BallMaxY - BallMinY) / 2)) << 2;\r
+               }\r
+               VW_UpdateScreen();\r
+               while (waittime == TimeCount)\r
+                       ;       // DEBUG - do adaptiveness\r
+       } while ((LastScan != sc_Escape) && !done);\r
+       IN_ClearKeysDown();\r
+}\r
+\r
+#pragma argsused\r
+static boolean\r
+USL_PongCustom(UserCall call,struct UserItem far *item)\r
+{\r
+       if (call != uic_SetupCard)\r
+               return(false);\r
+\r
+       VWB_DrawPic(0,0,CP_MENUSCREENPIC);\r
+       VWB_DrawPic(CtlPanelSX + 56,CtlPanelSY,CP_PADDLEWARPIC);\r
+       VWB_Hlin(CtlPanelSX + 3,CtlPanelEX - 3,CtlPanelSY + 12,HiliteColor ^ BackColor);\r
+       VWB_Hlin(CtlPanelSX + 3,CtlPanelEX - 3,CtlPanelEY - 7,HiliteColor ^ BackColor);\r
+       USL_PlayPong();\r
+\r
+       return(true);\r
+}\r
+\r
+#endif\r
+\r
+//      Flag management stuff\r
+static void\r
+USL_ClearFlags(UserItemGroup far *node)\r
+{\r
+       UserItem        far *i;\r
+\r
+       if (!node->items)\r
+               return;\r
+\r
+       for (i = node->items;i->type != uii_Bad;i++)\r
+       {\r
+               i->flags &= ~UISelectFlags;\r
+               if (i->child)\r
+                       USL_ClearFlags((UserItemGroup far *)i->child);\r
+       }\r
+}\r
+\r
+static int\r
+USL_FindPushedItem(UserItemGroup far *group)\r
+{\r
+       word            i;\r
+       UserItem        far *item;\r
+\r
+       for (item = group->items,i = 0;item->type != uii_Bad;item++,i++)\r
+               if (item->flags & ui_Pushed)\r
+                       return(i);\r
+       return(-1);\r
+}\r
+\r
+static void\r
+USL_SelectItem(UserItemGroup far *group,word index,boolean draw)\r
+{\r
+       UserItem        far *item;\r
+\r
+       if (index != group->cursor)\r
+       {\r
+               item = &group->items[group->cursor];\r
+               item->flags &= ~ui_Selected;\r
+               if (draw)\r
+                       USL_DrawItem(item);\r
+       }\r
+\r
+       group->cursor = index;\r
+       item = &group->items[group->cursor];\r
+       group->items[group->cursor].flags |= ui_Selected;\r
+       if (draw)\r
+               USL_DrawItem(item);\r
+}\r
+\r
+static void\r
+USL_PushItem(UserItemGroup far *group,word index,boolean draw)\r
+{\r
+       word            i;\r
+       UserItem        far *item;\r
+\r
+       USL_SelectItem(group,index,draw);\r
+       for (item = group->items,i = 0;item->type != uii_Bad;item++,i++)\r
+       {\r
+               if (item->type != uii_RadioButton)\r
+                       continue;\r
+\r
+               if (i == index)\r
+               {\r
+                       item->flags |= ui_Pushed;\r
+                       if (draw)\r
+                               USL_DrawItem(item);\r
+               }\r
+               else if (item->flags & ui_Pushed)\r
+               {\r
+                       item->flags &= ~ui_Pushed;\r
+                       if (draw)\r
+                               USL_DrawItem(item);\r
+               }\r
+       }\r
+}\r
+\r
+static void\r
+USL_NextItem(void)\r
+{\r
+       if (topcard->items[topcard->cursor + 1].type == uii_Bad)\r
+               return;\r
+       USL_SelectItem(topcard,topcard->cursor + 1,true);\r
+}\r
+\r
+static void\r
+USL_PrevItem(void)\r
+{\r
+       if (!topcard->cursor)\r
+               return;\r
+       USL_SelectItem(topcard,topcard->cursor - 1,true);\r
+}\r
+\r
+static void\r
+USL_SetupCard(void)\r
+{\r
+       BottomS1 = "Arrows move";\r
+       BottomS2 = "Enter selects";\r
+       BottomS3 = cstackptr? "ESC to back out" : "ESC to quit";\r
+\r
+       USL_SelectItem(topcard,topcard->cursor,false);\r
+       USL_DrawCtlPanel();     // Contents?\r
+}\r
+\r
+static void\r
+USL_DownLevel(UserItemGroup far *group)\r
+{\r
+       if (!group)\r
+               Quit("USL_DownLevel() - nil card");\r
+       USL_PushCard(group);\r
+       if (group->custom && group->custom(uic_SetupCard,nil))\r
+               USL_PopCard();\r
+       USL_SetupCard();\r
+}\r
+\r
+static void\r
+USL_UpLevel(void)\r
+{\r
+       if (!cstackptr)\r
+       {\r
+               USL_ConfirmComm(uc_Quit);\r
+               return;\r
+       }\r
+\r
+       if (topcard->items)\r
+               topcard->items[topcard->cursor].flags &= ~ui_Selected;\r
+       USL_PopCard();\r
+       USL_SetupCard();\r
+}\r
+\r
+static void\r
+USL_DoItem(void)\r
+{\r
+       // DEBUG - finish this routine\r
+       UserItem                far *item;\r
+\r
+       item = &topcard->items[topcard->cursor];\r
+       if (item->flags & ui_Disabled)\r
+               SD_PlaySound(NOWAYSND);\r
+       else\r
+       {\r
+               switch (item->type)\r
+               {\r
+               case uii_Button:\r
+                       if (!(topcard->custom && topcard->custom(uic_Hit,item)))\r
+                               USL_ConfirmComm(item->comm);\r
+                       break;\r
+               case uii_RadioButton:\r
+                       USL_PushItem(topcard,topcard->cursor,true);\r
+                       break;\r
+               case uii_Folder:\r
+                       USL_DownLevel(item->child);\r
+                       break;\r
+               }\r
+       }\r
+}\r
+\r
+static void\r
+USL_SetControlValues(void)\r
+{\r
+       USL_PushItem(&soundgroup,SoundMode,false);\r
+       USL_PushItem(&musicgroup,MusicMode,false);\r
+       if (!AdLibPresent)\r
+       {\r
+               soundi[2].flags |= ui_Disabled; // AdLib sound effects\r
+               musici[1].flags |= ui_Disabled; // AdLib music\r
+       }\r
+\r
+       if (!JoysPresent[0])\r
+               configi[3].flags |= ui_Disabled;\r
+       if (!JoysPresent[1])\r
+               configi[4].flags |= ui_Disabled;\r
+\r
+       rooti[4].text = ingame? "RETURN TO GAME" : "RETURN TO DEMO";\r
+       if (!ingame)\r
+       {\r
+               rooti[2].flags |= ui_Disabled;  // Save Game\r
+               rooti[5].flags |= ui_Disabled;  // End Game\r
+       }\r
+       rootgroup.cursor = ingame? 4 : 0;\r
+       USL_SetOptionsText();\r
+       // DEBUG - write the rest of this\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_SetUpCtlPanel() - Sets the states of the UserItems to reflect the\r
+//              values of all the appropriate variables\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_SetUpCtlPanel(void)\r
+{\r
+       int     i;\r
+\r
+       // Cache in all of the stuff for the control panel\r
+       CA_UpLevel();\r
+       for (i = CONTROLS_LUMP_START;i <= CONTROLS_LUMP_END;i++)\r
+               CA_MarkGrChunk(i);\r
+//     for (i = PADDLE_LUMP_START;i <= PADDLE_LUMP_END;i++)\r
+//             CA_MarkGrChunk(i);\r
+       CA_MarkGrChunk(STARTFONT+1);            // Little font\r
+       CA_MarkGrChunk(CP_MENUMASKPICM);        // Mask for dialogs\r
+       CA_CacheMarks("Control Panel");\r
+       CA_LoadAllSounds();\r
+\r
+       // Do some other setup\r
+       fontnumber = 1;\r
+       US_SetPrintRoutines(VW_MeasurePropString,VWB_DrawPropString);\r
+       fontcolor = F_BLACK;\r
+       VW_Bar (0,0,320,200,3); // CAT3D patch\r
+       RF_FixOfs();\r
+       VW_InitDoubleBuffer();\r
+\r
+       Communication = uc_None;\r
+       USL_ClearFlags(&rootgroup);\r
+       USL_SetControlValues();\r
+       USL_SetupStack();\r
+       USL_SetupCard();\r
+\r
+       if (ingame)\r
+               GameIsDirty = true;\r
+\r
+       IN_ClearKeysDown();\r
+}\r
+\r
+static void\r
+USL_HandleComm(UComm comm)\r
+{\r
+       switch (comm)\r
+       {\r
+       case uc_Loaded:\r
+       case uc_Return:\r
+               break;\r
+       case uc_Abort:\r
+               abortgame = true;\r
+               break;\r
+       case uc_Quit:\r
+               QuitToDos = true;\r
+               break;\r
+       case uc_SEasy:\r
+               restartgame = gd_Easy;\r
+               break;\r
+       case uc_SNormal:\r
+               restartgame = gd_Normal;\r
+               break;\r
+       case uc_SHard:\r
+               restartgame = gd_Hard;\r
+               break;\r
+\r
+       default:\r
+               Quit("USL_HandleComm() - unknown");\r
+               break;\r
+       }\r
+}\r
+\r
+static void\r
+USL_GetControlValues(void)\r
+{\r
+       int     i;\r
+\r
+       // DEBUG - write the rest of this\r
+       i = USL_FindPushedItem(&soundgroup);\r
+       if (i != SoundMode)\r
+               SD_SetSoundMode(i);\r
+\r
+       i = USL_FindPushedItem(&musicgroup);\r
+       if (i != MusicMode)\r
+               SD_SetMusicMode(i);\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      USL_TearDownCtlPanel() - Given the state of the control panel, sets the\r
+//              modes and values as appropriate\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+static void\r
+USL_TearDownCtlPanel(void)\r
+{\r
+       USL_GetControlValues();\r
+       if (Communication)\r
+               USL_HandleComm(Communication);\r
+\r
+       fontnumber = 0; // Normal font\r
+       fontcolor = F_BLACK;\r
+       if (restartgame && USL_ResetGame)\r
+               USL_ResetGame();\r
+       else if (QuitToDos)\r
+       {\r
+               if (tedlevel)\r
+                       TEDDeath();\r
+               else\r
+               {\r
+                       US_CenterWindow(20,3);\r
+                       fontcolor = F_SECONDCOLOR;\r
+                       US_PrintCentered("Quitting...");\r
+                       fontcolor = F_BLACK;\r
+                       VW_UpdateScreen();\r
+                       Quit(nil);\r
+               }\r
+       }\r
+\r
+       IN_ClearKeysDown();\r
+       SD_WaitSoundDone();\r
+       VW_Bar (0,0,320,200,3); // CAT3D patch\r
+       CA_DownLevel();\r
+       CA_LoadAllSounds();\r
+}\r
+\r
+///////////////////////////////////////////////////////////////////////////\r
+//\r
+//      US_ControlPanel() - This is the main routine for the control panel\r
+//\r
+///////////////////////////////////////////////////////////////////////////\r
+#define MoveMin 40\r
+void\r
+US_ControlPanel(void)\r
+{\r
+extern void HelpScreens(void);\r
+\r
+       boolean         resetitem,on;\r
+       word            i;\r
+       int                     ydelta;\r
+       longword        flashtime;\r
+       UserItem        far *item;\r
+       CursorInfo      cursorinfo;\r
+\r
+#if 0\r
+       // DEBUG!!!\r
+       {\r
+               USL_SetUpCtlPanel();\r
+               Communication = uc_Loaded;\r
+               CtlPanelDone = true;\r
+               loadedgame = true;\r
+               USL_TearDownCtlPanel();\r
+               return;\r
+       }\r
+#endif\r
+\r
+       if ((LastScan < sc_F1) || (LastScan > sc_F10))\r
+               IN_ClearKeysDown();\r
+\r
+       USL_SetUpCtlPanel();\r
+       USL_DrawCtlPanel();\r
+\r
+       ydelta = 0;\r
+       for (CtlPanelDone = false,resetitem = on = true;!CtlPanelDone;)\r
+       {\r
+               item = &(topcard->items[topcard->cursor]);\r
+\r
+               if (resetitem)\r
+               {\r
+                       flashtime = TimeCount + (TickBase / 2);\r
+                       resetitem = false;\r
+               }\r
+\r
+               if (TimeCount >= flashtime)\r
+               {\r
+                       on ^= true;\r
+                       resetitem = true;\r
+                       if (!on)\r
+                               item->flags &= ~ui_Selected;\r
+                       USL_DrawItemIcon(item);\r
+                       item->flags |= ui_Selected;\r
+               }\r
+\r
+               VW_UpdateScreen();\r
+\r
+               if (LastScan)\r
+               {\r
+                       switch (LastScan)\r
+                       {\r
+                       case sc_UpArrow:\r
+                               USL_PrevItem();\r
+                               resetitem = true;\r
+                               break;\r
+                       case sc_DownArrow:\r
+                               USL_NextItem();\r
+                               resetitem = true;\r
+                               break;\r
+                       case sc_Return:\r
+                               USL_DoItem();\r
+                               resetitem = true;\r
+                               break;\r
+                       case sc_Escape:\r
+                               USL_UpLevel();\r
+                               resetitem = true;\r
+                               break;\r
+#ifndef KEEN6\r
+                       case sc_F1:\r
+                               HelpScreens();\r
+                               USL_DrawCtlPanel();\r
+                               resetitem = true;\r
+                               break;\r
+#endif\r
+                       }\r
+\r
+                       if\r
+                       (\r
+                               (!resetitem)\r
+                       &&      (\r
+                                       (LastScan == KbdDefs[0].button0)\r
+                               ||      (LastScan == KbdDefs[0].button1)\r
+                               )\r
+                       )\r
+                       {\r
+                               USL_DoItem();\r
+                               resetitem = true;\r
+                       }\r
+\r
+                       if (!resetitem)\r
+                       {\r
+                               for (item = topcard->items,i = 0;item->type != uii_Bad;item++,i++)\r
+                               {\r
+                                       if (item->hotkey == LastScan)\r
+                                       {\r
+                                               USL_SelectItem(topcard,i,true);\r
+                                               resetitem = true;\r
+                                               break;\r
+                                       }\r
+                               }\r
+                       }\r
+\r
+                       IN_ClearKeysDown();\r
+               }\r
+               else\r
+               {\r
+                       IN_ReadCursor(&cursorinfo);\r
+                       ydelta += cursorinfo.y;\r
+                       if (cursorinfo.button0)\r
+                       {\r
+                               do\r
+                               {\r
+                                       IN_ReadCursor(&cursorinfo);\r
+                               } while (cursorinfo.button0);\r
+                               USL_DoItem();\r
+                               resetitem = true;\r
+                       }\r
+                       else if (cursorinfo.button1)\r
+                       {\r
+                               do\r
+                               {\r
+                                       IN_ReadCursor(&cursorinfo);\r
+                               } while (cursorinfo.button1);\r
+                               USL_UpLevel();\r
+                               resetitem = true;\r
+                       }\r
+                       else if (ydelta < -MoveMin)\r
+                       {\r
+                               ydelta += MoveMin;\r
+                               USL_PrevItem();\r
+                               resetitem = true;\r
+                       }\r
+                       else if (ydelta > MoveMin)\r
+                       {\r
+                               ydelta -= MoveMin;\r
+                               USL_NextItem();\r
+                               resetitem = true;\r
+                       }\r
+               }\r
+       }\r
+\r
+       USL_TearDownCtlPanel();\r
+}\r
diff --git a/16/cawat/ID_US_A.ASM b/16/cawat/ID_US_A.ASM
new file mode 100644 (file)
index 0000000..a27ca54
--- /dev/null
@@ -0,0 +1,117 @@
+; Catacomb Armageddon Source Code\r
+; Copyright (C) 1993-2014 Flat Rock Software\r
+;\r
+; This program is free software; you can redistribute it and/or modify\r
+; it under the terms of the GNU General Public License as published by\r
+; the Free Software Foundation; either version 2 of the License, or\r
+; (at your option) any later version.\r
+;\r
+; This program is distributed in the hope that it will be useful,\r
+; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+; GNU General Public License for more details.\r
+;\r
+; You should have received a copy of the GNU General Public License along\r
+; with this program; if not, write to the Free Software Foundation, Inc.,\r
+; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+IDEAL\r
+MODEL  MEDIUM,C\r
+\r
+;      Assembly portion of the User Mgr. This is just John Carmack's table\r
+;              driven pseudo-random number generator, and we put it in the User Mgr\r
+;              because we couldn't figure out where it should go\r
+\r
+\r
+;============================================================================\r
+;\r
+;                           RANDOM ROUTINES\r
+;\r
+;============================================================================\r
+\r
+       FARDATA\r
+\r
+rndindex       dw      ?\r
+\r
+rndtable db    0,   8, 109, 220, 222, 241, 149, 107,  75, 248, 254, 140,  16,  66\r
+       db   74,  21, 211,  47,  80, 242, 154,  27, 205, 128, 161,  89,  77,  36\r
+       db   95, 110,  85,  48, 212, 140, 211, 249,  22,  79, 200,  50,  28, 188\r
+       db   52, 140, 202, 120,  68, 145,  62,  70, 184, 190,  91, 197, 152, 224\r
+       db  149, 104,  25, 178, 252, 182, 202, 182, 141, 197,   4,  81, 181, 242\r
+       db  145,  42,  39, 227, 156, 198, 225, 193, 219,  93, 122, 175, 249,   0\r
+       db  175, 143,  70, 239,  46, 246, 163,  53, 163, 109, 168, 135,   2, 235\r
+       db   25,  92,  20, 145, 138,  77,  69, 166,  78, 176, 173, 212, 166, 113\r
+       db   94, 161,  41,  50, 239,  49, 111, 164,  70,  60,   2,  37, 171,  75\r
+       db  136, 156,  11,  56,  42, 146, 138, 229,  73, 146,  77,  61,  98, 196\r
+       db  135, 106,  63, 197, 195,  86,  96, 203, 113, 101, 170, 247, 181, 113\r
+       db   80, 250, 108,   7, 255, 237, 129, 226,  79, 107, 112, 166, 103, 241\r
+       db   24, 223, 239, 120, 198,  58,  60,  82, 128,   3, 184,  66, 143, 224\r
+       db  145, 224,  81, 206, 163,  45,  63,  90, 168, 114,  59,  33, 159,  95\r
+       db   28, 139, 123,  98, 125, 196,  15,  70, 194, 253,  54,  14, 109, 226\r
+       db   71,  17, 161,  93, 186,  87, 244, 138,  20,  52, 123, 251,  26,  36\r
+       db   17,  46,  52, 231, 232,  76,  31, 221,  84,  37, 216, 165, 212, 106\r
+       db  197, 242,  98,  43,  39, 175, 254, 145, 190,  84, 118, 222, 187, 136\r
+       db  120, 163, 236, 249\r
+\r
+\r
+       CODESEG\r
+\r
+LastRnd                dw      ?\r
+\r
+;=================================================\r
+;\r
+; void US_InitRndT (boolean randomize)\r
+; Init table based RND generator\r
+; if randomize is false, the counter is set to 0\r
+;\r
+;=================================================\r
+\r
+PROC   US_InitRndT randomize:word\r
+       uses    si,di\r
+       public  US_InitRndT\r
+\r
+       mov     ax,SEG rndtable\r
+       mov     es,ax\r
+       mov     ax,[randomize]\r
+       or      ax,ax\r
+       jne     @@timeit                ;if randomize is true, really random\r
+\r
+       mov     dx,0                    ;set to a definite value\r
+       jmp     @@setit\r
+\r
+@@timeit:\r
+       mov     ah,2ch\r
+       int     21h                     ;GetSystemTime\r
+       and     dx,0ffh\r
+\r
+@@setit:\r
+       mov     [es:rndindex],dx\r
+       ret\r
+\r
+ENDP\r
+\r
+;=================================================\r
+;\r
+; int US_RndT (void)\r
+; Return a random # between 0-255\r
+; Exit : AX = value\r
+;\r
+;=================================================\r
+PROC   US_RndT\r
+       public  US_RndT\r
+\r
+       mov     ax,SEG rndtable\r
+       mov     es,ax\r
+       mov     bx,[es:rndindex]\r
+       inc     bx\r
+       and     bx,0ffh\r
+       mov     [es:rndindex],bx\r
+       mov     al,[es:rndtable+BX]\r
+       xor     ah,ah\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+END\r
+\r
diff --git a/16/cawat/ID_VW.C b/16/cawat/ID_VW.C
new file mode 100644 (file)
index 0000000..f6a0067
--- /dev/null
@@ -0,0 +1,1591 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_VW.C\r
+\r
+#include "ID_HEADS.H"\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL CONSTANTS\r
+\r
+=============================================================================\r
+*/\r
+\r
+#define VIEWWIDTH              40\r
+\r
+#define PIXTOBLOCK             4               // 16 pixels to an update block\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                GLOBAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+cardtype       videocard;              // set by VW_Startup\r
+grtype         grmode;                 // CGAgr, EGAgr, VGAgr\r
+\r
+unsigned       bufferofs;              // hidden area to draw to before displaying\r
+unsigned       displayofs;             // origin of the visable screen\r
+unsigned       panx,pany;              // panning adjustments inside port in pixels\r
+unsigned       pansx,pansy;    // panning adjustments inside port in screen\r
+                                                       // block limited pixel values (ie 0/8 for ega x)\r
+unsigned       panadjust;              // panx/pany adjusted by screen resolution\r
+\r
+unsigned       screenseg;              // normally 0xa000 / 0xb800\r
+unsigned       linewidth;\r
+unsigned       ylookup[VIRTUALHEIGHT];\r
+\r
+unsigned       fontnumber;             // 0 based font number for drawing\r
+\r
+boolean                screenfaded;\r
+\r
+pictabletype   _seg *pictable;\r
+pictabletype   _seg *picmtable;\r
+spritetabletype _seg *spritetable;\r
+\r
+int                    bordercolor;\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                LOCAL VARIABLES\r
+\r
+=============================================================================\r
+*/\r
+\r
+void   VWL_MeasureString (char far *string, word *width, word *height,\r
+               fontstruct _seg *font);\r
+void   VWL_DrawCursor (void);\r
+void   VWL_EraseCursor (void);\r
+void   VWL_DBSetup (void);\r
+void   VWL_UpdateScreenBlocks (void);\r
+\r
+\r
+int                    bordercolor;\r
+int                    cursorvisible;\r
+int                    cursornumber,cursorwidth,cursorheight,cursorx,cursory;\r
+memptr         cursorsave;\r
+unsigned       cursorspot;\r
+\r
+//===========================================================================\r
+\r
+\r
+/*\r
+=======================\r
+=\r
+= VW_Startup\r
+=\r
+=======================\r
+*/\r
+\r
+static char *ParmStrings[] = {"HIDDENCARD",""};\r
+\r
+void   VW_Startup (void)\r
+{\r
+       int i;\r
+\r
+       asm     cld;\r
+\r
+       videocard = 0;\r
+\r
+       for (i = 1;i < _argc;i++)\r
+               if (US_CheckParm(_argv[i],ParmStrings) == 0)\r
+               {\r
+                       videocard = EGAcard;\r
+                       break;\r
+               }\r
+\r
+       if (!videocard)\r
+               videocard = VW_VideoID ();\r
+\r
+#if GRMODE == EGAGR\r
+       grmode = EGAGR;\r
+       if (videocard != EGAcard && videocard != VGAcard)\r
+Quit ("Improper video card!  If you really have an EGA/VGA card that I am not \n"\r
+         "detecting, use the -HIDDENCARD command line parameter!");\r
+       EGAWRITEMODE(0);\r
+#endif\r
+\r
+#if GRMODE == CGAGR\r
+       grmode = CGAGR;\r
+       if (videocard < CGAcard || videocard > VGAcard)\r
+Quit ("Improper video card!  If you really have a CGA card that I am not \n"\r
+         "detecting, use the -HIDDENCARD command line parameter!");\r
+       MM_GetPtr (&(memptr)screenseg,0x10000l);        // grab 64k for floating screen\r
+#endif\r
+\r
+       cursorvisible = 0;\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+=======================\r
+=\r
+= VW_Shutdown\r
+=\r
+=======================\r
+*/\r
+\r
+void   VW_Shutdown (void)\r
+{\r
+       VW_SetScreenMode (TEXTGR);\r
+#if GRMODE == EGAGR\r
+       VW_SetLineWidth (80);\r
+#endif\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+========================\r
+=\r
+= VW_SetScreenMode\r
+= Call BIOS to set TEXT / CGAgr / EGAgr / VGAgr\r
+=\r
+========================\r
+*/\r
+\r
+void VW_SetScreenMode (int grmode)\r
+{\r
+       switch (grmode)\r
+       {\r
+         case TEXTGR:  _AX = 3;\r
+                 geninterrupt (0x10);\r
+                 screenseg=0xb000;\r
+                 break;\r
+         case CGAGR: _AX = 4;\r
+                 geninterrupt (0x10);          // screenseg is actually a main mem buffer\r
+                 break;\r
+\r
+               case EGA320GR:                                          // MDM start (GAMERS EDGE)\r
+                 MaxX=320;\r
+                 MaxY=200;\r
+                 _AX = 0xd|128;\r
+                 geninterrupt (0x10);\r
+                 screenseg=0xa000;\r
+               break;\r
+\r
+               case EGA640GR:\r
+                 MaxX=640;\r
+                 MaxY=200;\r
+                 _AX = 0xe|128;\r
+                 geninterrupt (0x10);\r
+                 screenseg=0xa000;\r
+               break;                                                          // MDM end (GAMERS EDGE)\r
+\r
+         case EGAGR: _AX = 0xd;\r
+                 MaxX=320;\r
+                 MaxY=200;\r
+                 geninterrupt (0x10);\r
+                 screenseg=0xa000;\r
+                 break;\r
+#ifdef VGAGAME\r
+         case VGAGR:{\r
+                 char extern VGAPAL;   // deluxepaint vga pallet .OBJ file\r
+                 void far *vgapal = &VGAPAL;\r
+                 SetCool256 ();                // custom 256 color mode\r
+                 screenseg=0xa000;\r
+                 _ES = FP_SEG(vgapal);\r
+                 _DX = FP_OFF(vgapal);\r
+                 _BX = 0;\r
+                 _CX = 0x100;\r
+                 _AX = 0x1012;\r
+                 geninterrupt(0x10);                   // set the deluxepaint pallet\r
+\r
+                 break;\r
+#endif\r
+       }\r
+       VW_SetLineWidth(SCREENWIDTH);\r
+}\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                       SCREEN FADES\r
+\r
+=============================================================================\r
+*/\r
+\r
+char colors[7][17]=\r
+{{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},\r
+ {0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,0},\r
+ {0,0,0,0,0,0,0,0,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0},\r
+ {0,1,2,3,4,5,6,7,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0},\r
+ {0,1,2,3,4,5,6,7,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0},\r
+ {0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f}};\r
+\r
+\r
+void VW_ColorBorder (int color)\r
+{\r
+       _AH=0x10;\r
+       _AL=1;\r
+       _BH=color;\r
+       geninterrupt (0x10);\r
+       bordercolor = color;\r
+}\r
+\r
+void VW_SetPalette(byte *palette)\r
+{\r
+       byte    p;\r
+       word    i;\r
+\r
+       for (i = 0;i < 15;i++)\r
+       {\r
+               p = palette[i];\r
+               colors[0][i] = 0;\r
+               colors[1][i] = (p > 0x10)? (p & 0x0f) : 0;\r
+               colors[2][i] = (p > 0x10)? p : 0;\r
+               colors[3][i] = p;\r
+               colors[4][i] = (p > 0x10)? 0x1f : p;\r
+               colors[5][i] = 0x1f;\r
+       }\r
+}\r
+\r
+void VW_SetDefaultColors(void)\r
+{\r
+#if GRMODE == EGAGR\r
+       colors[3][16] = bordercolor;\r
+       _ES=FP_SEG(&colors[3]);\r
+       _DX=FP_OFF(&colors[3]);\r
+       _AX=0x1002;\r
+       geninterrupt(0x10);\r
+       screenfaded = false;\r
+#endif\r
+}\r
+\r
+\r
+void VW_FadeOut(void)\r
+{\r
+#if GRMODE == EGAGR\r
+       int i;\r
+\r
+       for (i=3;i>=0;i--)\r
+       {\r
+         colors[i][16] = bordercolor;\r
+         _ES=FP_SEG(&colors[i]);\r
+         _DX=FP_OFF(&colors[i]);\r
+         _AX=0x1002;\r
+         geninterrupt(0x10);\r
+         VW_WaitVBL(6);\r
+       }\r
+       screenfaded = true;\r
+#endif\r
+}\r
+\r
+\r
+void VW_FadeIn(void)\r
+{\r
+#if GRMODE == EGAGR\r
+       int i;\r
+\r
+       for (i=0;i<4;i++)\r
+       {\r
+         colors[i][16] = bordercolor;\r
+         _ES=FP_SEG(&colors[i]);\r
+         _DX=FP_OFF(&colors[i]);\r
+         _AX=0x1002;\r
+         geninterrupt(0x10);\r
+         VW_WaitVBL(6);\r
+       }\r
+       screenfaded = false;\r
+#endif\r
+}\r
+\r
+void VW_FadeUp(void)\r
+{\r
+#if GRMODE == EGAGR\r
+       int i;\r
+\r
+       for (i=3;i<6;i++)\r
+       {\r
+         colors[i][16] = bordercolor;\r
+         _ES=FP_SEG(&colors[i]);\r
+         _DX=FP_OFF(&colors[i]);\r
+         _AX=0x1002;\r
+         geninterrupt(0x10);\r
+         VW_WaitVBL(6);\r
+       }\r
+       screenfaded = true;\r
+#endif\r
+}\r
+\r
+void VW_FadeDown(void)\r
+{\r
+#if GRMODE == EGAGR\r
+       int i;\r
+\r
+       for (i=5;i>2;i--)\r
+       {\r
+         colors[i][16] = bordercolor;\r
+         _ES=FP_SEG(&colors[i]);\r
+         _DX=FP_OFF(&colors[i]);\r
+         _AX=0x1002;\r
+         geninterrupt(0x10);\r
+         VW_WaitVBL(6);\r
+       }\r
+       screenfaded = false;\r
+#endif\r
+}\r
+\r
+\r
+/*\r
+========================\r
+=\r
+= VW_SetAtrReg\r
+=\r
+= Sets an attribute (pallete / border) register\r
+= Does NOT vsync!\r
+=\r
+========================\r
+*/\r
+\r
+void VW_SetAtrReg (int reg, int value)\r
+{\r
+  asm  cli\r
+  asm  mov     dx,STATUS_REGISTER_1\r
+  asm  in      al,dx\r
+  asm  mov     dx,ATR_INDEX\r
+\r
+  asm  mov     al,BYTE PTR [reg]\r
+  asm  out     dx,al\r
+  asm  mov     al,BYTE PTR [value]\r
+  asm  out     dx,al\r
+  asm  mov     dx,0x3da\r
+  asm  in      al,dx\r
+  asm  mov     dx,ATR_INDEX\r
+  asm  mov     al,0x20\r
+  asm  out     dx,al\r
+  asm  sti\r
+}\r
+\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= VW_SetLineWidth\r
+=\r
+= Must be an even number of bytes\r
+=\r
+====================\r
+*/\r
+\r
+void VW_SetLineWidth (int width)\r
+{\r
+  int i,offset;\r
+\r
+#if GRMODE == EGAGR\r
+//\r
+// set wide virtual screen\r
+//\r
+asm    mov     dx,CRTC_INDEX\r
+asm    mov     al,CRTC_OFFSET\r
+asm mov        ah,[BYTE PTR width]\r
+asm    shr     ah,1\r
+asm    out     dx,ax\r
+#endif\r
+\r
+//\r
+// set up lookup tables\r
+//\r
+  linewidth = width;\r
+\r
+  offset = 0;\r
+\r
+  for (i=0;i<VIRTUALHEIGHT;i++)\r
+  {\r
+       ylookup[i]=offset;\r
+       offset += width;\r
+  }\r
+}\r
+\r
+\r
+//===========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= VW_SetSplitScreen\r
+=\r
+====================\r
+*/\r
+\r
+void VW_SetSplitScreen (int linenum)\r
+{\r
+       VW_WaitVBL (1);\r
+       if (videocard==VGAcard)\r
+               linenum=linenum*2-1;\r
+       outportb (CRTC_INDEX,CRTC_LINECOMPARE);\r
+       outportb (CRTC_INDEX+1,linenum % 256);\r
+       outportb (CRTC_INDEX,CRTC_OVERFLOW);\r
+       outportb (CRTC_INDEX+1, 1+16*(linenum/256));\r
+       if (videocard==VGAcard)\r
+       {\r
+               outportb (CRTC_INDEX,CRTC_MAXSCANLINE);\r
+               outportb (CRTC_INDEX+1,inportb(CRTC_INDEX+1) & (255-64));\r
+       }\r
+}\r
+\r
+//===========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= VW_ClearVideo\r
+=\r
+====================\r
+*/\r
+\r
+void   VW_ClearVideo (int color)\r
+{\r
+#if GRMODE == EGAGR\r
+       EGAWRITEMODE(2);\r
+       EGAMAPMASK(15);\r
+#endif\r
+\r
+asm    mov     es,[screenseg]\r
+asm    xor     di,di\r
+asm    mov     cx,0xffff\r
+asm    mov     al,[BYTE PTR color]\r
+asm    rep     stosb\r
+asm    stosb\r
+\r
+#if GRMODE == EGAGR\r
+       EGAWRITEMODE(0);\r
+#endif\r
+}\r
+\r
+//===========================================================================\r
+\r
+#if NUMPICS>0\r
+\r
+/*\r
+====================\r
+=\r
+= VW_DrawPic\r
+=\r
+= X in bytes, y in pixels, chunknum is the #defined picnum\r
+=\r
+====================\r
+*/\r
+\r
+void VW_DrawPic(unsigned x, unsigned y, unsigned chunknum)\r
+{\r
+       int     picnum = chunknum - STARTPICS;\r
+       memptr source;\r
+       unsigned dest,width,height;\r
+\r
+       source = grsegs[chunknum];\r
+       dest = ylookup[y]+x+bufferofs;\r
+       width = pictable[picnum].width;\r
+       height = pictable[picnum].height;\r
+\r
+       VW_MemToScreen(source,dest,width,height);\r
+}\r
+\r
+\r
+// MDM (GAMERS EDGE) begin\r
+/*\r
+====================\r
+=\r
+= VW_DrawPic2x - Same as VW_DrawPic, but doubles pixels horizontally\r
+=                (Great for drawing 320 graphics on 640 screen!)\r
+=\r
+= X in bytes, y in pixels, chunknum is the #defined picnum\r
+=\r
+====================\r
+*/\r
+\r
+void VW_DrawPic2x(unsigned x, unsigned y, unsigned chunknum)\r
+{\r
+       int     picnum = chunknum - STARTPICS;\r
+       memptr source;\r
+       unsigned dest,width,height;\r
+\r
+       source = grsegs[chunknum];\r
+       dest = ylookup[y]+x+bufferofs;\r
+       width = pictable[picnum].width;\r
+       height = pictable[picnum].height;\r
+\r
+       VW_MemToScreen2x(source,dest,width,height);\r
+}\r
+// MDM (GAMERS EDGE) end\r
+\r
+#endif\r
+\r
+#if NUMPICM>0\r
+\r
+/*\r
+====================\r
+=\r
+= VW_DrawMPic\r
+=\r
+= X in bytes, y in pixels, chunknum is the #defined picnum\r
+=\r
+====================\r
+*/\r
+\r
+void VW_DrawMPic(unsigned x, unsigned y, unsigned chunknum)\r
+{\r
+       int     picnum = chunknum - STARTPICM;\r
+       memptr source;\r
+       unsigned dest,width,height;\r
+\r
+       source = grsegs[chunknum];\r
+       dest = ylookup[y]+x+bufferofs;\r
+       width = picmtable[picnum].width;\r
+       height = picmtable[picnum].height;\r
+\r
+       VW_MaskBlock(source,0,dest,width,height,width*height);\r
+}\r
+\r
+void VW_ClipDrawMPic(unsigned x, int y, unsigned chunknum)\r
+{\r
+       int     picnum = chunknum - STARTPICM;\r
+       memptr source;\r
+       unsigned dest,width,ofs,plane;\r
+       int             height;\r
+\r
+       source = grsegs[chunknum];\r
+       width = picmtable[picnum].width;\r
+       height = picmtable[picnum].height;\r
+       plane = width*height;\r
+\r
+       ofs = 0;\r
+       if (y<0)\r
+       {\r
+               ofs= -y*width;\r
+               height+=y;\r
+               y=0;\r
+       }\r
+       else if (y+height>216)\r
+       {\r
+               height-=(y-216);\r
+       }\r
+       dest = ylookup[y]+x+bufferofs;\r
+       if (height<1)\r
+               return;\r
+\r
+       VW_MaskBlock(source,ofs,dest,width,height,plane);\r
+}\r
+\r
+\r
+#endif\r
+\r
+//===========================================================================\r
+\r
+#if NUMSPRITES>0\r
+\r
+/*\r
+====================\r
+=\r
+= VW_DrawSprite\r
+=\r
+= X and Y in pixels, it will match the closest shift possible\r
+=\r
+= To do:\r
+= Add vertical clipping!\r
+= Make the shifts act as center points, rather than break points\r
+=\r
+====================\r
+*/\r
+\r
+void VW_DrawSprite(int x, int y, unsigned chunknum)\r
+{\r
+       spritetabletype far *spr;\r
+       spritetype _seg *block;\r
+       unsigned        dest,shift;\r
+\r
+       spr = &spritetable[chunknum-STARTSPRITES];\r
+       block = (spritetype _seg *)grsegs[chunknum];\r
+\r
+       y+=spr->orgy>>G_P_SHIFT;\r
+       x+=spr->orgx>>G_P_SHIFT;\r
+\r
+#if GRMODE == EGAGR\r
+       shift = (x&7)/2;\r
+#endif\r
+#if GRMODE == CGAGR\r
+       shift = 0;\r
+#endif\r
+\r
+       dest = bufferofs + ylookup[y];\r
+       if (x>=0)\r
+               dest += x/SCREENXDIV;\r
+       else\r
+               dest += (x+1)/SCREENXDIV;\r
+\r
+       VW_MaskBlock (block,block->sourceoffset[shift],dest,\r
+               block->width[shift],spr->height,block->planesize[shift]);\r
+}\r
+\r
+#endif\r
+\r
+\r
+/*\r
+==================\r
+=\r
+= VW_Hlin\r
+=\r
+==================\r
+*/\r
+\r
+\r
+#if GRMODE == EGAGR\r
+\r
+unsigned char leftmask[8] = {0xff,0x7f,0x3f,0x1f,0xf,7,3,1};\r
+unsigned char rightmask[8] = {0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff};\r
+\r
+void VW_Hlin(unsigned xl, unsigned xh, unsigned y, unsigned color)\r
+{\r
+  unsigned dest,xlb,xhb,maskleft,maskright,mid;\r
+\r
+       xlb=xl/8;\r
+       xhb=xh/8;\r
+\r
+       EGAWRITEMODE(2);\r
+       EGAMAPMASK(15);\r
+\r
+       maskleft = leftmask[xl&7];\r
+       maskright = rightmask[xh&7];\r
+\r
+       mid = xhb-xlb-1;\r
+       dest = bufferofs+ylookup[y]+xlb;\r
+\r
+  if (xlb==xhb)\r
+  {\r
+  //\r
+  // entire line is in one byte\r
+  //\r
+\r
+       maskleft&=maskright;\r
+\r
+       asm     mov     es,[screenseg]\r
+       asm     mov     di,[dest]\r
+\r
+       asm     mov     dx,GC_INDEX\r
+       asm     mov     al,GC_BITMASK\r
+       asm     mov     ah,[BYTE PTR maskleft]\r
+       asm     out     dx,ax           // mask off pixels\r
+\r
+       asm     mov     al,[BYTE PTR color]\r
+       asm     xchg    al,[es:di]      // load latches and write pixels\r
+\r
+       goto    done;\r
+  }\r
+\r
+asm    mov     es,[screenseg]\r
+asm    mov     di,[dest]\r
+asm    mov     dx,GC_INDEX\r
+asm    mov     bh,[BYTE PTR color]\r
+\r
+//\r
+// draw left side\r
+//\r
+asm    mov     al,GC_BITMASK\r
+asm    mov     ah,[BYTE PTR maskleft]\r
+asm    out     dx,ax           // mask off pixels\r
+\r
+asm    mov     al,bh\r
+asm    mov     bl,[es:di]      // load latches\r
+asm    stosb\r
+\r
+//\r
+// draw middle\r
+//\r
+asm    mov     ax,GC_BITMASK + 255*256\r
+asm    out     dx,ax           // no masking\r
+\r
+asm    mov     al,bh\r
+asm    mov     cx,[mid]\r
+asm    rep     stosb\r
+\r
+//\r
+// draw right side\r
+//\r
+asm    mov     al,GC_BITMASK\r
+asm    mov     ah,[BYTE PTR maskright]\r
+asm    out     dx,ax           // mask off pixels\r
+\r
+asm    xchg    bh,[es:di]      // load latches and write pixels\r
+\r
+done:\r
+       EGABITMASK(255);\r
+       EGAWRITEMODE(0);\r
+}\r
+#endif\r
+\r
+\r
+#if GRMODE == CGAGR\r
+\r
+unsigned char pixmask[4] = {0xc0,0x30,0x0c,0x03};\r
+unsigned char leftmask[4] = {0xff,0x3f,0x0f,0x03};\r
+unsigned char rightmask[4] = {0xc0,0xf0,0xfc,0xff};\r
+unsigned char colorbyte[4] = {0,0x55,0xaa,0xff};\r
+\r
+//\r
+// could be optimized for rep stosw\r
+//\r
+void VW_Hlin(unsigned xl, unsigned xh, unsigned y, unsigned color)\r
+{\r
+       unsigned dest,xlb,xhb,mid;\r
+       byte maskleft,maskright;\r
+\r
+       color = colorbyte[color];       // expand 2 color bits to 8\r
+\r
+       xlb=xl/4;\r
+       xhb=xh/4;\r
+\r
+       maskleft = leftmask[xl&3];\r
+       maskright = rightmask[xh&3];\r
+\r
+       mid = xhb-xlb-1;\r
+       dest = bufferofs+ylookup[y]+xlb;\r
+asm    mov     es,[screenseg]\r
+\r
+       if (xlb==xhb)\r
+       {\r
+       //\r
+       // entire line is in one byte\r
+       //\r
+               maskleft&=maskright;\r
+\r
+               asm     mov     ah,[maskleft]\r
+               asm     mov     bl,[BYTE PTR color]\r
+               asm     and     bl,[maskleft]\r
+               asm     not     ah\r
+\r
+               asm     mov     di,[dest]\r
+\r
+               asm     mov     al,[es:di]\r
+               asm     and     al,ah                   // mask out pixels\r
+               asm     or      al,bl                   // or in color\r
+               asm     mov     [es:di],al\r
+               return;\r
+       }\r
+\r
+asm    mov     di,[dest]\r
+asm    mov     bh,[BYTE PTR color]\r
+\r
+//\r
+// draw left side\r
+//\r
+asm    mov     ah,[maskleft]\r
+asm    mov     bl,bh\r
+asm    and     bl,[maskleft]\r
+asm    not     ah\r
+asm    mov     al,[es:di]\r
+asm    and     al,ah                   // mask out pixels\r
+asm    or      al,bl                   // or in color\r
+asm    stosb\r
+\r
+//\r
+// draw middle\r
+//\r
+asm    mov     al,bh\r
+asm    mov     cx,[mid]\r
+asm    rep     stosb\r
+\r
+//\r
+// draw right side\r
+//\r
+asm    mov     ah,[maskright]\r
+asm    mov     bl,bh\r
+asm    and     bl,[maskright]\r
+asm    not     ah\r
+asm    mov     al,[es:di]\r
+asm    and     al,ah                   // mask out pixels\r
+asm    or      al,bl                   // or in color\r
+asm    stosb\r
+}\r
+#endif\r
+\r
+\r
+/*\r
+==================\r
+=\r
+= VW_Bar\r
+=\r
+= Pixel addressable block fill routine\r
+=\r
+==================\r
+*/\r
+\r
+#if GRMODE == CGAGR\r
+\r
+void VW_Bar (unsigned x, unsigned y, unsigned width, unsigned height,\r
+       unsigned color)\r
+{\r
+       unsigned xh = x+width-1;\r
+\r
+       while (height--)\r
+               VW_Hlin (x,xh,y++,color);\r
+}\r
+\r
+#endif\r
+\r
+\r
+#if    GRMODE == EGAGR\r
+\r
+void VW_Bar (unsigned x, unsigned y, unsigned width, unsigned height,\r
+       unsigned color)\r
+{\r
+       unsigned dest,xh,xlb,xhb,maskleft,maskright,mid;\r
+\r
+       xh = x+width-1;\r
+       xlb=x/8;\r
+       xhb=xh/8;\r
+\r
+       EGAWRITEMODE(2);\r
+       EGAMAPMASK(15);\r
+\r
+       maskleft = leftmask[x&7];\r
+       maskright = rightmask[xh&7];\r
+\r
+       mid = xhb-xlb-1;\r
+       dest = bufferofs+ylookup[y]+xlb;\r
+\r
+       if (xlb==xhb)\r
+       {\r
+       //\r
+       // entire line is in one byte\r
+       //\r
+\r
+               maskleft&=maskright;\r
+\r
+       asm     mov     es,[screenseg]\r
+       asm     mov     di,[dest]\r
+\r
+       asm     mov     dx,GC_INDEX\r
+       asm     mov     al,GC_BITMASK\r
+       asm     mov     ah,[BYTE PTR maskleft]\r
+       asm     out     dx,ax           // mask off pixels\r
+\r
+       asm     mov     ah,[BYTE PTR color]\r
+       asm     mov     dx,[linewidth]\r
+yloop1:\r
+       asm     mov     al,ah\r
+       asm     xchg    al,[es:di]      // load latches and write pixels\r
+       asm     add     di,dx                   // down to next line\r
+       asm     dec     [height]\r
+       asm     jnz     yloop1\r
+\r
+               goto    done;\r
+       }\r
+\r
+asm    mov     es,[screenseg]\r
+asm    mov     di,[dest]\r
+asm    mov     bh,[BYTE PTR color]\r
+asm    mov     dx,GC_INDEX\r
+asm    mov     si,[linewidth]\r
+asm    sub     si,[mid]                        // add to di at end of line to get to next scan\r
+asm    dec     si\r
+\r
+//\r
+// draw left side\r
+//\r
+yloop2:\r
+asm    mov     al,GC_BITMASK\r
+asm    mov     ah,[BYTE PTR maskleft]\r
+asm    out     dx,ax           // mask off pixels\r
+\r
+asm    mov     al,bh\r
+asm    mov     bl,[es:di]      // load latches\r
+asm    stosb\r
+\r
+//\r
+// draw middle\r
+//\r
+asm    mov     ax,GC_BITMASK + 255*256\r
+asm    out     dx,ax           // no masking\r
+\r
+asm    mov     al,bh\r
+asm    mov     cx,[mid]\r
+asm    rep     stosb\r
+\r
+//\r
+// draw right side\r
+//\r
+asm    mov     al,GC_BITMASK\r
+asm    mov     ah,[BYTE PTR maskright]\r
+asm    out     dx,ax           // mask off pixels\r
+\r
+asm    mov     al,bh\r
+asm    xchg    al,[es:di]      // load latches and write pixels\r
+\r
+asm    add     di,si           // move to start of next line\r
+asm    dec     [height]\r
+asm    jnz     yloop2\r
+\r
+done:\r
+       EGABITMASK(255);\r
+       EGAWRITEMODE(0);\r
+}\r
+\r
+#endif\r
+\r
+//==========================================================================\r
+\r
+/*\r
+==================\r
+=\r
+= VW_MeasureString\r
+=\r
+==================\r
+*/\r
+\r
+#if NUMFONT+NUMFONTM>0\r
+void\r
+VWL_MeasureString (char far *string, word *width, word *height, fontstruct _seg *font)\r
+{\r
+       *height = font->height-1;                       // MDM (GAMERS EDGE) - squeeze font vertically...\r
+       for (*width = 0;*string;string++)\r
+               *width += font->width[*((byte far *)string)];   // proportional width\r
+}\r
+\r
+void   VW_MeasurePropString (char far *string, word *width, word *height)\r
+{\r
+       VWL_MeasureString(string,width,height,(fontstruct _seg *)grsegs[STARTFONT+fontnumber]);\r
+}\r
+\r
+void   VW_MeasureMPropString  (char far *string, word *width, word *height)\r
+{\r
+       VWL_MeasureString(string,width,height,(fontstruct _seg *)grsegs[STARTFONTM+fontnumber]);\r
+}\r
+\r
+\r
+#endif\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                                       CGA stuff\r
+\r
+=============================================================================\r
+*/\r
+\r
+#if GRMODE == CGAGR\r
+\r
+#define CGACRTCWIDTH   40\r
+\r
+/*\r
+==========================\r
+=\r
+= VW_CGAFullUpdate\r
+=\r
+==========================\r
+*/\r
+\r
+void VW_CGAFullUpdate (void)\r
+{\r
+       byte    *update;\r
+       boolean halftile;\r
+       unsigned        x,y,middlerows,middlecollumns;\r
+\r
+       displayofs = bufferofs+panadjust;\r
+\r
+asm    mov     ax,0xb800\r
+asm    mov     es,ax\r
+\r
+asm    mov     si,[displayofs]\r
+asm    xor     di,di\r
+\r
+asm    mov     bx,100                          // pairs of scan lines to copy\r
+asm    mov     dx,[linewidth]\r
+asm    sub     dx,80\r
+\r
+asm    mov     ds,[screenseg]\r
+asm    test    si,1\r
+asm    jz      evenblock\r
+\r
+//\r
+// odd source\r
+//\r
+asm    mov     ax,39                           // words accross screen\r
+copytwolineso:\r
+asm    movsb\r
+asm    mov     cx,ax\r
+asm    rep     movsw\r
+asm    movsb\r
+asm    add     si,dx\r
+asm    add     di,0x2000-80            // go to the interlaced bank\r
+asm    movsb\r
+asm    mov     cx,ax\r
+asm    rep     movsw\r
+asm    movsb\r
+asm    add     si,dx\r
+asm    sub     di,0x2000                       // go to the non interlaced bank\r
+\r
+asm    dec     bx\r
+asm    jnz     copytwolineso\r
+asm    jmp     blitdone\r
+\r
+//\r
+// even source\r
+//\r
+evenblock:\r
+asm    mov     ax,40                           // words accross screen\r
+copytwolines:\r
+asm    mov     cx,ax\r
+asm    rep     movsw\r
+asm    add     si,dx\r
+asm    add     di,0x2000-80            // go to the interlaced bank\r
+asm    mov     cx,ax\r
+asm    rep     movsw\r
+asm    add     si,dx\r
+asm    sub     di,0x2000                       // go to the non interlaced bank\r
+\r
+asm    dec     bx\r
+asm    jnz     copytwolines\r
+\r
+blitdone:\r
+asm    mov     ax,ss\r
+asm    mov     ds,ax\r
+asm    mov     es,ax\r
+\r
+asm    xor     ax,ax                           // clear out the update matrix\r
+asm    mov     cx,UPDATEWIDE*UPDATEHIGH/2\r
+\r
+asm    mov     di,[baseupdateptr]\r
+asm    rep     stosw\r
+\r
+       updateptr = baseupdateptr;\r
+       *(unsigned *)(updateptr + UPDATEWIDE*PORTTILESHIGH) = UPDATETERMINATE;\r
+}\r
+\r
+\r
+#endif\r
+\r
+/*\r
+=============================================================================\r
+\r
+                                          CURSOR ROUTINES\r
+\r
+These only work in the context of the double buffered update routines\r
+\r
+=============================================================================\r
+*/\r
+\r
+/*\r
+====================\r
+=\r
+= VWL_DrawCursor\r
+=\r
+= Background saves, then draws the cursor at cursorspot\r
+=\r
+====================\r
+*/\r
+\r
+void VWL_DrawCursor (void)\r
+{\r
+       cursorspot = bufferofs + ylookup[cursory+pansy]+(cursorx+pansx)/SCREENXDIV;\r
+       VW_ScreenToMem(cursorspot,cursorsave,cursorwidth,cursorheight);\r
+       VWB_DrawSprite(cursorx,cursory,cursornumber);\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= VWL_EraseCursor\r
+=\r
+====================\r
+*/\r
+\r
+void VWL_EraseCursor (void)\r
+{\r
+       VW_MemToScreen(cursorsave,cursorspot,cursorwidth,cursorheight);\r
+       VW_MarkUpdateBlock ((cursorx+pansx)&SCREENXMASK,cursory+pansy,\r
+               ( (cursorx+pansx)&SCREENXMASK)+cursorwidth*SCREENXDIV-1,\r
+               cursory+pansy+cursorheight-1);\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= VW_ShowCursor\r
+=\r
+====================\r
+*/\r
+\r
+void VW_ShowCursor (void)\r
+{\r
+       cursorvisible++;\r
+}\r
+\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= VW_HideCursor\r
+=\r
+====================\r
+*/\r
+\r
+void VW_HideCursor (void)\r
+{\r
+       cursorvisible--;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= VW_MoveCursor\r
+=\r
+====================\r
+*/\r
+#define MAXCURSORX     (319-24)\r
+#define MAXCURSORY     (199-24)\r
+\r
+void VW_MoveCursor (int x, int y)\r
+{\r
+       if (x>MAXCURSORX)\r
+               x=MAXCURSORX;\r
+       if (y>MAXCURSORY)\r
+               y=MAXCURSORY;                   // catacombs hack to keep cursor on screen\r
+\r
+       cursorx = x;\r
+       cursory = y;\r
+}\r
+\r
+//==========================================================================\r
+\r
+/*\r
+====================\r
+=\r
+= VW_SetCursor\r
+=\r
+= Load in a sprite to be used as a cursor, and allocate background save space\r
+=\r
+====================\r
+*/\r
+\r
+void VW_SetCursor (int spritenum)\r
+{\r
+       VW_FreeCursor ();\r
+\r
+       cursornumber = spritenum;\r
+\r
+       CA_CacheGrChunk (spritenum);\r
+       MM_SetLock (&grsegs[spritenum],true);\r
+\r
+       cursorwidth = spritetable[spritenum-STARTSPRITES].width+1;\r
+       cursorheight = spritetable[spritenum-STARTSPRITES].height;\r
+\r
+       MM_GetPtr (&cursorsave,cursorwidth*cursorheight*5);\r
+       MM_SetLock (&cursorsave,true);\r
+}\r
+\r
+\r
+/*\r
+====================\r
+=\r
+= VW_FreeCursor\r
+=\r
+= Frees the memory used by the cursor and its background save\r
+=\r
+====================\r
+*/\r
+\r
+void VW_FreeCursor (void)\r
+{\r
+       if (cursornumber)\r
+       {\r
+               MM_SetLock (&grsegs[cursornumber],false);\r
+               MM_SetPurge (&grsegs[cursornumber],3);\r
+               MM_SetLock (&cursorsave,false);\r
+               MM_FreePtr (&cursorsave);\r
+               cursornumber = 0;\r
+       }\r
+}\r
+\r
+\r
+/*\r
+=============================================================================\r
+\r
+                               Double buffer management routines\r
+\r
+=============================================================================\r
+*/\r
+\r
+/*\r
+======================\r
+=\r
+= VW_InitDoubleBuffer\r
+=\r
+======================\r
+*/\r
+\r
+void VW_InitDoubleBuffer (void)\r
+{\r
+#if GRMODE == EGAGR\r
+       VW_SetScreen (displayofs+panadjust,0);                  // no pel pan\r
+#endif\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= VW_FixRefreshBuffer\r
+=\r
+= Copies the view page to the buffer page on page flipped refreshes to\r
+= avoid a one frame shear around pop up windows\r
+=\r
+======================\r
+*/\r
+\r
+void VW_FixRefreshBuffer (void)\r
+{\r
+#if GRMODE == EGAGR\r
+       VW_ScreenToScreen (displayofs,bufferofs,PORTTILESWIDE*4*CHARWIDTH,\r
+               (PORTTILESHIGH-1)*16);\r
+#endif\r
+}\r
+\r
+\r
+/*\r
+======================\r
+=\r
+= VW_QuitDoubleBuffer\r
+=\r
+======================\r
+*/\r
+\r
+void VW_QuitDoubleBuffer (void)\r
+{\r
+}\r
+\r
+\r
+/*\r
+=======================\r
+=\r
+= VW_MarkUpdateBlock\r
+=\r
+= Takes a pixel bounded block and marks the tiles in bufferblocks\r
+= Returns 0 if the entire block is off the buffer screen\r
+=\r
+=======================\r
+*/\r
+\r
+int VW_MarkUpdateBlock (int x1, int y1, int x2, int y2)\r
+{\r
+// MDM (GAMERS EDGE) begin - NOT NEEDED FOR 3D ENGINE\r
+#if 0\r
+       int     x,y,xt1,yt1,xt2,yt2,nextline;\r
+       byte *mark;\r
+\r
+       xt1 = x1>>PIXTOBLOCK;\r
+       yt1 = y1>>PIXTOBLOCK;\r
+\r
+       xt2 = x2>>PIXTOBLOCK;\r
+       yt2 = y2>>PIXTOBLOCK;\r
+\r
+       if (xt1<0)\r
+               xt1=0;\r
+       else if (xt1>=UPDATEWIDE-1)\r
+               return 0;\r
+\r
+       if (yt1<0)\r
+               yt1=0;\r
+       else if (yt1>UPDATEHIGH)\r
+               return 0;\r
+\r
+       if (xt2<0)\r
+               return 0;\r
+       else if (xt2>=UPDATEWIDE-1)\r
+               xt2 = UPDATEWIDE-2;\r
+\r
+       if (yt2<0)\r
+               return 0;\r
+       else if (yt2>=UPDATEHIGH)\r
+               yt2 = UPDATEHIGH-1;\r
+\r
+       mark = updateptr + uwidthtable[yt1] + xt1;\r
+       nextline = UPDATEWIDE - (xt2-xt1) - 1;\r
+\r
+       for (y=yt1;y<=yt2;y++)\r
+       {\r
+               for (x=xt1;x<=xt2;x++)\r
+                       *mark++ = 1;                    // this tile will need to be updated\r
+\r
+               mark += nextline;\r
+       }\r
+#endif\r
+// MDM (GAMERS EDGE) end\r
+\r
+       return 1;\r
+}\r
+\r
+\r
+/*\r
+===========================\r
+=\r
+= VW_UpdateScreen\r
+=\r
+= Updates any changed areas of the double buffer and displays the cursor\r
+=\r
+===========================\r
+*/\r
+\r
+void VW_UpdateScreen (void)\r
+{\r
+       if (cursorvisible>0)\r
+               VWL_DrawCursor();\r
+\r
+#if GRMODE == EGAGR\r
+       VWL_UpdateScreenBlocks();\r
+\r
+asm    mov     ax,ds\r
+asm    mov     es,ax\r
+asm    mov     di,[updateptr]          // cat3d patch\r
+asm    xor     ax,ax                           // clear out the update matrix\r
+asm    mov     cx,UPDATEWIDE*UPDATEHIGH/2\r
+asm    rep     stosw\r
+       *(unsigned *)(updateptr + UPDATEWIDE*PORTTILESHIGH) = UPDATETERMINATE;\r
+\r
+asm    cli\r
+asm    mov     cx,[displayofs]\r
+asm    add     cx,[panadjust]\r
+asm    mov     dx,CRTC_INDEX\r
+asm    mov     al,0ch          // start address high register\r
+asm    out     dx,al\r
+asm    inc     dx\r
+asm    mov     al,ch\r
+asm    out     dx,al\r
+asm    dec     dx\r
+asm    mov     al,0dh          // start address low register\r
+asm    out     dx,al\r
+asm    mov     al,cl\r
+asm    inc     dx\r
+asm    out     dx,al\r
+asm    sti\r
+\r
+#endif\r
+#if GRMODE == CGAGR\r
+       VW_CGAFullUpdate();\r
+#endif\r
+\r
+       if (cursorvisible>0)\r
+               VWL_EraseCursor();\r
+}\r
+\r
+\r
+\r
+void VWB_DrawTile8 (int x, int y, int tile)\r
+{\r
+       x+=pansx;\r
+       y+=pansy;\r
+       if (VW_MarkUpdateBlock (x&SCREENXMASK,y,(x&SCREENXMASK)+7,y+7))\r
+               VW_DrawTile8 (x/SCREENXDIV,y,tile);\r
+}\r
+\r
+void VWB_DrawTile8M (int x, int y, int tile)\r
+{\r
+       int xb;\r
+\r
+       x+=pansx;\r
+       y+=pansy;\r
+       xb = x/SCREENXDIV;                      // use intermediate because VW_DT8M is macro\r
+//     if (VW_MarkUpdateBlock (x&SCREENXMASK,y,(x&SCREENXMASK)+7,y+7)) // MDM (GAMER EDGE)\r
+               VW_DrawTile8M (xb,y,tile);                                                                                                      // statement prevents drawing chars past 42\r
+}\r
+\r
+void VWB_DrawTile16 (int x, int y, int tile)\r
+{\r
+       x+=pansx;\r
+       y+=pansy;\r
+       if (VW_MarkUpdateBlock (x&SCREENXMASK,y,(x&SCREENXMASK)+15,y+15))\r
+               VW_DrawTile16 (x/SCREENXDIV,y,tile);\r
+}\r
+\r
+void VWB_DrawTile16M (int x, int y, int tile)\r
+{\r
+       int xb;\r
+\r
+       x+=pansx;\r
+       y+=pansy;\r
+       xb = x/SCREENXDIV;              // use intermediate because VW_DT16M is macro\r
+       if (VW_MarkUpdateBlock (x&SCREENXMASK,y,(x&SCREENXMASK)+15,y+15))\r
+               VW_DrawTile16M (xb,y,tile);\r
+}\r
+\r
+#if NUMPICS\r
+void VWB_DrawPic (int x, int y, int chunknum)\r
+{\r
+// mostly copied from drawpic\r
+       int     picnum = chunknum - STARTPICS;\r
+       memptr source;\r
+       unsigned dest,width,height;\r
+\r
+       x+=pansx;\r
+       y+=pansy;\r
+       x/= SCREENXDIV;\r
+\r
+       source = grsegs[chunknum];\r
+       dest = ylookup[y]+x+bufferofs;\r
+       width = pictable[picnum].width;\r
+       height = pictable[picnum].height;\r
+\r
+       if (VW_MarkUpdateBlock (x*SCREENXDIV,y,(x+width)*SCREENXDIV-1,y+height-1))\r
+               VW_MemToScreen(source,dest,width,height);\r
+}\r
+#endif\r
+\r
+#if NUMPICM>0\r
+void VWB_DrawMPic(int x, int y, int chunknum)\r
+{\r
+// mostly copied from drawmpic\r
+       int     picnum = chunknum - STARTPICM;\r
+       memptr source;\r
+       unsigned dest,width,height;\r
+\r
+       x+=pansx;\r
+       y+=pansy;\r
+       x/=SCREENXDIV;\r
+\r
+       source = grsegs[chunknum];\r
+       dest = ylookup[y]+x+bufferofs;\r
+       width = picmtable[picnum].width;\r
+       height = picmtable[picnum].height;\r
+\r
+       if (VW_MarkUpdateBlock (x*SCREENXDIV,y,(x+width)*SCREENXDIV-1,y+height-1))\r
+               VW_MaskBlock(source,0,dest,width,height,width*height);\r
+}\r
+#endif\r
+\r
+\r
+void VWB_Bar (int x, int y, int width, int height, int color)\r
+{\r
+       x+=pansx;\r
+       y+=pansy;\r
+       if (VW_MarkUpdateBlock (x,y,x+width,y+height-1) )\r
+               VW_Bar (x,y,width,height,color);\r
+}\r
+\r
+\r
+#if NUMFONT\r
+void VWB_DrawPropString         (char far *string)\r
+{\r
+       int x,y;\r
+       x = px+pansx;\r
+       y = py+pansy;\r
+       VW_DrawPropString (string);\r
+       VW_MarkUpdateBlock(x,y,x+bufferwidth*8-1,y+bufferheight-1);\r
+}\r
+#endif\r
+\r
+\r
+#if NUMFONTM\r
+void VWB_DrawMPropString (char far *string)\r
+{\r
+       int x,y;\r
+       x = px+pansx;\r
+       y = py+pansy;\r
+       VW_DrawMPropString (string);\r
+       VW_MarkUpdateBlock(x,y,x+bufferwidth*8-1,y+bufferheight-1);\r
+}\r
+#endif\r
+\r
+#if NUMSPRITES\r
+void VWB_DrawSprite(int x, int y, int chunknum)\r
+{\r
+       spritetabletype far *spr;\r
+       spritetype _seg *block;\r
+       unsigned        dest,shift,width,height;\r
+\r
+       x+=pansx;\r
+       y+=pansy;\r
+\r
+       spr = &spritetable[chunknum-STARTSPRITES];\r
+       block = (spritetype _seg *)grsegs[chunknum];\r
+\r
+       y+=spr->orgy>>G_P_SHIFT;\r
+       x+=spr->orgx>>G_P_SHIFT;\r
+\r
+\r
+#if GRMODE == EGAGR\r
+       shift = (x&7)/2;\r
+#endif\r
+#if GRMODE == CGAGR\r
+       shift = 0;\r
+#endif\r
+\r
+       dest = bufferofs + ylookup[y];\r
+       if (x>=0)\r
+               dest += x/SCREENXDIV;\r
+       else\r
+               dest += (x+1)/SCREENXDIV;\r
+\r
+       width = block->width[shift];\r
+       height = spr->height;\r
+\r
+       if (VW_MarkUpdateBlock (x&SCREENXMASK,y,(x&SCREENXMASK)+width*SCREENXDIV-1\r
+               ,y+height-1))\r
+               VW_MaskBlock (block,block->sourceoffset[shift],dest,\r
+                       width,height,block->planesize[shift]);\r
+}\r
+#endif\r
+\r
+void VWB_Plot (int x, int y, int color)\r
+{\r
+       x+=pansx;\r
+       y+=pansy;\r
+       if (VW_MarkUpdateBlock (x,y,x,y))\r
+               VW_Plot(x,y,color);\r
+}\r
+\r
+void VWB_Hlin (int x1, int x2, int y, int color)\r
+{\r
+       x1+=pansx;\r
+       x2+=pansx;\r
+       y+=pansy;\r
+       if (VW_MarkUpdateBlock (x1,y,x2,y))\r
+               VW_Hlin(x1,x2,y,color);\r
+}\r
+\r
+void VWB_Vlin (int y1, int y2, int x, int color)\r
+{\r
+       x+=pansx;\r
+       y1+=pansy;\r
+       y2+=pansy;\r
+       if (VW_MarkUpdateBlock (x,y1,x,y2))\r
+               VW_Vlin(y1,y2,x,color);\r
+}\r
+\r
+\r
+//===========================================================================\r
diff --git a/16/cawat/ID_VW.H b/16/cawat/ID_VW.H
new file mode 100644 (file)
index 0000000..49e41d3
--- /dev/null
@@ -0,0 +1,374 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+// ID_VW.H\r
+\r
+#ifndef __TYPES__\r
+#include "ID_TYPES.H"\r
+#endif\r
+\r
+#ifndef __ID_MM__\r
+#include "ID_MM.H"\r
+#endif\r
+\r
+#ifndef __ID_GLOB__\r
+#include "ID_GLOB.H"\r
+#endif\r
+\r
+#define __ID_VW__\r
+\r
+//===========================================================================\r
+\r
+#define        G_P_SHIFT               4       // global >> ?? = pixels\r
+\r
+#if GRMODE == EGAGR\r
+#define        SCREENWIDTH             40\r
+#define CHARWIDTH              1\r
+#define TILEWIDTH              2\r
+#define GRPLANES               4\r
+#define BYTEPIXELS             8\r
+#endif\r
+\r
+#if GRMODE == CGAGR\r
+#define        SCREENWIDTH             128\r
+#define CHARWIDTH              2\r
+#define TILEWIDTH              4\r
+#define GRPLANES               1\r
+#define BYTEPIXELS             4\r
+#endif\r
+\r
+#define VIRTUALHEIGHT  300\r
+#define        VIRTUALWIDTH    512\r
+\r
+\r
+#if GRMODE == CGAGR\r
+\r
+#define        MAXSHIFTS               1\r
+\r
+#define WHITE                  3                       // graphics mode independant colors\r
+#define BLACK                  0\r
+#define FIRSTCOLOR             1\r
+#define SECONDCOLOR            2\r
+#define F_WHITE                        0                       // for XOR font drawing\r
+#define F_BLACK                        3\r
+#define F_FIRSTCOLOR   2\r
+#define F_SECONDCOLOR  1\r
+\r
+#endif\r
+\r
+#if GRMODE == EGAGR\r
+\r
+#define        MAXSHIFTS               4\r
+\r
+#define WHITE                  15                      // graphics mode independant colors\r
+#define BLACK                  0\r
+#define LT_GREY                7\r
+#define FIRSTCOLOR             1\r
+#define SECONDCOLOR            12\r
+#define F_WHITE                        0                       // for XOR font drawing\r
+#define F_BLACK                        15\r
+#define F_FIRSTCOLOR   14\r
+#define F_SECONDCOLOR  3\r
+\r
+#endif\r
+\r
+#if GRMODE == EGAGR\r
+#define SCREENXMASK            (~7)\r
+#define SCREENXPLUS            (7)\r
+#define SCREENXDIV             (8)\r
+#endif\r
+\r
+#if GRMODE == CGAGR\r
+#define SCREENXMASK            (~3)\r
+#define SCREENXDIV             (4)\r
+#endif\r
+\r
+//===========================================================================\r
+\r
+\r
+#define SC_INDEX       0x3C4\r
+#define SC_RESET       0\r
+#define SC_CLOCK       1\r
+#define SC_MAPMASK     2\r
+#define SC_CHARMAP     3\r
+#define SC_MEMMODE     4\r
+\r
+#define CRTC_INDEX     0x3D4\r
+#define CRTC_H_TOTAL   0\r
+#define CRTC_H_DISPEND 1\r
+#define CRTC_H_BLANK   2\r
+#define CRTC_H_ENDBLANK        3\r
+#define CRTC_H_RETRACE 4\r
+#define CRTC_H_ENDRETRACE 5\r
+#define CRTC_V_TOTAL   6\r
+#define CRTC_OVERFLOW  7\r
+#define CRTC_ROWSCAN   8\r
+#define CRTC_MAXSCANLINE 9\r
+#define CRTC_CURSORSTART 10\r
+#define CRTC_CURSOREND 11\r
+#define CRTC_STARTHIGH 12\r
+#define CRTC_STARTLOW  13\r
+#define CRTC_CURSORHIGH        14\r
+#define CRTC_CURSORLOW 15\r
+#define CRTC_V_RETRACE 16\r
+#define CRTC_V_ENDRETRACE 17\r
+#define CRTC_V_DISPEND 18\r
+#define CRTC_OFFSET    19\r
+#define CRTC_UNDERLINE 20\r
+#define CRTC_V_BLANK   21\r
+#define CRTC_V_ENDBLANK        22\r
+#define CRTC_MODE      23\r
+#define CRTC_LINECOMPARE 24\r
+\r
+\r
+#define GC_INDEX       0x3CE\r
+#define GC_SETRESET    0\r
+#define GC_ENABLESETRESET 1\r
+#define GC_COLORCOMPARE        2\r
+#define GC_DATAROTATE  3\r
+#define GC_READMAP     4\r
+#define GC_MODE                5\r
+#define GC_MISCELLANEOUS 6\r
+#define GC_COLORDONTCARE 7\r
+#define GC_BITMASK     8\r
+\r
+#define ATR_INDEX      0x3c0\r
+#define ATR_MODE       16\r
+#define ATR_OVERSCAN   17\r
+#define ATR_COLORPLANEENABLE 18\r
+#define ATR_PELPAN     19\r
+#define ATR_COLORSELECT        20\r
+\r
+#define        STATUS_REGISTER_1    0x3da\r
+\r
+//===========================================================================\r
+\r
+typedef enum {NOcard,MDAcard,CGAcard,EGAcard,MCGAcard,VGAcard,\r
+                 HGCcard=0x80,HGCPcard,HICcard} cardtype;\r
+\r
+typedef struct\r
+{\r
+  int  width,\r
+       height,\r
+       orgx,orgy,\r
+       xl,yl,xh,yh,\r
+       shifts;\r
+} spritetabletype;\r
+\r
+typedef        struct\r
+{\r
+       unsigned        sourceoffset[MAXSHIFTS];\r
+       unsigned        planesize[MAXSHIFTS];\r
+       unsigned        width[MAXSHIFTS];\r
+       byte            data[];\r
+} spritetype;          // the memptr for each sprite points to this\r
+\r
+typedef struct\r
+{\r
+       int width,height;\r
+} pictabletype;\r
+\r
+\r
+typedef struct\r
+{\r
+       int height;\r
+       int location[256];\r
+       char width[256];\r
+} fontstruct;\r
+\r
+\r
+typedef enum {CGAgr,EGAgr,VGAgr} grtype;\r
+\r
+//===========================================================================\r
+\r
+extern cardtype        videocard;              // set by VW_Startup\r
+extern grtype          grmode;                 // CGAgr, EGAgr, VGAgr\r
+\r
+extern unsigned        bufferofs;              // hidden port to draw to before displaying\r
+extern unsigned        displayofs;             // origin of port on visable screen\r
+extern unsigned        panx,pany;              // panning adjustments inside port in pixels\r
+extern unsigned        pansx,pansy;\r
+extern unsigned        panadjust;              // panx/pany adjusted by screen resolution\r
+\r
+extern unsigned        screenseg;              // normally 0xa000 or buffer segment\r
+\r
+extern unsigned        linewidth;\r
+extern unsigned        ylookup[VIRTUALHEIGHT];\r
+\r
+extern boolean         screenfaded;\r
+extern char            colors[7][17];  // pallets for fades\r
+\r
+extern pictabletype    _seg *pictable;\r
+extern pictabletype    _seg *picmtable;\r
+extern spritetabletype _seg *spritetable;\r
+\r
+extern unsigned        fontnumber;             // 0 based font number for drawing\r
+extern int                     px,py;\r
+extern byte            pdrawmode,fontcolor;\r
+\r
+extern int                     bordercolor;\r
+\r
+//\r
+// asm globals\r
+//\r
+\r
+extern unsigned        *shifttabletable[8];\r
+extern unsigned        bufferwidth,bufferheight,screenspot;    // used by font drawing stuff\r
+\r
+\r
+\r
+//===========================================================================\r
+\r
+\r
+void   VW_Startup (void);\r
+void   VW_Shutdown (void);\r
+\r
+cardtype       VW_VideoID (void);\r
+\r
+//\r
+// EGA hardware routines\r
+//\r
+\r
+#define EGAWRITEMODE(x) asm{cli;mov dx,GC_INDEX;mov ax,GC_MODE+256*x;out dx,ax;sti;}\r
+#define EGABITMASK(x) asm{cli;mov dx,GC_INDEX;mov ax,GC_BITMASK+256*x;out dx,ax;sti;}\r
+#define EGAMAPMASK(x) asm{cli;mov dx,SC_INDEX;mov ax,SC_MAPMASK+x*256;out dx,ax;sti;}\r
+#define EGAREADMAP(x) asm{cli;mov dx,GC_INDEX;mov ax,GC_READMAP+x*256;out dx,ax;sti;}\r
+\r
+void   VW_SetLineWidth(int width);\r
+void   VW_SetSplitScreen(int width);\r
+void   VW_SetScreen (unsigned CRTC, unsigned pelpan);\r
+\r
+void   VW_SetScreenMode (int grmode);\r
+void   VW_ClearVideo (int color);\r
+void   VW_WaitVBL (int number);\r
+\r
+void   VW_ColorBorder (int color);\r
+void   VW_SetPalette(byte *palette);\r
+void   VW_SetDefaultColors(void);\r
+void   VW_FadeOut(void);\r
+void   VW_FadeIn(void);\r
+void   VW_FadeUp(void);\r
+void   VW_FadeDown(void);\r
+\r
+void   VW_SetAtrReg (int reg, int value);\r
+\r
+//\r
+// block primitives\r
+//\r
+\r
+void VW_MaskBlock(memptr segm,unsigned ofs,unsigned dest,\r
+       unsigned wide,unsigned height,unsigned planesize);\r
+void VW_MemToScreen(memptr source,unsigned dest,unsigned width,unsigned height);\r
+void VW_MemToScreen2x(memptr source,unsigned dest,unsigned width,unsigned height);\r
+void VW_ScreenToMem(unsigned source,memptr dest,unsigned width,unsigned height);\r
+void VW_ScreenToScreen(unsigned source,unsigned dest,unsigned width,unsigned height);\r
+\r
+\r
+//\r
+// block addressable routines\r
+//\r
+\r
+void VW_DrawTile8(unsigned x, unsigned y, unsigned tile);\r
+\r
+#if GRMODE == EGAGR\r
+\r
+#define VW_DrawTile8M(x,y,t) \\r
+       VW_MaskBlock(grsegs[STARTTILE8M],(t)*40,bufferofs+ylookup[y]+(x),1,8,8)\r
+#define VW_DrawTile16(x,y,t) \\r
+       VW_MemToScreen(grsegs[STARTTILE16+t],bufferofs+ylookup[y]+(x),2,16)\r
+#define VW_DrawTile16M(x,y,t) \\r
+       VW_MaskBlock(grsegs[STARTTILE16M],(t)*160,bufferofs+ylookup[y]+(x),2,16,32)\r
+\r
+#endif\r
+\r
+#if GRMODE == CGAGR\r
+\r
+#define VW_DrawTile8M(x,y,t) \\r
+       VW_MaskBlock(grsegs[STARTTILE8M],(t)*32,bufferofs+ylookup[y]+(x),2,8,16)\r
+#define VW_DrawTile16(x,y,t) \\r
+       VW_MemToScreen(grsegs[STARTTILE16+t],bufferofs+ylookup[y]+(x),4,16)\r
+#define VW_DrawTile16M(x,y,t) \\r
+       VW_MaskBlock(grsegs[STARTTILE16M],(t)*128,bufferofs+ylookup[y]+(x),4,16,64)\r
+\r
+#endif\r
+\r
+void VW_DrawPic(unsigned x, unsigned y, unsigned chunknum);\r
+void VW_DrawPic2x(unsigned x, unsigned y, unsigned chunknum);\r
+void VW_DrawMPic(unsigned x, unsigned y, unsigned chunknum);\r
+void VW_ClipDrawMPic(unsigned x, int y, unsigned chunknum);\r
+\r
+//\r
+// pixel addressable routines\r
+//\r
+void   VW_MeasurePropString (char far *string, word *width, word *height);\r
+void   VW_MeasureMPropString  (char far *string, word *width, word *height);\r
+\r
+void VW_DrawPropString (char far *string);\r
+void VW_DrawMPropString (char far *string);\r
+void VW_DrawSprite(int x, int y, unsigned sprite);\r
+void VW_Plot(unsigned x, unsigned y, unsigned color);\r
+void VW_Hlin(unsigned xl, unsigned xh, unsigned y, unsigned color);\r
+void VW_Vlin(unsigned yl, unsigned yh, unsigned x, unsigned color);\r
+void VW_Bar (unsigned x, unsigned y, unsigned width, unsigned height,\r
+       unsigned color);\r
+\r
+//===========================================================================\r
+\r
+//\r
+// Double buffer management routines\r
+//\r
+\r
+void VW_InitDoubleBuffer (void);\r
+void VW_FixRefreshBuffer (void);\r
+int     VW_MarkUpdateBlock (int x1, int y1, int x2, int y2);\r
+void VW_UpdateScreen (void);\r
+void VW_CGAFullUpdate (void);\r
+\r
+//\r
+// cursor\r
+//\r
+\r
+void VW_ShowCursor (void);\r
+void VW_HideCursor (void);\r
+void VW_MoveCursor (int x, int y);\r
+void VW_SetCursor (int spritenum);\r
+void VW_FreeCursor (void);\r
+\r
+//\r
+// mode independant routines\r
+// coordinates in pixels, rounded to best screen res\r
+// regions marked in double buffer\r
+//\r
+\r
+void VWB_DrawTile8 (int x, int y, int tile);\r
+void VWB_DrawTile8M (int x, int y, int tile);\r
+void VWB_DrawTile16 (int x, int y, int tile);\r
+void VWB_DrawTile16M (int x, int y, int tile);\r
+void VWB_DrawPic (int x, int y, int chunknum);\r
+void VWB_DrawMPic(int x, int y, int chunknum);\r
+void VWB_Bar (int x, int y, int width, int height, int color);\r
+\r
+void VWB_DrawPropString         (char far *string);\r
+void VWB_DrawMPropString (char far *string);\r
+void VWB_DrawSprite (int x, int y, int chunknum);\r
+void VWB_Plot (int x, int y, int color);\r
+void VWB_Hlin (int x1, int x2, int y, int color);\r
+void VWB_Vlin (int y1, int y2, int x, int color);\r
+\r
+void VWL_MeasureString (char far *string, word *width, word *height, fontstruct _seg *font);\r
+//===========================================================================\r
diff --git a/16/cawat/ID_VW_A.ASM b/16/cawat/ID_VW_A.ASM
new file mode 100644 (file)
index 0000000..d6376b2
--- /dev/null
@@ -0,0 +1,752 @@
+; Catacomb Armageddon Source Code\r
+; Copyright (C) 1993-2014 Flat Rock Software\r
+;\r
+; This program is free software; you can redistribute it and/or modify\r
+; it under the terms of the GNU General Public License as published by\r
+; the Free Software Foundation; either version 2 of the License, or\r
+; (at your option) any later version.\r
+;\r
+; This program is distributed in the hope that it will be useful,\r
+; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+; GNU General Public License for more details.\r
+;\r
+; You should have received a copy of the GNU General Public License along\r
+; with this program; if not, write to the Free Software Foundation, Inc.,\r
+; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+; ID_VW_A.ASM\r
+\r
+IDEAL\r
+MODEL  MEDIUM,C\r
+\r
+INCLUDE        "ID_ASM.EQU"\r
+\r
+WAITFORVBL     =       1                       ; setting to 0 causes setscreen and waitvbl\r
+                                                       ; to skip waiting for VBL (for timing things)\r
+\r
+;============================================================================\r
+\r
+DATASEG\r
+\r
+EXTRN  screenseg                       :WORD\r
+EXTRN  drawofs                         :WORD\r
+EXTRN  bufferofs                       :WORD\r
+EXTRN  displayofs                      :WORD\r
+EXTRN  drawofs                         :WORD\r
+EXTRN  panadjust                       :WORD\r
+EXTRN  ylookup                         :WORD\r
+EXTRN  linewidth                       :WORD\r
+EXTRN  grsegs                          :WORD\r
+EXTRN  updateptr                       :WORD\r
+EXTRN  blockstarts                     :WORD   ;offsets from drawofs for each update block\r
+EXTRN  fontspace                       :WORD\r
+EXTRN  fontnumber                      :WORD\r
+\r
+\r
+planemask      db      ?\r
+planenum       db      ?\r
+screendest     dw      ?\r
+linedelta      dw      ?\r
+\r
+LABEL shiftdata0 WORD\r
+;      dw 0\r
+\r
+if 1\r
+       dw     0,    1,    2,    3,    4,    5,    6,    7,    8,    9,   10,   11,   12,   13\r
+       dw    14,   15,   16,   17,   18,   19,   20,   21,   22,   23,   24,   25,   26,   27\r
+       dw    28,   29,   30,   31,   32,   33,   34,   35,   36,   37,   38,   39,   40,   41\r
+       dw    42,   43,   44,   45,   46,   47,   48,   49,   50,   51,   52,   53,   54,   55\r
+       dw    56,   57,   58,   59,   60,   61,   62,   63,   64,   65,   66,   67,   68,   69\r
+       dw    70,   71,   72,   73,   74,   75,   76,   77,   78,   79,   80,   81,   82,   83\r
+       dw    84,   85,   86,   87,   88,   89,   90,   91,   92,   93,   94,   95,   96,   97\r
+       dw    98,   99,  100,  101,  102,  103,  104,  105,  106,  107,  108,  109,  110,  111\r
+       dw   112,  113,  114,  115,  116,  117,  118,  119,  120,  121,  122,  123,  124,  125\r
+       dw   126,  127,  128,  129,  130,  131,  132,  133,  134,  135,  136,  137,  138,  139\r
+       dw   140,  141,  142,  143,  144,  145,  146,  147,  148,  149,  150,  151,  152,  153\r
+       dw   154,  155,  156,  157,  158,  159,  160,  161,  162,  163,  164,  165,  166,  167\r
+       dw   168,  169,  170,  171,  172,  173,  174,  175,  176,  177,  178,  179,  180,  181\r
+       dw   182,  183,  184,  185,  186,  187,  188,  189,  190,  191,  192,  193,  194,  195\r
+       dw   196,  197,  198,  199,  200,  201,  202,  203,  204,  205,  206,  207,  208,  209\r
+       dw   210,  211,  212,  213,  214,  215,  216,  217,  218,  219,  220,  221,  222,  223\r
+       dw   224,  225,  226,  227,  228,  229,  230,  231,  232,  233,  234,  235,  236,  237\r
+       dw   238,  239,  240,  241,  242,  243,  244,  245,  246,  247,  248,  249,  250,  251\r
+       dw   252,  253,  254,  255\r
+endif\r
+\r
+LABEL shiftdata1 WORD\r
+;      dw 0\r
+\r
+if 1\r
+       dw     0,32768,    1,32769,    2,32770,    3,32771,    4,32772,    5,32773,    6,32774\r
+       dw     7,32775,    8,32776,    9,32777,   10,32778,   11,32779,   12,32780,   13,32781\r
+       dw    14,32782,   15,32783,   16,32784,   17,32785,   18,32786,   19,32787,   20,32788\r
+       dw    21,32789,   22,32790,   23,32791,   24,32792,   25,32793,   26,32794,   27,32795\r
+       dw    28,32796,   29,32797,   30,32798,   31,32799,   32,32800,   33,32801,   34,32802\r
+       dw    35,32803,   36,32804,   37,32805,   38,32806,   39,32807,   40,32808,   41,32809\r
+       dw    42,32810,   43,32811,   44,32812,   45,32813,   46,32814,   47,32815,   48,32816\r
+       dw    49,32817,   50,32818,   51,32819,   52,32820,   53,32821,   54,32822,   55,32823\r
+       dw    56,32824,   57,32825,   58,32826,   59,32827,   60,32828,   61,32829,   62,32830\r
+       dw    63,32831,   64,32832,   65,32833,   66,32834,   67,32835,   68,32836,   69,32837\r
+       dw    70,32838,   71,32839,   72,32840,   73,32841,   74,32842,   75,32843,   76,32844\r
+       dw    77,32845,   78,32846,   79,32847,   80,32848,   81,32849,   82,32850,   83,32851\r
+       dw    84,32852,   85,32853,   86,32854,   87,32855,   88,32856,   89,32857,   90,32858\r
+       dw    91,32859,   92,32860,   93,32861,   94,32862,   95,32863,   96,32864,   97,32865\r
+       dw    98,32866,   99,32867,  100,32868,  101,32869,  102,32870,  103,32871,  104,32872\r
+       dw   105,32873,  106,32874,  107,32875,  108,32876,  109,32877,  110,32878,  111,32879\r
+       dw   112,32880,  113,32881,  114,32882,  115,32883,  116,32884,  117,32885,  118,32886\r
+       dw   119,32887,  120,32888,  121,32889,  122,32890,  123,32891,  124,32892,  125,32893\r
+       dw   126,32894,  127,32895\r
+endif\r
+\r
+LABEL shiftdata2 WORD\r
+       dw     0,16384,32768,49152,    1,16385,32769,49153,    2,16386,32770,49154,    3,16387\r
+       dw 32771,49155,    4,16388,32772,49156,    5,16389,32773,49157,    6,16390,32774,49158\r
+       dw     7,16391,32775,49159,    8,16392,32776,49160,    9,16393,32777,49161,   10,16394\r
+       dw 32778,49162,   11,16395,32779,49163,   12,16396,32780,49164,   13,16397,32781,49165\r
+       dw    14,16398,32782,49166,   15,16399,32783,49167,   16,16400,32784,49168,   17,16401\r
+       dw 32785,49169,   18,16402,32786,49170,   19,16403,32787,49171,   20,16404,32788,49172\r
+       dw    21,16405,32789,49173,   22,16406,32790,49174,   23,16407,32791,49175,   24,16408\r
+       dw 32792,49176,   25,16409,32793,49177,   26,16410,32794,49178,   27,16411,32795,49179\r
+       dw    28,16412,32796,49180,   29,16413,32797,49181,   30,16414,32798,49182,   31,16415\r
+       dw 32799,49183,   32,16416,32800,49184,   33,16417,32801,49185,   34,16418,32802,49186\r
+       dw    35,16419,32803,49187,   36,16420,32804,49188,   37,16421,32805,49189,   38,16422\r
+       dw 32806,49190,   39,16423,32807,49191,   40,16424,32808,49192,   41,16425,32809,49193\r
+       dw    42,16426,32810,49194,   43,16427,32811,49195,   44,16428,32812,49196,   45,16429\r
+       dw 32813,49197,   46,16430,32814,49198,   47,16431,32815,49199,   48,16432,32816,49200\r
+       dw    49,16433,32817,49201,   50,16434,32818,49202,   51,16435,32819,49203,   52,16436\r
+       dw 32820,49204,   53,16437,32821,49205,   54,16438,32822,49206,   55,16439,32823,49207\r
+       dw    56,16440,32824,49208,   57,16441,32825,49209,   58,16442,32826,49210,   59,16443\r
+       dw 32827,49211,   60,16444,32828,49212,   61,16445,32829,49213,   62,16446,32830,49214\r
+       dw    63,16447,32831,49215\r
+\r
+LABEL shiftdata3 WORD\r
+;      dw 0\r
+\r
+if 1\r
+       dw     0, 8192,16384,24576,32768,40960,49152,57344,    1, 8193,16385,24577,32769,40961\r
+       dw 49153,57345,    2, 8194,16386,24578,32770,40962,49154,57346,    3, 8195,16387,24579\r
+       dw 32771,40963,49155,57347,    4, 8196,16388,24580,32772,40964,49156,57348,    5, 8197\r
+       dw 16389,24581,32773,40965,49157,57349,    6, 8198,16390,24582,32774,40966,49158,57350\r
+       dw     7, 8199,16391,24583,32775,40967,49159,57351,    8, 8200,16392,24584,32776,40968\r
+       dw 49160,57352,    9, 8201,16393,24585,32777,40969,49161,57353,   10, 8202,16394,24586\r
+       dw 32778,40970,49162,57354,   11, 8203,16395,24587,32779,40971,49163,57355,   12, 8204\r
+       dw 16396,24588,32780,40972,49164,57356,   13, 8205,16397,24589,32781,40973,49165,57357\r
+       dw    14, 8206,16398,24590,32782,40974,49166,57358,   15, 8207,16399,24591,32783,40975\r
+       dw 49167,57359,   16, 8208,16400,24592,32784,40976,49168,57360,   17, 8209,16401,24593\r
+       dw 32785,40977,49169,57361,   18, 8210,16402,24594,32786,40978,49170,57362,   19, 8211\r
+       dw 16403,24595,32787,40979,49171,57363,   20, 8212,16404,24596,32788,40980,49172,57364\r
+       dw    21, 8213,16405,24597,32789,40981,49173,57365,   22, 8214,16406,24598,32790,40982\r
+       dw 49174,57366,   23, 8215,16407,24599,32791,40983,49175,57367,   24, 8216,16408,24600\r
+       dw 32792,40984,49176,57368,   25, 8217,16409,24601,32793,40985,49177,57369,   26, 8218\r
+       dw 16410,24602,32794,40986,49178,57370,   27, 8219,16411,24603,32795,40987,49179,57371\r
+       dw    28, 8220,16412,24604,32796,40988,49180,57372,   29, 8221,16413,24605,32797,40989\r
+       dw 49181,57373,   30, 8222,16414,24606,32798,40990,49182,57374,   31, 8223,16415,24607\r
+       dw 32799,40991,49183,57375\r
+endif\r
+\r
+LABEL shiftdata4 WORD\r
+       dw     0, 4096, 8192,12288,16384,20480,24576,28672,32768,36864,40960,45056,49152,53248\r
+       dw 57344,61440,    1, 4097, 8193,12289,16385,20481,24577,28673,32769,36865,40961,45057\r
+       dw 49153,53249,57345,61441,    2, 4098, 8194,12290,16386,20482,24578,28674,32770,36866\r
+       dw 40962,45058,49154,53250,57346,61442,    3, 4099, 8195,12291,16387,20483,24579,28675\r
+       dw 32771,36867,40963,45059,49155,53251,57347,61443,    4, 4100, 8196,12292,16388,20484\r
+       dw 24580,28676,32772,36868,40964,45060,49156,53252,57348,61444,    5, 4101, 8197,12293\r
+       dw 16389,20485,24581,28677,32773,36869,40965,45061,49157,53253,57349,61445,    6, 4102\r
+       dw  8198,12294,16390,20486,24582,28678,32774,36870,40966,45062,49158,53254,57350,61446\r
+       dw     7, 4103, 8199,12295,16391,20487,24583,28679,32775,36871,40967,45063,49159,53255\r
+       dw 57351,61447,    8, 4104, 8200,12296,16392,20488,24584,28680,32776,36872,40968,45064\r
+       dw 49160,53256,57352,61448,    9, 4105, 8201,12297,16393,20489,24585,28681,32777,36873\r
+       dw 40969,45065,49161,53257,57353,61449,   10, 4106, 8202,12298,16394,20490,24586,28682\r
+       dw 32778,36874,40970,45066,49162,53258,57354,61450,   11, 4107, 8203,12299,16395,20491\r
+       dw 24587,28683,32779,36875,40971,45067,49163,53259,57355,61451,   12, 4108, 8204,12300\r
+       dw 16396,20492,24588,28684,32780,36876,40972,45068,49164,53260,57356,61452,   13, 4109\r
+       dw  8205,12301,16397,20493,24589,28685,32781,36877,40973,45069,49165,53261,57357,61453\r
+       dw    14, 4110, 8206,12302,16398,20494,24590,28686,32782,36878,40974,45070,49166,53262\r
+       dw 57358,61454,   15, 4111, 8207,12303,16399,20495,24591,28687,32783,36879,40975,45071\r
+       dw 49167,53263,57359,61455\r
+\r
+LABEL shiftdata5 WORD\r
+;      dw 0\r
+\r
+if 1\r
+       dw     0, 2048, 4096, 6144, 8192,10240,12288,14336,16384,18432,20480,22528,24576,26624\r
+       dw 28672,30720,32768,34816,36864,38912,40960,43008,45056,47104,49152,51200,53248,55296\r
+       dw 57344,59392,61440,63488,    1, 2049, 4097, 6145, 8193,10241,12289,14337,16385,18433\r
+       dw 20481,22529,24577,26625,28673,30721,32769,34817,36865,38913,40961,43009,45057,47105\r
+       dw 49153,51201,53249,55297,57345,59393,61441,63489,    2, 2050, 4098, 6146, 8194,10242\r
+       dw 12290,14338,16386,18434,20482,22530,24578,26626,28674,30722,32770,34818,36866,38914\r
+       dw 40962,43010,45058,47106,49154,51202,53250,55298,57346,59394,61442,63490,    3, 2051\r
+       dw  4099, 6147, 8195,10243,12291,14339,16387,18435,20483,22531,24579,26627,28675,30723\r
+       dw 32771,34819,36867,38915,40963,43011,45059,47107,49155,51203,53251,55299,57347,59395\r
+       dw 61443,63491,    4, 2052, 4100, 6148, 8196,10244,12292,14340,16388,18436,20484,22532\r
+       dw 24580,26628,28676,30724,32772,34820,36868,38916,40964,43012,45060,47108,49156,51204\r
+       dw 53252,55300,57348,59396,61444,63492,    5, 2053, 4101, 6149, 8197,10245,12293,14341\r
+       dw 16389,18437,20485,22533,24581,26629,28677,30725,32773,34821,36869,38917,40965,43013\r
+       dw 45061,47109,49157,51205,53253,55301,57349,59397,61445,63493,    6, 2054, 4102, 6150\r
+       dw  8198,10246,12294,14342,16390,18438,20486,22534,24582,26630,28678,30726,32774,34822\r
+       dw 36870,38918,40966,43014,45062,47110,49158,51206,53254,55302,57350,59398,61446,63494\r
+       dw     7, 2055, 4103, 6151, 8199,10247,12295,14343,16391,18439,20487,22535,24583,26631\r
+       dw 28679,30727,32775,34823,36871,38919,40967,43015,45063,47111,49159,51207,53255,55303\r
+       dw 57351,59399,61447,63495\r
+endif\r
+\r
+LABEL shiftdata6 WORD\r
+       dw     0, 1024, 2048, 3072, 4096, 5120, 6144, 7168, 8192, 9216,10240,11264,12288,13312\r
+       dw 14336,15360,16384,17408,18432,19456,20480,21504,22528,23552,24576,25600,26624,27648\r
+       dw 28672,29696,30720,31744,32768,33792,34816,35840,36864,37888,38912,39936,40960,41984\r
+       dw 43008,44032,45056,46080,47104,48128,49152,50176,51200,52224,53248,54272,55296,56320\r
+       dw 57344,58368,59392,60416,61440,62464,63488,64512,    1, 1025, 2049, 3073, 4097, 5121\r
+       dw  6145, 7169, 8193, 9217,10241,11265,12289,13313,14337,15361,16385,17409,18433,19457\r
+       dw 20481,21505,22529,23553,24577,25601,26625,27649,28673,29697,30721,31745,32769,33793\r
+       dw 34817,35841,36865,37889,38913,39937,40961,41985,43009,44033,45057,46081,47105,48129\r
+       dw 49153,50177,51201,52225,53249,54273,55297,56321,57345,58369,59393,60417,61441,62465\r
+       dw 63489,64513,    2, 1026, 2050, 3074, 4098, 5122, 6146, 7170, 8194, 9218,10242,11266\r
+       dw 12290,13314,14338,15362,16386,17410,18434,19458,20482,21506,22530,23554,24578,25602\r
+       dw 26626,27650,28674,29698,30722,31746,32770,33794,34818,35842,36866,37890,38914,39938\r
+       dw 40962,41986,43010,44034,45058,46082,47106,48130,49154,50178,51202,52226,53250,54274\r
+       dw 55298,56322,57346,58370,59394,60418,61442,62466,63490,64514,    3, 1027, 2051, 3075\r
+       dw  4099, 5123, 6147, 7171, 8195, 9219,10243,11267,12291,13315,14339,15363,16387,17411\r
+       dw 18435,19459,20483,21507,22531,23555,24579,25603,26627,27651,28675,29699,30723,31747\r
+       dw 32771,33795,34819,35843,36867,37891,38915,39939,40963,41987,43011,44035,45059,46083\r
+       dw 47107,48131,49155,50179,51203,52227,53251,54275,55299,56323,57347,58371,59395,60419\r
+       dw 61443,62467,63491,64515\r
+\r
+LABEL shiftdata7 WORD\r
+;      dw 0\r
+\r
+if 1\r
+       dw     0,  512, 1024, 1536, 2048, 2560, 3072, 3584, 4096, 4608, 5120, 5632, 6144, 6656\r
+       dw  7168, 7680, 8192, 8704, 9216, 9728,10240,10752,11264,11776,12288,12800,13312,13824\r
+       dw 14336,14848,15360,15872,16384,16896,17408,17920,18432,18944,19456,19968,20480,20992\r
+       dw 21504,22016,22528,23040,23552,24064,24576,25088,25600,26112,26624,27136,27648,28160\r
+       dw 28672,29184,29696,30208,30720,31232,31744,32256,32768,33280,33792,34304,34816,35328\r
+       dw 35840,36352,36864,37376,37888,38400,38912,39424,39936,40448,40960,41472,41984,42496\r
+       dw 43008,43520,44032,44544,45056,45568,46080,46592,47104,47616,48128,48640,49152,49664\r
+       dw 50176,50688,51200,51712,52224,52736,53248,53760,54272,54784,55296,55808,56320,56832\r
+       dw 57344,57856,58368,58880,59392,59904,60416,60928,61440,61952,62464,62976,63488,64000\r
+       dw 64512,65024,    1,  513, 1025, 1537, 2049, 2561, 3073, 3585, 4097, 4609, 5121, 5633\r
+       dw  6145, 6657, 7169, 7681, 8193, 8705, 9217, 9729,10241,10753,11265,11777,12289,12801\r
+       dw 13313,13825,14337,14849,15361,15873,16385,16897,17409,17921,18433,18945,19457,19969\r
+       dw 20481,20993,21505,22017,22529,23041,23553,24065,24577,25089,25601,26113,26625,27137\r
+       dw 27649,28161,28673,29185,29697,30209,30721,31233,31745,32257,32769,33281,33793,34305\r
+       dw 34817,35329,35841,36353,36865,37377,37889,38401,38913,39425,39937,40449,40961,41473\r
+       dw 41985,42497,43009,43521,44033,44545,45057,45569,46081,46593,47105,47617,48129,48641\r
+       dw 49153,49665,50177,50689,51201,51713,52225,52737,53249,53761,54273,54785,55297,55809\r
+       dw 56321,56833,57345,57857,58369,58881,59393,59905,60417,60929,61441,61953,62465,62977\r
+       dw 63489,64001,64513,65025\r
+endif\r
+\r
+shifttabletable        dw      shiftdata0,shiftdata1,shiftdata2,shiftdata3\r
+               dw      shiftdata4,shiftdata5,shiftdata6,shiftdata7\r
+\r
+PUBLIC shifttabletable\r
+\r
+\r
+;============================================================================\r
+\r
+CODESEG\r
+\r
+IFE GRMODE-CGAGR\r
+INCLUDE        "ID_VW_AC.ASM"\r
+ENDIF\r
+\r
+IFE GRMODE-EGAGR\r
+INCLUDE        "ID_VW_AE.ASM"\r
+ENDIF\r
+\r
+IFE GRMODE-VGAGR\r
+INCLUDE        "ID_VW_AV.ASM"\r
+ENDIF\r
+\r
+;============================================================================\r
+;\r
+;                           MISC VIDEO ROUTINES\r
+;\r
+;============================================================================\r
+\r
+;========\r
+;\r
+; VW_WaitVBL (int number)\r
+;\r
+;========\r
+\r
+PROC   VW_WaitVBL number:WORD\r
+PUBLIC VW_WaitVBL\r
+\r
+if WAITFORVBL                          ; skip wait if profiling\r
+\r
+       mov     dx,STATUS_REGISTER_1\r
+\r
+       mov     cx,[number]\r
+\r
+waitvbl1:\r
+       in      al,dx\r
+       test    al,00001000b    ;look for vbl\r
+       jnz     waitvbl1\r
+\r
+waitvbl2:\r
+       in      al,dx\r
+       test    al,00001000b    ;look for vbl\r
+       jz      waitvbl2\r
+\r
+       loop    waitvbl1\r
+\r
+endif\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+;===========================================================================\r
+\r
+\r
+       MASM\r
+;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ\r
+;\r
+; Name:        VW_VideoID\r
+;\r
+; Function:    Detects the presence of various video subsystems\r
+;\r
+; int VideoID;\r
+;\r
+; Subsystem ID values:\r
+;       0  = (none)\r
+;       1  = MDA\r
+;       2  = CGA\r
+;       3  = EGA\r
+;       4  = MCGA\r
+;       5  = VGA\r
+;      80h = HGC\r
+;      81h = HGC+\r
+;      82h = Hercules InColor\r
+;\r
+;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ\r
+\r
+;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ\r
+;\r
+; Equates\r
+;\r
+;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ\r
+VIDstruct      STRUC           ; corresponds to C data structure\r
+\r
+Video0Type     DB      ?       ; first subsystem type\r
+Display0Type   DB      ?       ; display attached to first subsystem\r
+\r
+Video1Type     DB      ?       ; second subsystem type\r
+Display1Type   DB      ?       ; display attached to second subsystem\r
+\r
+VIDstruct      ENDS\r
+\r
+\r
+Device0        EQU     word ptr Video0Type[di]\r
+Device1        EQU     word ptr Video1Type[di]\r
+\r
+\r
+MDA    EQU     1       ; subsystem types\r
+CGA    EQU     2\r
+EGA    EQU     3\r
+MCGA   EQU     4\r
+VGA    EQU     5\r
+HGC    EQU     80h\r
+HGCPlus        EQU     81h\r
+InColor        EQU     82h\r
+\r
+MDADisplay     EQU     1       ; display types\r
+CGADisplay     EQU     2\r
+EGAColorDisplay        EQU     3\r
+PS2MonoDisplay EQU     4\r
+PS2ColorDisplay        EQU     5\r
+\r
+TRUE   EQU     1\r
+FALSE  EQU     0\r
+\r
+;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ\r
+;\r
+; Program\r
+;\r
+;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ\r
+\r
+Results        VIDstruct <>    ;results go here!\r
+\r
+EGADisplays    DB      CGADisplay      ; 0000b, 0001b  (EGA switch values)\r
+       DB      EGAColorDisplay ; 0010b, 0011b\r
+       DB      MDADisplay      ; 0100b, 0101b\r
+       DB      CGADisplay      ; 0110b, 0111b\r
+       DB      EGAColorDisplay ; 1000b, 1001b\r
+       DB      MDADisplay      ; 1010b, 1011b\r
+\r
+DCCtable       DB      0,0     ; translate table for INT 10h func 1Ah\r
+       DB      MDA,MDADisplay\r
+       DB      CGA,CGADisplay\r
+       DB      0,0\r
+       DB      EGA,EGAColorDisplay\r
+       DB      EGA,MDADisplay\r
+       DB      0,0\r
+       DB      VGA,PS2MonoDisplay\r
+       DB      VGA,PS2ColorDisplay\r
+       DB      0,0\r
+       DB      MCGA,EGAColorDisplay\r
+       DB      MCGA,PS2MonoDisplay\r
+       DB      MCGA,PS2ColorDisplay\r
+\r
+TestSequence   DB      TRUE    ; this list of flags and addresses\r
+       DW      FindPS2 ;  determines the order in which this\r
+                       ;  program looks for the various\r
+EGAflag        DB      ?       ;  subsystems\r
+       DW      FindEGA\r
+\r
+CGAflag        DB      ?\r
+       DW      FindCGA\r
+\r
+Monoflag       DB      ?\r
+       DW      FindMono\r
+\r
+NumberOfTests  EQU     ($-TestSequence)/3\r
+\r
+\r
+PUBLIC VW_VideoID\r
+VW_VideoID     PROC\r
+\r
+       push    bp      ; preserve caller registers\r
+       mov     bp,sp\r
+       push    ds\r
+       push    si\r
+       push    di\r
+\r
+       push    cs\r
+       pop     ds\r
+       ASSUME  DS:@Code\r
+\r
+; initialize the data structure that will contain the results\r
+\r
+       lea     di,Results      ; DS:DI -> start of data structure\r
+\r
+       mov     Device0,0       ; zero these variables\r
+       mov     Device1,0\r
+\r
+; look for the various subsystems using the subroutines whose addresses are\r
+; tabulated in TestSequence; each subroutine sets flags in TestSequence\r
+; to indicate whether subsequent subroutines need to be called\r
+\r
+       mov     byte ptr CGAflag,TRUE\r
+       mov     byte ptr EGAflag,TRUE\r
+       mov     byte ptr Monoflag,TRUE\r
+\r
+       mov     cx,NumberOfTests\r
+       mov     si,offset TestSequence\r
+\r
+@@L01: lodsb           ; AL := flag\r
+       test    al,al\r
+       lodsw           ; AX := subroutine address\r
+       jz      @@L02   ; skip subroutine if flag is false\r
+\r
+       push    si\r
+       push    cx\r
+       call    ax      ; call subroutine to detect subsystem\r
+       pop     cx\r
+       pop     si\r
+\r
+@@L02: loop    @@L01\r
+\r
+; determine which subsystem is active\r
+\r
+       call    FindActive\r
+\r
+       mov     al,Results.Video0Type\r
+       mov     ah,0    ; was:  Results.Display0Type\r
+\r
+       pop     di      ; restore caller registers and return\r
+       pop     si\r
+       pop     ds\r
+       mov     sp,bp\r
+       pop     bp\r
+       ret\r
+\r
+VW_VideoID     ENDP\r
+\r
+\r
+;\r
+; FindPS2\r
+;\r
+; This subroutine uses INT 10H function 1Ah to determine the video BIOS\r
+; Display Combination Code (DCC) for each video subsystem present.\r
+;\r
+\r
+FindPS2        PROC    near\r
+\r
+       mov     ax,1A00h\r
+       int     10h     ; call video BIOS for info\r
+\r
+       cmp     al,1Ah\r
+       jne     @@L13   ; exit if function not supported (i.e.,\r
+                       ;  no MCGA or VGA in system)\r
+\r
+; convert BIOS DCCs into specific subsystems & displays\r
+\r
+       mov     cx,bx\r
+       xor     bh,bh   ; BX := DCC for active subsystem\r
+\r
+       or      ch,ch\r
+       jz      @@L11   ; jump if only one subsystem present\r
+\r
+       mov     bl,ch   ; BX := inactive DCC\r
+       add     bx,bx\r
+       mov     ax,[bx+offset DCCtable]\r
+\r
+       mov     Device1,ax\r
+\r
+       mov     bl,cl\r
+       xor     bh,bh   ; BX := active DCC\r
+\r
+@@L11: add     bx,bx\r
+       mov     ax,[bx+offset DCCtable]\r
+\r
+       mov     Device0,ax\r
+\r
+; reset flags for subsystems that have been ruled out\r
+\r
+       mov     byte ptr CGAflag,FALSE\r
+       mov     byte ptr EGAflag,FALSE\r
+       mov     byte ptr Monoflag,FALSE\r
+\r
+       lea     bx,Video0Type[di]  ; if the BIOS reported an MDA ...\r
+       cmp     byte ptr [bx],MDA\r
+       je      @@L12\r
+\r
+       lea     bx,Video1Type[di]\r
+       cmp     byte ptr [bx],MDA\r
+       jne     @@L13\r
+\r
+@@L12: mov     word ptr [bx],0    ; ... Hercules can't be ruled out\r
+       mov     byte ptr Monoflag,TRUE\r
+\r
+@@L13: ret\r
+\r
+FindPS2        ENDP\r
+\r
+\r
+;\r
+; FindEGA\r
+;\r
+; Look for an EGA.  This is done by making a call to an EGA BIOS function\r
+;  which doesn't exist in the default (MDA, CGA) BIOS.\r
+\r
+FindEGA        PROC    near    ; Caller:       AH = flags\r
+                       ; Returns:      AH = flags\r
+                       ;               Video0Type and\r
+                       ;                Display0Type updated\r
+\r
+       mov     bl,10h  ; BL := 10h (return EGA info)\r
+       mov     ah,12h  ; AH := INT 10H function number\r
+       int     10h     ; call EGA BIOS for info\r
+                       ; if EGA BIOS is present,\r
+                       ;  BL <> 10H\r
+                       ;  CL = switch setting\r
+       cmp     bl,10h\r
+       je      @@L22   ; jump if EGA BIOS not present\r
+\r
+       mov     al,cl\r
+       shr     al,1    ; AL := switches/2\r
+       mov     bx,offset EGADisplays\r
+       xlat            ; determine display type from switches\r
+       mov     ah,al   ; AH := display type\r
+       mov     al,EGA  ; AL := subystem type\r
+       call    FoundDevice\r
+\r
+       cmp     ah,MDADisplay\r
+       je      @@L21   ; jump if EGA has a monochrome display\r
+\r
+       mov     CGAflag,FALSE   ; no CGA if EGA has color display\r
+       jmp     short @@L22\r
+\r
+@@L21: mov     Monoflag,FALSE  ; EGA has a mono display, so MDA and\r
+                       ;  Hercules are ruled out\r
+@@L22: ret\r
+\r
+FindEGA        ENDP\r
+\r
+;\r
+; FindCGA\r
+;\r
+; This is done by looking for the CGA's 6845 CRTC at I/O port 3D4H.\r
+;\r
+FindCGA        PROC    near    ; Returns:      VIDstruct updated\r
+\r
+       mov     dx,3D4h ; DX := CRTC address port\r
+       call    Find6845\r
+       jc      @@L31   ; jump if not present\r
+\r
+       mov     al,CGA\r
+       mov     ah,CGADisplay\r
+       call    FoundDevice\r
+\r
+@@L31: ret\r
+\r
+FindCGA        ENDP\r
+\r
+;\r
+; FindMono\r
+;\r
+; This is done by looking for the MDA's 6845 CRTC at I/O port 3B4H.  If\r
+; a 6845 is found, the subroutine distinguishes between an MDA\r
+; and a Hercules adapter by monitoring bit 7 of the CRT Status byte.\r
+; This bit changes on Hercules adapters but does not change on an MDA.\r
+;\r
+; The various Hercules adapters are identified by bits 4 through 6 of\r
+; the CRT Status value:\r
+;\r
+; 000b = HGC\r
+; 001b = HGC+\r
+; 101b = InColor card\r
+;\r
+\r
+FindMono       PROC    near    ; Returns:      VIDstruct updated\r
+\r
+       mov     dx,3B4h ; DX := CRTC address port\r
+       call    Find6845\r
+       jc      @@L44   ; jump if not present\r
+\r
+       mov     dl,0BAh ; DX := 3BAh (status port)\r
+       in      al,dx\r
+       and     al,80h\r
+       mov     ah,al   ; AH := bit 7 (vertical sync on HGC)\r
+\r
+       mov     cx,8000h        ; do this 32768 times\r
+@@L41: in      al,dx\r
+       and     al,80h  ; isolate bit 7\r
+       cmp     ah,al\r
+       loope   @@L41   ; wait for bit 7 to change\r
+       jne     @@L42   ; if bit 7 changed, it's a Hercules\r
+\r
+       mov     al,MDA  ; if bit 7 didn't change, it's an MDA\r
+       mov     ah,MDADisplay\r
+       call    FoundDevice\r
+       jmp     short @@L44\r
+\r
+@@L42: in      al,dx\r
+       mov     dl,al   ; DL := value from status port\r
+       and     dl,01110000b    ; mask bits 4 thru 6\r
+\r
+       mov     ah,MDADisplay   ; assume it's a monochrome display\r
+\r
+       mov     al,HGCPlus      ; look for an HGC+\r
+       cmp     dl,00010000b\r
+       je      @@L43   ; jump if it's an HGC+\r
+\r
+       mov     al,HGC  ; look for an InColor card or HGC\r
+       cmp     dl,01010000b\r
+       jne     @@L43   ; jump if it's not an InColor card\r
+\r
+       mov     al,InColor      ; it's an InColor card\r
+       mov     ah,EGAColorDisplay\r
+\r
+@@L43: call    FoundDevice\r
+\r
+@@L44: ret\r
+\r
+FindMono       ENDP\r
+\r
+;\r
+; Find6845\r
+;\r
+; This routine detects the presence of the CRTC on a MDA, CGA or HGC.\r
+; The technique is to write and read register 0Fh of the chip (cursor\r
+; low).  If the same value is read as written, assume the chip is\r
+; present at the specified port addr.\r
+;\r
+\r
+Find6845       PROC    near    ; Caller:  DX = port addr\r
+                       ; Returns: cf set if not present\r
+       mov     al,0Fh\r
+       out     dx,al   ; select 6845 reg 0Fh (Cursor Low)\r
+       inc     dx\r
+       in      al,dx   ; AL := current Cursor Low value\r
+       mov     ah,al   ; preserve in AH\r
+       mov     al,66h  ; AL := arbitrary value\r
+       out     dx,al   ; try to write to 6845\r
+\r
+       mov     cx,100h\r
+@@L51: loop    @@L51   ; wait for 6845 to respond\r
+\r
+       in      al,dx\r
+       xchg    ah,al   ; AH := returned value\r
+                       ; AL := original value\r
+       out     dx,al   ; restore original value\r
+\r
+       cmp     ah,66h  ; test whether 6845 responded\r
+       je      @@L52   ; jump if it did (cf is reset)\r
+\r
+       stc             ; set carry flag if no 6845 present\r
+\r
+@@L52: ret\r
+\r
+Find6845       ENDP\r
+\r
+\r
+;\r
+; FindActive\r
+;\r
+; This subroutine stores the currently active device as Device0.  The\r
+; current video mode determines which subsystem is active.\r
+;\r
+\r
+FindActive     PROC    near\r
+\r
+       cmp     word ptr Device1,0\r
+       je      @@L63   ; exit if only one subsystem\r
+\r
+       cmp     Video0Type[di],4        ; exit if MCGA or VGA present\r
+       jge     @@L63   ;  (INT 10H function 1AH\r
+       cmp     Video1Type[di],4        ;  already did the work)\r
+       jge     @@L63\r
+\r
+       mov     ah,0Fh\r
+       int     10h     ; AL := current BIOS video mode\r
+\r
+       and     al,7\r
+       cmp     al,7    ; jump if monochrome\r
+       je      @@L61   ;  (mode 7 or 0Fh)\r
+\r
+       cmp     Display0Type[di],MDADisplay\r
+       jne     @@L63   ; exit if Display0 is color\r
+       jmp     short @@L62\r
+\r
+@@L61: cmp     Display0Type[di],MDADisplay\r
+       je      @@L63   ; exit if Display0 is monochrome\r
+\r
+@@L62: mov     ax,Device0      ; make Device0 currently active\r
+       xchg    ax,Device1\r
+       mov     Device0,ax\r
+\r
+@@L63: ret\r
+\r
+FindActive     ENDP\r
+\r
+\r
+;\r
+; FoundDevice\r
+;\r
+; This routine updates the list of subsystems.\r
+;\r
+\r
+FoundDevice    PROC    near    ; Caller:    AH = display #\r
+                       ;            AL = subsystem #\r
+                       ; Destroys:  BX\r
+       lea     bx,Video0Type[di]\r
+       cmp     byte ptr [bx],0\r
+       je      @@L71   ; jump if 1st subsystem\r
+\r
+       lea     bx,Video1Type[di]       ; must be 2nd subsystem\r
+\r
+@@L71: mov     [bx],ax ; update list entry\r
+       ret\r
+\r
+FoundDevice    ENDP\r
+\r
+IDEAL\r
+\r
+\r
+\r
+END\r
diff --git a/16/cawat/ID_VW_AC.ASM b/16/cawat/ID_VW_AC.ASM
new file mode 100644 (file)
index 0000000..97352be
--- /dev/null
@@ -0,0 +1,1485 @@
+; Catacomb Armageddon Source Code\r
+; Copyright (C) 1993-2014 Flat Rock Software\r
+;\r
+; This program is free software; you can redistribute it and/or modify\r
+; it under the terms of the GNU General Public License as published by\r
+; the Free Software Foundation; either version 2 of the License, or\r
+; (at your option) any later version.\r
+;\r
+; This program is distributed in the hope that it will be useful,\r
+; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+; GNU General Public License for more details.\r
+;\r
+; You should have received a copy of the GNU General Public License along\r
+; with this program; if not, write to the Free Software Foundation, Inc.,\r
+; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+;=================================\r
+;\r
+; CGA view manager routines\r
+;\r
+;=================================\r
+\r
+;============================================================================\r
+;\r
+; All of these routines draw into a floating virtual screen segment in main\r
+; memory.  bufferofs points to the origin of the drawing page in screenseg.\r
+; The routines that write out words must take into account buffer wrapping\r
+; and not write a word at 0xffff (which causes an exception on 386s).\r
+;\r
+; The direction flag should be clear\r
+;\r
+;============================================================================\r
+\r
+DATASEG\r
+\r
+plotpixels     db      0c0h,030h,0ch,03h\r
+colorbyte      db      000000b,01010101b,10101010b,11111111b\r
+colorword      dw      0,5555h,0aaaah,0ffffh\r
+\r
+CODESEG\r
+\r
+;============================================================================\r
+;\r
+; VW_Plot (int x,y,color)\r
+;\r
+;============================================================================\r
+\r
+\r
+PROC   VW_Plot x:WORD, y:WORD, color:WORD\r
+PUBLIC VW_Plot\r
+USES   SI,DI\r
+\r
+       mov     es,[screenseg]\r
+\r
+       mov     di,[bufferofs]\r
+       mov     bx,[y]\r
+       shl     bx,1\r
+       add     di,[ylookup+bx]\r
+       mov     bx,[x]\r
+       mov     ax,bx\r
+       shr     ax,1\r
+       shr     ax,1\r
+       add     di,ax                           ; di = byte on screen\r
+\r
+       and     bx,3\r
+       mov     ah,[plotpixels+bx]\r
+       mov     bx,[color]\r
+       mov     cl,[colorbyte+bx]\r
+       and     cl,ah\r
+       not     ah\r
+\r
+       mov     al,[es:di]\r
+       and     al,ah                           ; mask off other pixels\r
+       or      al,cl\r
+       stosb\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_Vlin (int yl,yh,x,color)\r
+;\r
+;============================================================================\r
+\r
+PROC   VW_Vlin yl:WORD, yh:WORD, x:WORD, color:WORD\r
+PUBLIC VW_Vlin\r
+USES   SI,DI\r
+\r
+       mov     es,[screenseg]\r
+\r
+       mov     di,[bufferofs]\r
+       mov     bx,[yl]\r
+       shl     bx,1\r
+       add     di,[ylookup+bx]\r
+       mov     bx,[x]\r
+       mov     ax,bx\r
+       shr     ax,1\r
+       shr     ax,1\r
+       add     di,ax                           ; di = byte on screen\r
+\r
+       and     bx,3\r
+       mov     ah,[plotpixels+bx]\r
+       mov     bx,[color]\r
+       mov     bl,[colorbyte+bx]\r
+       and     bl,ah\r
+       not     ah\r
+\r
+       mov     cx,[yh]\r
+       sub     cx,[yl]\r
+       inc     cx                                      ;number of pixels to plot\r
+\r
+       mov     dx,[linewidth]\r
+\r
+@@plot:\r
+       mov     al,[es:di]\r
+       and     al,ah                           ; mask off other pixels\r
+       or      al,bl\r
+       mov [es:di],al\r
+       add     di,dx\r
+       loop    @@plot\r
+\r
+       ret\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+\r
+\r
+;===================\r
+;\r
+; VW_DrawTile8\r
+;\r
+; xcoord in bytes (8 pixels), ycoord in pixels\r
+; All Tile8s are in one grseg, so an offset is calculated inside it\r
+;\r
+; DONE\r
+;\r
+;===================\r
+\r
+PROC   VW_DrawTile8    xcoord:WORD, ycoord:WORD, tile:WORD\r
+PUBLIC VW_DrawTile8\r
+USES   SI,DI\r
+\r
+       mov     es,[screenseg]\r
+\r
+       mov     di,[bufferofs]\r
+       add     di,[xcoord]\r
+       mov     bx,[ycoord]\r
+       shl     bx,1\r
+       add     di,[ylookup+bx]\r
+\r
+       mov     bx,[linewidth]\r
+       sub     bx,2\r
+\r
+       mov     si,[tile]\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1\r
+\r
+       mov     ds,[grsegs+STARTTILE8*2] ; segment for all tile8s\r
+\r
+;\r
+; start drawing\r
+;\r
+\r
+REPT   7\r
+       movsb                                           ;no word moves because of segment wrapping\r
+       movsb\r
+       add     di,bx\r
+ENDM\r
+       movsb\r
+       movsb\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_MaskBlock\r
+;\r
+; Draws a masked block shape to the screen.  bufferofs is NOT accounted for.\r
+; The mask comes first, then the data.  Seperate unwound routines are used\r
+; to speed drawing.\r
+;\r
+; Mask blocks will allways be an even width because of the way IGRAB works\r
+;\r
+; DONE\r
+;\r
+;============================================================================\r
+\r
+DATASEG\r
+\r
+UNWOUNDMASKS   =       18\r
+\r
+\r
+maskroutines   dw      mask0,mask0,mask2E,mask2O,mask4E,mask4O\r
+                               dw      mask6E,mask6O,mask8E,mask8O,mask10E,mask10O\r
+                               dw      mask12E,mask12O,mask14E,mask14O,mask16E,mask16O\r
+                               dw      mask18E,mask18O\r
+\r
+\r
+routinetouse   dw      ?\r
+\r
+CODESEG\r
+\r
+PROC   VW_MaskBlock    segm:WORD, ofs:WORD, dest:WORD, wide:WORD, height:WORD, planesize:WORD\r
+PUBLIC VW_MaskBlock\r
+USES   SI,DI\r
+\r
+       mov     es,[screenseg]\r
+\r
+       mov     di,[wide]\r
+       mov     dx,[linewidth]\r
+       sub     dx,di                                   ;dx = delta to start of next line\r
+\r
+       mov     bx,[planesize]                  ; si+bx = data location\r
+\r
+       cmp     di,UNWOUNDMASKS\r
+       jbe     @@unwoundroutine\r
+\r
+;==============\r
+;\r
+; General purpose masked block drawing.  This could be optimised into\r
+; four routines to use words, but few play loop sprites should be this big!\r
+;\r
+;==============\r
+\r
+       mov     [ss:linedelta],dx\r
+       mov     ds,[segm]\r
+       mov     si,[ofs]\r
+       mov     di,[dest]\r
+       mov     dx,[height]                             ;scan lines to draw\r
+\r
+@@lineloopgen:\r
+       mov     cx,[wide]\r
+@@byteloop:\r
+       mov     al,[es:di]\r
+       and     al,[si]\r
+       or      al,[bx+si]\r
+       inc     si\r
+       stosb\r
+       loop    @@byteloop\r
+\r
+       add     di,[ss:linedelta]\r
+       dec     dx\r
+       jnz     @@lineloopgen\r
+\r
+mask0:\r
+       mov     ax,ss\r
+       mov     ds,ax\r
+       ret                                                     ;width of 0 = no drawing\r
+\r
+\r
+;=================\r
+;\r
+; use the unwound routines\r
+;\r
+;=================\r
+\r
+@@unwoundroutine:\r
+       shr     di,1                                    ;we only have even width unwound routines\r
+       mov     cx,[dest]\r
+       shr     cx,1\r
+       rcl     di,1                                    ;shift a 1 in if destination is odd\r
+       shl     di,1\r
+       mov     ax,[maskroutines+di]    ;call the right routine\r
+\r
+       mov     ds,[segm]\r
+       mov     si,[ofs]\r
+       mov     di,[dest]\r
+       mov     cx,[height]                             ;scan lines to draw\r
+\r
+       jmp ax                                          ;draw it\r
+\r
+;=================\r
+;\r
+; Horizontally unwound routines to draw certain masked blocks faster\r
+;\r
+;=================\r
+\r
+MACRO  MASKBYTE\r
+       mov     al,[es:di]\r
+       and     al,[si]\r
+       or      al,[bx+si]\r
+       inc     si\r
+       stosb\r
+ENDM\r
+\r
+MACRO  MASKWORD\r
+       mov     ax,[es:di]\r
+       and     ax,[si]\r
+       or      ax,[bx+si]\r
+       inc     si\r
+       inc     si\r
+       stosw\r
+ENDM\r
+\r
+MACRO  SPRITELOOP      addr\r
+       add     di,dx\r
+       loop    addr\r
+       mov     ax,ss\r
+       mov     ds,ax\r
+       ret\r
+ENDM\r
+\r
+\r
+EVEN\r
+mask2E:\r
+       MASKWORD\r
+       SPRITELOOP      mask2E\r
+\r
+EVEN\r
+mask2O:\r
+       MASKBYTE\r
+       MASKBYTE\r
+       SPRITELOOP      mask2O\r
+\r
+EVEN\r
+mask4E:\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask4E\r
+\r
+EVEN\r
+mask4O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask4O\r
+\r
+EVEN\r
+mask6E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask6E\r
+\r
+EVEN\r
+mask6O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask6O\r
+\r
+EVEN\r
+mask8E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask8E\r
+\r
+EVEN\r
+mask8O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask8O\r
+\r
+EVEN\r
+mask10E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask10E\r
+\r
+EVEN\r
+mask10O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask10O\r
+\r
+EVEN\r
+mask12E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask12E\r
+\r
+EVEN\r
+mask12O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask12O\r
+\r
+EVEN\r
+mask14E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask14E\r
+\r
+EVEN\r
+mask14O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask14O\r
+\r
+EVEN\r
+mask16E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask16E\r
+\r
+EVEN\r
+mask16O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask16O\r
+\r
+EVEN\r
+mask18E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask18E\r
+\r
+EVEN\r
+mask18O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask18O\r
+\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_ScreenToScreen\r
+;\r
+; Basic block copy routine.  Copies one block of screen memory to another,\r
+; bufferofs is NOT accounted for.\r
+;\r
+; DONE\r
+;\r
+;============================================================================\r
+\r
+PROC   VW_ScreenToScreen       source:WORD, dest:WORD, wide:WORD, height:WORD\r
+PUBLIC VW_ScreenToScreen\r
+USES   SI,DI\r
+\r
+       mov     bx,[linewidth]\r
+       sub     bx,[wide]\r
+\r
+       mov     ax,[screenseg]\r
+       mov     es,ax\r
+       mov     ds,ax\r
+\r
+       mov     si,[source]\r
+       mov     di,[dest]                               ;start at same place in all planes\r
+       mov     dx,[height]                             ;scan lines to draw\r
+       mov     ax,[wide]\r
+;\r
+; if the width, source, and dest are all even, use word moves\r
+; This is allways the case in the CGA refresh\r
+;\r
+       test    ax,1\r
+       jnz     @@bytelineloop\r
+       test    si,1\r
+       jnz     @@bytelineloop\r
+       test    di,1\r
+       jnz     @@bytelineloop\r
+\r
+       shr     ax,1\r
+@@wordlineloop:\r
+       mov     cx,ax\r
+       rep     movsw\r
+       add     si,bx\r
+       add     di,bx\r
+\r
+       dec     dx\r
+       jnz     @@wordlineloop\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+@@bytelineloop:\r
+       mov     cx,ax\r
+       rep     movsb\r
+       add     si,bx\r
+       add     di,bx\r
+\r
+       dec     dx\r
+       jnz     @@bytelineloop\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_MemToScreen\r
+;\r
+; Basic block drawing routine. Takes a block shape at segment pointer source\r
+; of width by height data, and draws it to dest in the virtual screen,\r
+; based on linewidth.  bufferofs is NOT accounted for.\r
+; There are four drawing routines to provide the best optimized code while\r
+; accounting for odd segment wrappings due to the floating screens.\r
+;\r
+; DONE\r
+;\r
+;============================================================================\r
+\r
+DATASEG\r
+\r
+memtoscreentable       dw      eventoeven,eventoodd,oddtoeven,oddtoodd\r
+\r
+CODESEG\r
+\r
+\r
+PROC   VW_MemToScreen  source:WORD, dest:WORD, wide:WORD, height:WORD\r
+PUBLIC VW_MemToScreen\r
+USES   SI,DI\r
+\r
+       mov     es,[screenseg]\r
+\r
+       mov     bx,[linewidth]\r
+       sub     bx,[wide]\r
+\r
+       mov     ds,[source]\r
+\r
+       xor     si,si                                   ;block is segment aligned\r
+\r
+       xor     di,di\r
+       shr     [wide],1                                ;change wide to words, and see if carry is set\r
+       rcl     di,1                                    ;1 if wide is odd\r
+       mov     ax,[dest]\r
+       shr     ax,1\r
+       rcl     di,1                                    ;shift a 1 in if destination is odd\r
+       shl     di,1                                    ;to index into a word width table\r
+       mov     dx,[height]                             ;scan lines to draw\r
+       mov     ax,[wide]\r
+       jmp     [ss:memtoscreentable+di]        ;call the right routine\r
+\r
+;==============\r
+;\r
+; Copy an even width block to an even destination address\r
+;\r
+;==============\r
+\r
+eventoeven:\r
+       mov     di,[dest]                               ;start at same place in all planes\r
+EVEN\r
+@@lineloopEE:\r
+       mov     cx,ax\r
+       rep     movsw\r
+       add     di,bx\r
+       dec     dx\r
+       jnz     @@lineloopEE\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+;==============\r
+;\r
+; Copy an odd width block to an even video address\r
+;\r
+;==============\r
+\r
+oddtoeven:\r
+       mov     di,[dest]                               ;start at same place in all planes\r
+EVEN\r
+@@lineloopOE:\r
+       mov     cx,ax\r
+       rep     movsw\r
+       movsb                                           ;copy the last byte\r
+       add     di,bx\r
+       dec     dx\r
+       jnz     @@lineloopOE\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+;==============\r
+;\r
+; Copy an even width block to an odd video address\r
+;\r
+;==============\r
+\r
+eventoodd:\r
+       mov     di,[dest]                               ;start at same place in all planes\r
+       dec     ax                                              ;one word has to be handled seperately\r
+EVEN\r
+@@lineloopEO:\r
+       movsb\r
+       mov     cx,ax\r
+       rep     movsw\r
+       movsb\r
+       add     di,bx\r
+       dec     dx\r
+       jnz     @@lineloopEO\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+;==============\r
+;\r
+; Copy an odd width block to an odd video address\r
+;\r
+;==============\r
+\r
+oddtoodd:\r
+       mov     di,[dest]                               ;start at same place in all planes\r
+EVEN\r
+@@lineloopOO:\r
+       movsb\r
+       mov     cx,ax\r
+       rep     movsw\r
+       add     di,bx\r
+       dec     dx\r
+       jnz     @@lineloopOO\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+       ret\r
+\r
+\r
+ENDP\r
+\r
+;===========================================================================\r
+;\r
+; VW_ScreenToMem\r
+;\r
+; Copies a block of video memory to main memory, in order from planes 0-3.\r
+; This could be optimized along the lines of VW_MemToScreen to take advantage\r
+; of word copies, but this is an infrequently called routine.\r
+;\r
+; DONE\r
+;\r
+;===========================================================================\r
+\r
+PROC   VW_ScreenToMem  source:WORD, dest:WORD, wide:WORD, height:WORD\r
+PUBLIC VW_ScreenToMem\r
+USES   SI,DI\r
+\r
+       mov     es,[dest]\r
+\r
+       mov     bx,[linewidth]\r
+       sub     bx,[wide]\r
+\r
+       mov     ds,[screenseg]\r
+\r
+       xor     di,di\r
+\r
+       mov     si,[source]\r
+       mov     dx,[height]                             ;scan lines to draw\r
+\r
+@@lineloop:\r
+       mov     cx,[wide]\r
+       rep     movsb\r
+\r
+       add     si,bx\r
+\r
+       dec     dx\r
+       jnz     @@lineloop\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+;===========================================================================\r
+;\r
+;                    MISC CGA ROUTINES\r
+;\r
+;===========================================================================\r
+\r
+;==============\r
+;\r
+; VW_SetScreen\r
+;\r
+; DONE\r
+;\r
+;==============\r
+\r
+PROC   VW_SetScreen  crtc:WORD\r
+PUBLIC VW_SetScreen\r
+\r
+;\r
+; for some reason, my XT's EGA card doesn't like word outs to the CRTC\r
+; index...\r
+;\r
+       cli\r
+\r
+       mov     cx,[crtc]\r
+       mov     dx,CRTC_INDEX\r
+       mov     al,0ch          ;start address high register\r
+       out     dx,al\r
+       inc     dx\r
+       mov     al,ch\r
+       out     dx,al\r
+       dec     dx\r
+       mov     al,0dh          ;start address low register\r
+       out     dx,al\r
+       mov     al,cl\r
+       inc     dx\r
+       out     dx,al\r
+\r
+       sti\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+if NUMFONT+NUMFONTM\r
+\r
+;===========================================================================\r
+;\r
+; GENERAL FONT DRAWING ROUTINES\r
+;\r
+;===========================================================================\r
+\r
+DATASEG\r
+\r
+px     dw      ?                                       ; proportional character drawing coordinates\r
+py     dw      ?\r
+pdrawmode      db      11000b          ; 8 = OR, 24 = XOR, put in GC_DATAROTATE\r
+fontcolor      db      15              ;0-15 mapmask value\r
+\r
+PUBLIC px,py,pdrawmode,fontcolor\r
+\r
+;\r
+; offsets in font structure\r
+;\r
+pcharheight    =       0               ;lines high\r
+charloc                =       2               ;pointers to every character\r
+charwidth      =       514             ;every character's width in pixels\r
+\r
+\r
+propchar       dw      ?                       ; the character number to shift\r
+stringptr      dw      ?,?\r
+\r
+fontcolormask  dw      ?                       ; font color expands into this\r
+\r
+BUFFWIDTH      =       100\r
+BUFFHEIGHT     =   32                  ; must be twice as high as font for masked fonts\r
+\r
+databuffer     db      BUFFWIDTH*BUFFHEIGHT dup (?)\r
+\r
+bufferwidth    dw      ?                                               ; bytes with valid info / line\r
+bufferheight dw        ?                                               ; number of lines currently used\r
+\r
+bufferbyte     dw      ?\r
+bufferbit      dw      ?\r
+PUBLIC bufferwidth,bufferheight,bufferbyte,bufferbit\r
+\r
+screenspot     dw      ?                                               ; where the buffer is going\r
+\r
+bufferextra    dw      ?                                               ; add at end of a line copy\r
+screenextra    dw      ?\r
+\r
+CODESEG\r
+\r
+;======================\r
+;\r
+; Macros to table shift a byte of font\r
+;\r
+;======================\r
+\r
+MACRO  SHIFTNOXOR\r
+       mov     al,[es:bx]              ; source\r
+       xor     ah,ah\r
+       shl     ax,1\r
+       mov     si,ax\r
+       mov     ax,[bp+si]              ; table shift into two bytes\r
+       or      [di],al                 ; or with first byte\r
+       inc     di\r
+       mov     [di],ah                 ; replace next byte\r
+       inc     bx                              ; next source byte\r
+ENDM\r
+\r
+MACRO  SHIFTWITHXOR\r
+       mov     al,[es:bx]              ; source\r
+       xor     ah,ah\r
+       shl     ax,1\r
+       mov     si,ax\r
+       mov     ax,[bp+si]              ; table shift into two bytes\r
+       not     ax\r
+       and     [di],al                 ; and with first byte\r
+       inc     di\r
+       mov     [di],ah                 ; replace next byte\r
+       inc     bx                              ; next source byte\r
+ENDM\r
+\r
+\r
+;=======================\r
+;\r
+; VWL_XORBuffer\r
+;\r
+; Pass buffer start in SI (somewhere in databuffer)\r
+; Draws the buffer to the screen buffer\r
+;\r
+;========================\r
+\r
+PROC   VWL_XORBuffer   NEAR\r
+USES   BP\r
+       mov     bl,[fontcolor]\r
+       xor     bh,bh\r
+       shl     bx,1\r
+       mov     ax,[colorword+bx]\r
+       mov     [fontcolormask],ax\r
+\r
+       mov     es,[screenseg]\r
+       mov     di,[screenspot]\r
+\r
+       mov     bx,[bufferwidth]                ;calculate offsets for end of each line\r
+       mov     [bufferwidth],bx\r
+\r
+       or      bx,bx\r
+       jnz     @@isthere\r
+       ret                                                     ;nothing to draw\r
+\r
+@@isthere:\r
+       test    bx,1\r
+       jnz     @@odd\r
+       jmp     @@even\r
+;\r
+; clear the last byte so word draws can be used\r
+;\r
+@@odd:\r
+       mov     al,0\r
+line   =       0\r
+REPT   BUFFHEIGHT\r
+       mov     [BYTE databuffer+BUFFWIDTH*line+bx],al\r
+line   =       line+1\r
+ENDM\r
+\r
+       inc     bx\r
+@@even:\r
+       mov     ax,[linewidth]\r
+       sub     ax,bx\r
+       mov     [screenextra],ax\r
+       mov     ax,BUFFWIDTH\r
+       sub     ax,bx\r
+       mov     [bufferextra],ax\r
+       mov     dx,bx\r
+       shr     dx,1                                    ;word to copy\r
+\r
+       mov     bx,[bufferheight]               ;lines to copy\r
+       mov     bp,[fontcolormask]\r
+@@lineloop:\r
+       mov     cx,dx\r
+@@wordloop:\r
+       lodsw                                           ;get a word from the buffer\r
+       and     ax,bp\r
+       xor     [es:di],ax                              ;draw it\r
+       add     di,2\r
+       loop    @@wordloop\r
+\r
+       add     si,[bufferextra]\r
+       add     di,[screenextra]\r
+\r
+       dec     bx\r
+       jnz     @@lineloop\r
+\r
+       ret\r
+ENDP\r
+\r
+\r
+DATASEG\r
+\r
+;============================================================================\r
+;\r
+; NON MASKED FONT DRAWING ROUTINES\r
+;\r
+;============================================================================\r
+\r
+if numfont\r
+\r
+DATASEG\r
+\r
+shiftdrawtable dw      0,shift1wide,shift2wide,shift3wide,shift4wide\r
+                               dw              shift5wide,shift6wide\r
+\r
+CODESEG\r
+\r
+;==================\r
+;\r
+; ShiftPropChar\r
+;\r
+; Call with BX = character number (0-255)\r
+; Draws one character to the buffer at bufferbyte/bufferbit, and adjusts\r
+; them to the new position\r
+;\r
+;==================\r
+\r
+PROC   ShiftPropChar   NEAR\r
+\r
+       mov     es,[grsegs+STARTFONT*2] ;segment of font to use\r
+\r
+;\r
+; find character location, width, and height\r
+;\r
+       mov     si,[es:charwidth+bx]\r
+       and     si,0ffh                                 ;SI hold width in pixels\r
+       shl     bx,1\r
+       mov     bx,[es:charloc+bx]              ;BX holds pointer to character data\r
+\r
+;\r
+; look up which shift table to use, based on bufferbit\r
+;\r
+       mov     di,[bufferbit]\r
+       shl     di,1\r
+       mov     bp,[shifttabletable+di] ;BP holds pointer to shift table\r
+\r
+       mov     di,OFFSET databuffer\r
+       add     di,[bufferbyte]                 ;DI holds pointer to buffer\r
+\r
+       mov     cx,[bufferbit]\r
+       add     cx,si                                   ;add twice because pixel == two bits\r
+       add     cx,si                                   ;new bit position\r
+       mov     ax,cx\r
+       and     ax,7\r
+       mov     [bufferbit],ax                  ;new bit position\r
+       mov     ax,cx\r
+       shr     ax,1\r
+       shr     ax,1\r
+       shr     ax,1\r
+       add     [bufferbyte],ax                 ;new byte position\r
+\r
+       add     si,3\r
+       shr     si,1\r
+       shr     si,1                                    ;bytes the character is wide\r
+       shl     si,1                    ;*2 to look up in shiftdrawtable\r
+\r
+       mov     cx,[es:pcharheight]\r
+       mov     dx,BUFFWIDTH\r
+       jmp     [ss:shiftdrawtable+si]  ;procedure to draw this width\r
+\r
+;\r
+; one byte character\r
+;\r
+shift1wide:\r
+       dec     dx\r
+EVEN\r
+@@loop1:\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+\r
+       loop    @@loop1\r
+\r
+       ret\r
+\r
+;\r
+; two byte character\r
+;\r
+shift2wide:\r
+       dec     dx\r
+       dec     dx\r
+EVEN\r
+@@loop2:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+\r
+       loop    @@loop2\r
+\r
+       ret\r
+\r
+;\r
+; three byte character\r
+;\r
+shift3wide:\r
+       sub     dx,3\r
+EVEN\r
+@@loop3:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+\r
+       loop    @@loop3\r
+\r
+       ret\r
+\r
+\r
+;\r
+; four byte character\r
+;\r
+shift4wide:\r
+       sub     dx,4\r
+EVEN\r
+@@loop4:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+\r
+       loop    @@loop4\r
+\r
+       ret\r
+\r
+\r
+;\r
+; five byte character\r
+;\r
+shift5wide:\r
+       sub     dx,5\r
+EVEN\r
+@@loop5:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+\r
+       loop    @@loop5\r
+\r
+       ret\r
+\r
+;\r
+; six byte character\r
+;\r
+shift6wide:\r
+       sub     dx,6\r
+EVEN\r
+@@loop6:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+\r
+       loop    @@loop6\r
+\r
+       ret\r
+\r
+\r
+\r
+ENDP\r
+\r
+;============================================================================\r
+\r
+;==================\r
+;\r
+; VW_DrawPropString\r
+;\r
+; Draws a C string of characters at px/py and advances px\r
+;\r
+;==================\r
+\r
+CODESEG\r
+\r
+PROC   VW_DrawPropString       string:DWORD\r
+PUBLIC VW_DrawPropString\r
+USES   SI,DI\r
+\r
+;\r
+; proportional spaceing, which clears the buffer ahead of it, so only\r
+; clear the first collumn\r
+;\r
+       mov     al,0\r
+line   =       0\r
+REPT   BUFFHEIGHT\r
+       mov     [BYTE databuffer+BUFFWIDTH*line],al\r
+line   =       line+1\r
+ENDM\r
+\r
+;\r
+; shift the characters into the buffer\r
+;\r
+@@shiftchars:\r
+       mov     ax,[px]\r
+       and     ax,3\r
+       shl     ax,1                    ;one pixel == two bits\r
+       mov     [bufferbit],ax\r
+       mov     [bufferbyte],0\r
+\r
+       mov     ax,[WORD string]\r
+       mov     [stringptr],ax\r
+       mov     ax,[WORD string+2]\r
+       mov     [stringptr+2],ax\r
+\r
+@@shiftone:\r
+       mov     es,[stringptr+2]\r
+       mov     bx,[stringptr]\r
+       inc     [stringptr]\r
+       mov     bx,[es:bx]\r
+       xor     bh,bh\r
+       or      bl,bl\r
+       jz      @@allshifted\r
+       call    ShiftPropChar\r
+       jmp     @@shiftone\r
+\r
+@@allshifted:\r
+;\r
+; calculate position to draw buffer on screen\r
+;\r
+       mov     bx,[py]\r
+       shl     bx,1\r
+       mov     di,[ylookup+bx]\r
+       add     di,[bufferofs]\r
+       add     di,[panadjust]\r
+\r
+       mov     ax,[px]\r
+       shr     ax,1\r
+       shr     ax,1            ;x location in bytes\r
+       add     di,ax\r
+       mov     [screenspot],di\r
+\r
+;\r
+; advance px\r
+;\r
+       mov     ax,[bufferbyte]\r
+       shl     ax,1\r
+       shl     ax,1\r
+       mov     bx,[bufferbit]\r
+       shr     bx,1                    ;two bits == one pixel\r
+       or      ax,bx\r
+       add     [px],ax\r
+\r
+;\r
+; draw it\r
+;\r
+       mov     ax,[bufferbyte]\r
+       test    [bufferbit],7\r
+       jz      @@go\r
+       inc     ax                              ;so the partial byte also gets drawn\r
+@@go:\r
+       mov     [bufferwidth],ax\r
+       mov     es,[grsegs+STARTFONT*2]\r
+       mov     ax,[es:pcharheight]\r
+       mov     [bufferheight],ax\r
+\r
+       mov     si,OFFSET databuffer\r
+       call    VWL_XORBuffer\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+endif  ;numfont\r
+\r
+;============================================================================\r
+;\r
+; MASKED FONT DRAWING ROUTINES\r
+;\r
+;============================================================================\r
+\r
+if     numfontm\r
+\r
+DATASEG\r
+\r
+mshiftdrawtable        dw      0,mshift1wide,mshift2wide,mshift3wide\r
+\r
+\r
+CODESEG\r
+\r
+;==================\r
+;\r
+; ShiftMPropChar\r
+;\r
+; Call with BX = character number (0-255)\r
+; Draws one character to the buffer at bufferbyte/bufferbit, and adjusts\r
+; them to the new position\r
+;\r
+;==================\r
+\r
+PROC   ShiftMPropChar  NEAR\r
+\r
+       mov     es,[grsegs+STARTFONTM*2]        ;segment of font to use\r
+\r
+;\r
+; find character location, width, and height\r
+;\r
+       mov     si,[es:charwidth+bx]\r
+       and     si,0ffh                                 ;SI hold width in pixels\r
+       shl     bx,1\r
+       mov     bx,[es:charloc+bx]              ;BX holds pointer to character data\r
+\r
+;\r
+; look up which shift table to use, based on bufferbit\r
+;\r
+       mov     di,[bufferbit]\r
+       shl     di,1\r
+       mov     bp,[shifttabletable+di] ;BP holds pointer to shift table\r
+\r
+       mov     di,OFFSET databuffer\r
+       add     di,[bufferbyte]                 ;DI holds pointer to buffer\r
+\r
+;\r
+; advance position by character width\r
+;\r
+       mov     cx,[bufferbit]\r
+       add     cx,si                                   ;new bit position\r
+       mov     ax,cx\r
+       and     ax,7\r
+       mov     [bufferbit],ax                  ;new bit position\r
+       mov     ax,cx\r
+       shr     ax,1\r
+       shr     ax,1\r
+       shr     ax,1\r
+       add     [bufferbyte],ax                 ;new byte position\r
+\r
+       add     si,7\r
+       shr     si,1\r
+       shr     si,1\r
+       shr     si,1                                    ;bytes the character is wide\r
+       shl     si,1                    ;*2 to look up in shiftdrawtable\r
+\r
+       mov     cx,[es:pcharheight]\r
+       mov     dx,BUFFWIDTH\r
+       jmp     [ss:mshiftdrawtable+si] ;procedure to draw this width\r
+\r
+;\r
+; one byte character\r
+;\r
+mshift1wide:\r
+       dec     dx\r
+\r
+EVEN\r
+@@loop1m:\r
+       SHIFTWITHXOR\r
+       add     di,dx                   ; next line in buffer\r
+\r
+       loop    @@loop1m\r
+\r
+       mov     cx,[es:pcharheight]\r
+\r
+EVEN\r
+@@loop1:\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+       loop    @@loop1\r
+\r
+       ret\r
+\r
+;\r
+; two byte character\r
+;\r
+mshift2wide:\r
+       dec     dx\r
+       dec     dx\r
+EVEN\r
+@@loop2m:\r
+       SHIFTWITHXOR\r
+       SHIFTWITHXOR\r
+       add     di,dx                   ; next line in buffer\r
+\r
+       loop    @@loop2m\r
+\r
+       mov     cx,[es:pcharheight]\r
+\r
+EVEN\r
+@@loop2:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+       loop    @@loop2\r
+\r
+       ret\r
+\r
+;\r
+; three byte character\r
+;\r
+mshift3wide:\r
+       sub     dx,3\r
+EVEN\r
+@@loop3m:\r
+       SHIFTWITHXOR\r
+       SHIFTWITHXOR\r
+       SHIFTWITHXOR\r
+       add     di,dx                   ; next line in buffer\r
+\r
+       loop    @@loop3m\r
+\r
+       mov     cx,[es:pcharheight]\r
+\r
+EVEN\r
+@@loop3:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+       loop    @@loop3\r
+\r
+       ret\r
+\r
+\r
+ENDP\r
+\r
+;============================================================================\r
+\r
+;==================\r
+;\r
+; VW_DrawMPropString\r
+;\r
+; Draws a C string of characters at px/py and advances px\r
+;\r
+;==================\r
+\r
+\r
+\r
+PROC   VW_DrawMPropString      string:DWORD\r
+PUBLIC VW_DrawMPropString\r
+USES   SI,DI\r
+\r
+;\r
+; clear out the first byte of the buffer, the rest will automatically be\r
+; cleared as characters are drawn into it\r
+;\r
+       mov     es,[grsegs+STARTFONTM*2]\r
+       mov     dx,[es:pcharheight]\r
+       mov     di,OFFSET databuffer\r
+       mov     ax,ds\r
+       mov     es,ax\r
+       mov     bx,BUFFWIDTH-1\r
+\r
+       mov     cx,dx\r
+       mov     al,0ffh\r
+@@maskfill:\r
+       stosb                           ; fill the mask part with $ff\r
+       add     di,bx\r
+       loop    @@maskfill\r
+\r
+       mov     cx,dx\r
+       xor     al,al\r
+@@datafill:\r
+       stosb                           ; fill the data part with $0\r
+       add     di,bx\r
+       loop    @@datafill\r
+\r
+;\r
+; shift the characters into the buffer\r
+;\r
+       mov     ax,[px]\r
+       and     ax,7\r
+       mov     [bufferbit],ax\r
+       mov     [bufferbyte],0\r
+\r
+       mov     ax,[WORD string]\r
+       mov     [stringptr],ax\r
+       mov     ax,[WORD string+2]\r
+       mov     [stringptr+2],ax\r
+\r
+@@shiftone:\r
+       mov     es,[stringptr+2]\r
+       mov     bx,[stringptr]\r
+       inc     [stringptr]\r
+       mov     bx,[es:bx]\r
+       xor     bh,bh\r
+       or      bl,bl\r
+       jz      @@allshifted\r
+       call    ShiftMPropChar\r
+       jmp     @@shiftone\r
+\r
+@@allshifted:\r
+;\r
+; calculate position to draw buffer on screen\r
+;\r
+       mov     bx,[py]\r
+       shl     bx,1\r
+       mov     di,[ylookup+bx]\r
+       add     di,[bufferofs]\r
+\r
+       mov     ax,[px]\r
+       shr     ax,1\r
+       shr     ax,1\r
+       shr     ax,1            ;x location in bytes\r
+       add     di,ax\r
+       mov     [screenspot],di\r
+\r
+;\r
+; advance px\r
+;\r
+       mov     ax,[bufferbyte]\r
+       shl     ax,1\r
+       shl     ax,1\r
+       shl     ax,1\r
+       or      ax,[bufferbit]\r
+       add     [px],ax\r
+\r
+;\r
+; draw it\r
+;\r
+       mov     ax,[bufferbyte]\r
+       test    [bufferbit],7\r
+       jz      @@go\r
+       inc     ax                              ;so the partial byte also gets drawn\r
+@@go:\r
+       mov     [bufferwidth],ax\r
+       mov     es,[grsegs+STARTFONTM*2]\r
+       mov     ax,[es:pcharheight]\r
+       mov     [bufferheight],ax\r
+\r
+       mov     si,OFFSET databuffer\r
+       call    BufferToScreen          ; cut out mask\r
+                                                               ; or in data\r
+       call    BufferToScreen          ; SI is still in the right position in buffer\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+endif          ; if numfontm\r
+\r
+endif          ; if fonts\r
diff --git a/16/cawat/ID_VW_AE.ASM b/16/cawat/ID_VW_AE.ASM
new file mode 100644 (file)
index 0000000..e26c50b
--- /dev/null
@@ -0,0 +1,1900 @@
+; Catacomb Armageddon Source Code\r
+; Copyright (C) 1993-2014 Flat Rock Software\r
+;\r
+; This program is free software; you can redistribute it and/or modify\r
+; it under the terms of the GNU General Public License as published by\r
+; the Free Software Foundation; either version 2 of the License, or\r
+; (at your option) any later version.\r
+;\r
+; This program is distributed in the hope that it will be useful,\r
+; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+; GNU General Public License for more details.\r
+;\r
+; You should have received a copy of the GNU General Public License along\r
+; with this program; if not, write to the Free Software Foundation, Inc.,\r
+; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+;=================================\r
+;\r
+; EGA view manager routines\r
+;\r
+;=================================\r
+\r
+;============================================================================\r
+;\r
+; All EGA drawing routines that write out words need to have alternate forms\r
+; for starting on even and odd addresses, because writing a word at segment\r
+; offset 0xffff causes an exception!  To work around this, write a single\r
+; byte out to make the address even, so it wraps cleanly at the end.\r
+;\r
+; All of these routines assume read/write mode 0, and will allways return\r
+; in that state.\r
+; The direction flag should be clear\r
+; readmap/writemask is left in an undefined state\r
+;\r
+;============================================================================\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_Plot (int x,y,color)\r
+;\r
+;============================================================================\r
+\r
+DATASEG\r
+\r
+plotpixels     db      128,64,32,16,8,4,2,1\r
+\r
+CODESEG\r
+\r
+PROC   VW_Plot x:WORD, y:WORD, color:WORD\r
+PUBLIC VW_Plot\r
+USES   SI,DI\r
+\r
+       mov     es,[screenseg]\r
+\r
+       mov     dx,SC_INDEX\r
+       mov     ax,SC_MAPMASK+15*256\r
+       WORDOUT\r
+\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_MODE+2*256        ;write mode 2\r
+       WORDOUT\r
+\r
+       mov     di,[bufferofs]\r
+       mov     bx,[y]\r
+       shl     bx,1\r
+       add     di,[ylookup+bx]\r
+       mov     bx,[x]\r
+       mov     ax,bx\r
+       shr     ax,1\r
+       shr     ax,1\r
+       shr     ax,1\r
+       add     di,ax                           ; di = byte on screen\r
+\r
+       and     bx,7\r
+       mov     ah,[plotpixels+bx]\r
+       mov     al,GC_BITMASK           ;mask off other pixels\r
+       WORDOUT\r
+\r
+       mov             bl,[BYTE color]\r
+       xchg    bl,[es:di]              ; load latches and write pixel\r
+\r
+       mov     dx,GC_INDEX\r
+       mov     ah,0ffh                         ;no bit mask\r
+       WORDOUT\r
+       mov     ax,GC_MODE+0*256        ;write mode 0\r
+       WORDOUT\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_Vlin (int yl,yh,x,color)\r
+;\r
+;============================================================================\r
+\r
+PROC   VW_Vlin yl:WORD, yh:WORD, x:WORD, color:WORD\r
+PUBLIC VW_Vlin\r
+USES   SI,DI\r
+\r
+       mov     es,[screenseg]\r
+\r
+       mov     dx,SC_INDEX\r
+       mov     ax,SC_MAPMASK+15*256\r
+       WORDOUT\r
+\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_MODE+2*256        ;write mode 2\r
+       WORDOUT\r
+\r
+       mov     di,[bufferofs]\r
+       mov     bx,[yl]\r
+       shl     bx,1\r
+       add     di,[ylookup+bx]\r
+       mov     bx,[x]\r
+       mov     ax,bx\r
+       shr     ax,1\r
+       shr     ax,1\r
+       shr     ax,1\r
+       add     di,ax                           ; di = byte on screen\r
+\r
+       and     bx,7\r
+       mov     ah,[plotpixels+bx]\r
+       mov     al,GC_BITMASK           ;mask off other pixels\r
+       WORDOUT\r
+\r
+       mov     cx,[yh]\r
+       sub     cx,[yl]\r
+       inc     cx                                      ;number of pixels to plot\r
+\r
+       mov     bh,[BYTE color]\r
+       mov     dx,[linewidth]\r
+\r
+@@plot:\r
+       mov             bl,bh\r
+       xchg    bl,[es:di]              ; load latches and write pixel\r
+       add             di,dx\r
+\r
+       loop    @@plot\r
+\r
+       mov     dx,GC_INDEX\r
+       mov     ah,0ffh                         ;no bit mask\r
+       WORDOUT\r
+       mov     ax,GC_MODE+0*256        ;write mode 0\r
+       WORDOUT\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+\r
+\r
+;===================\r
+;\r
+; VW_DrawTile8\r
+;\r
+; xcoord in bytes (8 pixels), ycoord in pixels\r
+; All Tile8s are in one grseg, so an offset is calculated inside it\r
+;\r
+;===================\r
+\r
+PROC   VW_DrawTile8    xcoord:WORD, ycoord:WORD, tile:WORD\r
+PUBLIC VW_DrawTile8\r
+USES   SI,DI\r
+\r
+       mov     es,[screenseg]\r
+\r
+       mov     di,[bufferofs]\r
+       add     di,[xcoord]\r
+       mov     bx,[ycoord]\r
+       shl     bx,1\r
+       add     di,[ylookup+bx]\r
+       mov     [ss:screendest],di              ;screen destination\r
+\r
+       mov     bx,[linewidth]\r
+       dec     bx\r
+\r
+       mov     si,[tile]\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1\r
+       shl     si,1\r
+\r
+       mov     ds,[grsegs+STARTTILE8*2] ; segment for all tile8s\r
+\r
+       mov     cx,4                                    ;planes to draw\r
+       mov     ah,0001b                                ;map mask\r
+\r
+       mov     dx,SC_INDEX\r
+       mov     al,SC_MAPMASK\r
+\r
+;\r
+; start drawing\r
+;\r
+\r
+@@planeloop:\r
+       WORDOUT\r
+       shl     ah,1                                    ;shift plane mask over for next plane\r
+\r
+       mov     di,[ss:screendest]              ;start at same place in all planes\r
+\r
+REPT   7\r
+       movsb\r
+       add     di,bx\r
+ENDM\r
+       movsb\r
+\r
+       loop    @@planeloop\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_MaskBlock\r
+;\r
+; Draws a masked block shape to the screen.  bufferofs is NOT accounted for.\r
+; The mask comes first, then four planes of data.\r
+;\r
+;============================================================================\r
+\r
+DATASEG\r
+\r
+UNWOUNDMASKS   =       10\r
+\r
+\r
+maskroutines   dw      mask0,mask0,mask1E,mask1E,mask2E,mask2O,mask3E,mask3O\r
+                               dw      mask4E,mask4O,mask5E,mask5O,mask6E,mask6O\r
+                               dw      mask7E,mask7O,mask8E,mask8O,mask9E,mask9O\r
+                               dw      mask10E,mask10O\r
+\r
+\r
+routinetouse   dw      ?\r
+\r
+CODESEG\r
+\r
+PROC   VW_MaskBlock    segm:WORD, ofs:WORD, dest:WORD, wide:WORD, height:WORD, planesize:WORD\r
+PUBLIC VW_MaskBlock\r
+USES   SI,DI\r
+\r
+       mov     es,[screenseg]\r
+\r
+       mov     [BYTE planemask],1\r
+       mov     [BYTE planenum],0\r
+\r
+       mov     di,[wide]\r
+       mov     dx,[linewidth]\r
+       sub     dx,[wide]\r
+       mov     [linedelta],dx                  ;amount to add after drawing each line\r
+\r
+       mov     bx,[planesize]                  ; si+bx = data location\r
+\r
+       cmp     di,UNWOUNDMASKS\r
+       jbe     @@unwoundroutine\r
+       mov     [routinetouse],OFFSET generalmask\r
+       jmp     NEAR @@startloop\r
+\r
+;=================\r
+;\r
+; use the unwound routines\r
+;\r
+;=================\r
+\r
+@@unwoundroutine:\r
+       mov     cx,[dest]\r
+       shr     cx,1\r
+       rcl     di,1                                    ;shift a 1 in if destination is odd\r
+       shl     di,1                                    ;to index into a word width table\r
+       mov     ax,[maskroutines+di]    ;call the right routine\r
+       mov     [routinetouse],ax\r
+\r
+@@startloop:\r
+       mov     ds,[segm]\r
+\r
+@@drawplane:\r
+       mov     dx,SC_INDEX\r
+       mov     al,SC_MAPMASK\r
+       mov     ah,[ss:planemask]\r
+       WORDOUT\r
+       mov     dx,GC_INDEX\r
+       mov     al,GC_READMAP\r
+       mov     ah,[ss:planenum]\r
+       WORDOUT\r
+\r
+       mov     si,[ofs]                                ;start back at the top of the mask\r
+       mov     di,[dest]                               ;start at same place in all planes\r
+       mov     cx,[height]                             ;scan lines to draw\r
+       mov dx,[ss:linedelta]\r
+\r
+       jmp [ss:routinetouse]           ;draw one plane\r
+planereturn:                                   ;routine jmps back here\r
+\r
+       add     bx,[ss:planesize]               ;start of mask = start of next plane\r
+\r
+       inc     [ss:planenum]\r
+       shl     [ss:planemask],1                ;shift plane mask over for next plane\r
+       cmp     [ss:planemask],10000b   ;done all four planes?\r
+       jne     @@drawplane\r
+\r
+mask0:\r
+       mov     ax,ss\r
+       mov     ds,ax\r
+       ret                                                     ;width of 0 = no drawing\r
+\r
+;==============\r
+;\r
+; General purpose masked block drawing.  This could be optimised into\r
+; four routines to use words, but few play loop sprites should be this big!\r
+;\r
+;==============\r
+\r
+generalmask:\r
+       mov     dx,cx\r
+\r
+@@lineloopgen:\r
+       mov     cx,[wide]\r
+@@byteloop:\r
+       mov     al,[es:di]\r
+       and     al,[si]\r
+       or      al,[bx+si]\r
+       inc     si\r
+       stosb\r
+       loop    @@byteloop\r
+\r
+       add     di,[ss:linedelta]\r
+       dec     dx\r
+       jnz     @@lineloopgen\r
+       jmp     planereturn\r
+\r
+;=================\r
+;\r
+; Horizontally unwound routines to draw certain masked blocks faster\r
+;\r
+;=================\r
+\r
+MACRO  MASKBYTE\r
+       mov     al,[es:di]\r
+       and     al,[si]\r
+       or      al,[bx+si]\r
+       inc     si\r
+       stosb\r
+ENDM\r
+\r
+MACRO  MASKWORD\r
+       mov     ax,[es:di]\r
+       and     ax,[si]\r
+       or      ax,[bx+si]\r
+       inc     si\r
+       inc     si\r
+       stosw\r
+ENDM\r
+\r
+MACRO  SPRITELOOP      addr\r
+       add     di,dx\r
+       loop    addr\r
+       jmp     planereturn\r
+ENDM\r
+\r
+\r
+EVEN\r
+mask1E:\r
+       MASKBYTE\r
+       SPRITELOOP      mask1E\r
+\r
+EVEN\r
+mask2E:\r
+       MASKWORD\r
+       SPRITELOOP      mask2E\r
+\r
+EVEN\r
+mask2O:\r
+       MASKBYTE\r
+       MASKBYTE\r
+       SPRITELOOP      mask2O\r
+\r
+EVEN\r
+mask3E:\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask3E\r
+\r
+EVEN\r
+mask3O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       SPRITELOOP      mask3O\r
+\r
+EVEN\r
+mask4E:\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask4E\r
+\r
+EVEN\r
+mask4O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask4O\r
+\r
+EVEN\r
+mask5E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask5E\r
+\r
+EVEN\r
+mask5O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask5O\r
+\r
+EVEN\r
+mask6E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask6E\r
+\r
+EVEN\r
+mask6O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask6O\r
+\r
+EVEN\r
+mask7E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask7E\r
+\r
+EVEN\r
+mask7O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask7O\r
+\r
+EVEN\r
+mask8E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask8E\r
+\r
+EVEN\r
+mask8O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask8O\r
+\r
+EVEN\r
+mask9E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask9E\r
+\r
+EVEN\r
+mask9O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask9O\r
+\r
+EVEN\r
+mask10E:\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       SPRITELOOP      mask10E\r
+\r
+EVEN\r
+mask10O:\r
+       MASKBYTE\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKWORD\r
+       MASKBYTE\r
+       SPRITELOOP      mask10O\r
+\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_ScreenToScreen\r
+;\r
+; Basic block copy routine.  Copies one block of screen memory to another,\r
+; using write mode 1 (sets it and returns with write mode 0).  bufferofs is\r
+; NOT accounted for.\r
+;\r
+;============================================================================\r
+\r
+PROC   VW_ScreenToScreen       source:WORD, dest:WORD, wide:WORD, height:WORD\r
+PUBLIC VW_ScreenToScreen\r
+USES   SI,DI\r
+\r
+       pushf\r
+       cli\r
+\r
+       mov     dx,SC_INDEX\r
+       mov     ax,SC_MAPMASK+15*256\r
+       WORDOUT\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_MODE+1*256\r
+       WORDOUT\r
+\r
+       popf\r
+\r
+       mov     bx,[linewidth]\r
+       sub     bx,[wide]\r
+\r
+       mov     ax,[screenseg]\r
+       mov     es,ax\r
+       mov     ds,ax\r
+\r
+       mov     si,[source]\r
+       mov     di,[dest]                               ;start at same place in all planes\r
+       mov     dx,[height]                             ;scan lines to draw\r
+       mov     ax,[wide]\r
+\r
+@@lineloop:\r
+       mov     cx,ax\r
+       rep     movsb\r
+       add     si,bx\r
+       add     di,bx\r
+\r
+       dec     dx\r
+       jnz     @@lineloop\r
+\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_MODE+0*256\r
+       WORDOUT\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_MemToScreen\r
+;\r
+; Basic block drawing routine. Takes a block shape at segment pointer source\r
+; with four planes of width by height data, and draws it to dest in the\r
+; virtual screen, based on linewidth.  bufferofs is NOT accounted for.\r
+; There are four drawing routines to provide the best optimized code while\r
+; accounting for odd segment wrappings due to the floating screens.\r
+;\r
+;============================================================================\r
+\r
+DATASEG\r
+\r
+memtoscreentable       dw      eventoeven,eventoodd,oddtoeven,oddtoodd\r
+\r
+CODESEG\r
+\r
+\r
+PROC   VW_MemToScreen  source:WORD, dest:WORD, wide:WORD, height:WORD\r
+PUBLIC VW_MemToScreen\r
+USES   SI,DI\r
+\r
+       mov     es,[screenseg]\r
+\r
+       mov     bx,[linewidth]\r
+       sub     bx,[wide]\r
+\r
+       mov     ds,[source]\r
+\r
+\r
+       xor     si,si                                   ;block is segment aligned\r
+\r
+       xor     di,di\r
+       shr     [wide],1                                ;change wide to words, and see if carry is set\r
+       rcl     di,1                                    ;1 if wide is odd\r
+       mov     ax,[dest]\r
+       shr     ax,1\r
+       rcl     di,1                                    ;shift a 1 in if destination is odd\r
+       shl     di,1                                    ;to index into a word width table\r
+       mov     ax,SC_MAPMASK+0001b*256 ;map mask for plane 0\r
+       jmp     [ss:memtoscreentable+di]        ;call the right routine\r
+\r
+;==============\r
+;\r
+; Copy an even width block to an even video address\r
+;\r
+;==============\r
+\r
+eventoeven:\r
+       mov     dx,SC_INDEX\r
+       WORDOUT\r
+\r
+       mov     di,[dest]                               ;start at same place in all planes\r
+       mov     dx,[height]                             ;scan lines to draw\r
+\r
+@@lineloopEE:\r
+       mov     cx,[wide]\r
+       rep     movsw\r
+\r
+       add     di,bx\r
+\r
+       dec     dx\r
+       jnz     @@lineloopEE\r
+\r
+       shl     ah,1                                    ;shift plane mask over for next plane\r
+       cmp     ah,10000b                               ;done all four planes?\r
+       jne     eventoeven\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+;==============\r
+;\r
+; Copy an odd width block to an even video address\r
+;\r
+;==============\r
+\r
+oddtoeven:\r
+       mov     dx,SC_INDEX\r
+       WORDOUT\r
+\r
+       mov     di,[dest]                               ;start at same place in all planes\r
+       mov     dx,[height]                             ;scan lines to draw\r
+\r
+@@lineloopOE:\r
+       mov     cx,[wide]\r
+       rep     movsw\r
+       movsb                                           ;copy the last byte\r
+\r
+       add     di,bx\r
+\r
+       dec     dx\r
+       jnz     @@lineloopOE\r
+\r
+       shl     ah,1                                    ;shift plane mask over for next plane\r
+       cmp     ah,10000b                               ;done all four planes?\r
+       jne     oddtoeven\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+;==============\r
+;\r
+; Copy an even width block to an odd video address\r
+;\r
+;==============\r
+\r
+eventoodd:\r
+       dec     [wide]                                  ;one word has to be handled seperately\r
+EOplaneloop:\r
+       mov     dx,SC_INDEX\r
+       WORDOUT\r
+\r
+       mov     di,[dest]                               ;start at same place in all planes\r
+       mov     dx,[height]                             ;scan lines to draw\r
+\r
+@@lineloopEO:\r
+       movsb\r
+       mov     cx,[wide]\r
+       rep     movsw\r
+       movsb\r
+\r
+       add     di,bx\r
+\r
+       dec     dx\r
+       jnz     @@lineloopEO\r
+\r
+       shl     ah,1                                    ;shift plane mask over for next plane\r
+       cmp     ah,10000b                               ;done all four planes?\r
+       jne     EOplaneloop\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+;==============\r
+;\r
+; Copy an odd width block to an odd video address\r
+;\r
+;==============\r
+\r
+oddtoodd:\r
+       mov     dx,SC_INDEX\r
+       WORDOUT\r
+\r
+       mov     di,[dest]                               ;start at same place in all planes\r
+       mov     dx,[height]                             ;scan lines to draw\r
+\r
+@@lineloopOO:\r
+       movsb\r
+       mov     cx,[wide]\r
+       rep     movsw\r
+\r
+       add     di,bx\r
+\r
+       dec     dx\r
+       jnz     @@lineloopOO\r
+\r
+       shl     ah,1                                    ;shift plane mask over for next plane\r
+       cmp     ah,10000b                               ;done all four planes?\r
+       jne     oddtoodd\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+\r
+ENDP\r
+\r
+; MDM (GAMERS EDGE) begin\r
+\r
+\r
+MACRO  XPAND_BYTE\r
+       test    al,128                                                                  ; handle bit 7\r
+       jne   @@over7\r
+       or              [BYTE PTR es:di],11000000b\r
+@@over7:\r
+\r
+       test    al,64                                                                           ; handle bit 6\r
+       jne   @@over6\r
+       or              [BYTE PTR es:di],00110000b\r
+@@over6:\r
+\r
+       test    al,32                                                                           ; handle bit 5\r
+       jne   @@over5\r
+       or              [BYTE PTR es:di],00001100b\r
+@@over5:\r
+\r
+       test    al,16                                                                           ; handle bit 4\r
+       jne   @@over4\r
+       or              [BYTE PTR es:di],00000011b\r
+@@over4:\r
+\r
+       inc     di                                                                                      ; inc destination\r
+\r
+       test    al,8                                                                            ; handle bit 3\r
+       jne   @@over3\r
+       or              [BYTE PTR es:di],11000000b\r
+@@over3:\r
+\r
+       test    al,4                                                                            ; handle bit 2\r
+       jne   @@over2\r
+       or              [BYTE PTR es:di],00110000b\r
+@@over2:\r
+\r
+       test    al,2                                                                            ; handle bit 1\r
+       jne   @@over1\r
+       or              [BYTE PTR es:di],00001100b\r
+@@over1:\r
+\r
+       test    al,1                                                                            ; handle bit 0\r
+       jne   @@over0\r
+       or              [BYTE PTR es:di],00000011b\r
+@@over0:\r
+\r
+       inc     si                                                                                      ; inc source\r
+       inc     di                                                                                      ; inc destination\r
+ENDM\r
+\r
+\r
+;============================================================================\r
+;\r
+; VW_MemToScreen2x\r
+;\r
+; Basic block drawing routine. Takes a block shape at segment pointer source\r
+; with four planes of width by height data, and draws it to dest in the\r
+; virtual screen, based on linewidth.  bufferofs is NOT accounted for.\r
+; There are four drawing routines to provide the best optimized code while\r
+; accounting for odd segment wrappings due to the floating screens.\r
+;\r
+;============================================================================\r
+\r
+DATASEG\r
+\r
+xpandhorz      db 00000000b,00000011b,00001100b,00001111b\r
+                               db 00110000b,00110011b,00111100b,00111111b\r
+                               db 11000000b,11000011b,11001100b,11001111b\r
+                               db 11110000b,11110011b,11111100b,11111111b\r
+\r
+CODESEG\r
+\r
+\r
+PROC   VW_MemToScreen2x        source:WORD, dest:WORD, wide:WORD, height:WORD\r
+PUBLIC VW_MemToScreen2x\r
+USES   SI,DI\r
+\r
+       mov     es,[screenseg]\r
+\r
+       mov     bx,[linewidth]\r
+       sub     bx,[wide]\r
+       sub     bx,[wide]\r
+\r
+       mov     ds,[source]\r
+\r
+\r
+       xor     si,si                                   ;block is segment aligned\r
+\r
+       mov     ah,0001b                                ;map mask for plane 0\r
+\r
+@@depthloop:\r
+       mov     al,SC_MAPMASK                   ;restore map mask in al\r
+       mov     dx,SC_INDEX\r
+       WORDOUT\r
+\r
+       mov     di,[dest]                               ;start at same place in all planes\r
+       mov     dx,[height]                             ;scan lines to draw\r
+\r
+@@heightloop:\r
+       mov     cx,[wide]\r
+@@widthloop:\r
+\r
+; handle first nybble\r
+;\r
+       push    di\r
+       mov     di,[si]\r
+       shr     di,1\r
+       shr     di,1\r
+       shr     di,1\r
+       shr     di,1\r
+       and     di,15\r
+       mov     al,[ss:xpandhorz+di]\r
+       pop     di\r
+       mov     [es:di],al\r
+       inc     di\r
+\r
+; handle second nybble\r
+;\r
+       push    di\r
+       mov     di,[si]\r
+       and     di,15\r
+       mov     al,[ss:xpandhorz+di]\r
+       pop     di\r
+       mov     [es:di],al\r
+       inc     si\r
+       inc     di\r
+\r
+\r
+       dec     cx\r
+       jne     @@widthloop\r
+\r
+       add     di,bx\r
+\r
+       dec     dx\r
+       jnz     @@heightloop\r
+\r
+       shl     ah,1                                    ;shift plane mask over for next plane\r
+       cmp     ah,10000b                               ;done all four planes?\r
+       jne     @@depthloop\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+ENDP\r
+; MDM (GAMERS EDGE) end\r
+\r
+;===========================================================================\r
+;\r
+; VW_ScreenToMem\r
+;\r
+; Copies a block of video memory to main memory, in order from planes 0-3.\r
+; This could be optimized along the lines of VW_MemToScreen to take advantage\r
+; of word copies, but this is an infrequently called routine.\r
+;\r
+;===========================================================================\r
+\r
+PROC   VW_ScreenToMem  source:WORD, dest:WORD, wide:WORD, height:WORD\r
+PUBLIC VW_ScreenToMem\r
+USES   SI,DI\r
+\r
+       mov     es,[dest]\r
+\r
+       mov     bx,[linewidth]\r
+       sub     bx,[wide]\r
+\r
+       mov     ds,[screenseg]\r
+\r
+       mov     ax,GC_READMAP                   ;read map for plane 0\r
+\r
+       xor     di,di\r
+\r
+@@planeloop:\r
+       mov     dx,GC_INDEX\r
+       WORDOUT\r
+\r
+       mov     si,[source]                             ;start at same place in all planes\r
+       mov     dx,[height]                             ;scan lines to draw\r
+\r
+@@lineloop:\r
+       mov     cx,[wide]\r
+       rep     movsb\r
+\r
+       add     si,bx\r
+\r
+       dec     dx\r
+       jnz     @@lineloop\r
+\r
+       inc     ah\r
+       cmp     ah,4                                    ;done all four planes?\r
+       jne     @@planeloop\r
+\r
+       mov     ax,ss\r
+       mov     ds,ax                                   ;restore turbo's data segment\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; VWL_UpdateScreenBlocks\r
+;\r
+; Scans through the update matrix and copies any areas that have changed\r
+; to the visable screen, then zeros the update array\r
+;\r
+;============================================================================\r
+\r
+\r
+\r
+; AX   0/1 for scasb, temp for segment register transfers\r
+; BX    width for block copies\r
+; CX   REP counter\r
+; DX   line width deltas\r
+; SI   source for copies\r
+; DI   scas dest / movsb dest\r
+; BP   pointer to end of bufferblocks\r
+\r
+PROC   VWL_UpdateScreenBlocks\r
+PUBLIC VWL_UpdateScreenBlocks\r
+USES   SI,DI,BP\r
+\r
+       jmp     SHORT @@realstart\r
+@@done:\r
+;\r
+; all tiles have been scanned\r
+;\r
+       mov     dx,GC_INDEX                             ; restore write mode 0\r
+       mov     ax,GC_MODE+0*256\r
+       WORDOUT\r
+\r
+       xor     ax,ax                                   ; clear out the update matrix\r
+       mov     cx,UPDATEWIDE*UPDATEHIGH/2\r
+\r
+       mov     di,[updateptr]\r
+       rep     stosw\r
+\r
+       ret\r
+\r
+@@realstart:\r
+       mov     dx,SC_INDEX\r
+       mov     ax,SC_MAPMASK+15*256\r
+       WORDOUT\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_MODE+1*256\r
+       WORDOUT\r
+\r
+       mov     di,[updateptr]                  ; start of floating update screen\r
+       mov     bp,di\r
+       add     bp,UPDATEWIDE*UPDATEHIGH+1 ; when di = bp, all tiles have been scanned\r
+\r
+       push    di\r
+       mov     cx,-1                                   ; definately scan the entire thing\r
+\r
+;\r
+; scan for a 1 in the update list, meaning a tile needs to be copied\r
+; from the master screen to the current screen\r
+;\r
+@@findtile:\r
+       pop     di                                              ; place to continue scaning from\r
+       mov     ax,ss\r
+       mov     es,ax                                   ; search in the data segment\r
+       mov     ds,ax\r
+       mov al,1\r
+       repne   scasb\r
+       cmp     di,bp\r
+       jae     @@done\r
+\r
+       cmp     [BYTE di],al\r
+       jne     @@singletile\r
+       jmp     @@tileblock\r
+\r
+;============\r
+;\r
+; copy a single tile\r
+;\r
+;============\r
+@@singletile:\r
+       inc     di                                              ; we know the next tile is nothing\r
+       push    di                                      ; save off the spot being scanned\r
+       sub     di,[updateptr]\r
+       shl     di,1\r
+       mov     di,[blockstarts-4+di]   ; start of tile location on screen\r
+       mov     si,di\r
+       add     si,[bufferofs]\r
+       add     di,[displayofs]\r
+\r
+       mov     dx,[linewidth]\r
+       sub     dx,2\r
+       mov     ax,[screenseg]\r
+       mov     ds,ax\r
+       mov     es,ax\r
+\r
+REPT   15\r
+       movsb\r
+       movsb\r
+       add     si,dx\r
+       add     di,dx\r
+ENDM\r
+       movsb\r
+       movsb\r
+\r
+       jmp     @@findtile\r
+\r
+;============\r
+;\r
+; more than one tile in a row needs to be updated, so do it as a group\r
+;\r
+;============\r
+EVEN\r
+@@tileblock:\r
+       mov     dx,di                                   ; hold starting position + 1 in dx\r
+       inc     di                                              ; we know the next tile also gets updated\r
+       repe    scasb                           ; see how many more in a row\r
+       push    di                                      ; save off the spot being scanned\r
+\r
+       mov     bx,di\r
+       sub     bx,dx                                   ; number of tiles in a row\r
+       shl     bx,1                                    ; number of bytes / row\r
+\r
+       mov     di,dx                                   ; lookup position of start tile\r
+       sub     di,[updateptr]\r
+       shl     di,1\r
+       mov     di,[blockstarts-2+di]   ; start of tile location\r
+       mov     si,di\r
+       add     si,[bufferofs]\r
+       add     di,[displayofs]\r
+\r
+       mov     dx,[linewidth]\r
+       sub     dx,bx                                   ; offset to next line on screen\r
+\r
+       mov     ax,[screenseg]\r
+       mov     ds,ax\r
+       mov     es,ax\r
+\r
+REPT   15\r
+       mov     cx,bx\r
+       rep     movsb\r
+       add     si,dx\r
+       add     di,dx\r
+ENDM\r
+       mov     cx,bx\r
+       rep     movsb\r
+\r
+       dec     cx                                              ; was 0 from last rep movsb, now $ffff for scasb\r
+       jmp     @@findtile\r
+\r
+ENDP\r
+\r
+\r
+;===========================================================================\r
+;\r
+;                    MISC EGA ROUTINES\r
+;\r
+;===========================================================================\r
+\r
+;==============\r
+;\r
+; VW_SetScreen\r
+;\r
+;==============\r
+\r
+PROC   VW_SetScreen  crtc:WORD, pel:WORD\r
+PUBLIC VW_SetScreen\r
+\r
+if waitforvbl\r
+\r
+       mov     dx,STATUS_REGISTER_1\r
+\r
+;\r
+; wait util the CRTC just starts scaning a diplayed line to set the CRTC start\r
+;\r
+       cli\r
+\r
+@@waitnodisplay:\r
+       in      al,dx\r
+       test    al,1\r
+       jz      @@waitnodisplay\r
+\r
+; the display is now disabled (in a HBL / VBL)\r
+\r
+@@waitdisplay:\r
+       in      al,dx\r
+       test    al,1    ;1 = display is disabled (HBL / VBL)\r
+       jnz     @@waitdisplay\r
+\r
+; the display was just enabled, so a full scan line is available for CRTC set\r
+\r
+endif\r
+\r
+;\r
+; set CRTC start\r
+;\r
+; for some reason, my XT's EGA card doesn't like word outs to the CRTC\r
+; index...\r
+;\r
+       mov     cx,[crtc]\r
+       mov     dx,CRTC_INDEX\r
+       mov     al,0ch          ;start address high register\r
+       out     dx,al\r
+       inc     dx\r
+       mov     al,ch\r
+       out     dx,al\r
+       dec     dx\r
+       mov     al,0dh          ;start address low register\r
+       out     dx,al\r
+       mov     al,cl\r
+       inc     dx\r
+       out     dx,al\r
+\r
+if waitforvbl\r
+\r
+;\r
+; wait for a vertical retrace to set pel panning\r
+;\r
+       mov     dx,STATUS_REGISTER_1\r
+@@waitvbl:\r
+       sti                     ;service interrupts\r
+       jmp     $+2\r
+       cli\r
+       in      al,dx\r
+       test    al,00001000b    ;look for vertical retrace\r
+       jz      @@waitvbl\r
+\r
+endif\r
+\r
+;\r
+; set horizontal panning\r
+;\r
+\r
+       mov     dx,ATR_INDEX\r
+       mov     al,ATR_PELPAN or 20h\r
+       out     dx,al\r
+       jmp     $+2\r
+       mov     al,[BYTE pel]           ;pel pan value\r
+       out     dx,al\r
+\r
+       sti\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+\r
+if NUMFONT+NUMFONTM\r
+\r
+;===========================================================================\r
+;\r
+; GENERAL FONT DRAWING ROUTINES\r
+;\r
+;===========================================================================\r
+\r
+DATASEG\r
+\r
+px     dw      ?                                       ; proportional character drawing coordinates\r
+py     dw      ?\r
+pdrawmode      db      11000b          ; 8 = OR, 24 = XOR, put in GC_DATAROTATE\r
+fontcolor      db      15              ;0-15 mapmask value\r
+\r
+PUBLIC px,py,pdrawmode,fontcolor\r
+\r
+;\r
+; offsets in font structure\r
+;\r
+pcharheight    =       0               ;lines high\r
+charloc                =       2               ;pointers to every character\r
+charwidth      =       514             ;every character's width in pixels\r
+\r
+\r
+propchar       dw      ?                       ; the character number to shift\r
+stringptr      dw      ?,?\r
+\r
+\r
+BUFFWIDTH      =       82                      ; MDM (GAMERS EDGE) - increased from 50\r
+BUFFHEIGHT     =   20                  ; must be twice as high as font for masked fonts\r
+\r
+databuffer     db      BUFFWIDTH*BUFFHEIGHT dup (?)\r
+\r
+bufferwidth    dw      ?                                               ; bytes with valid info / line\r
+bufferheight dw        ?                                               ; number of lines currently used\r
+\r
+bufferbyte     dw      ?\r
+bufferbit      dw      ?\r
+\r
+screenspot     dw      ?                                               ; where the buffer is going\r
+\r
+bufferextra    dw      ?                                               ; add at end of a line copy\r
+screenextra    dw      ?\r
+\r
+PUBLIC bufferwidth,bufferheight,screenspot\r
+\r
+CODESEG\r
+\r
+;======================\r
+;\r
+; Macros to table shift a byte of font\r
+;\r
+;======================\r
+\r
+MACRO  SHIFTNOXOR\r
+       mov     al,[es:bx]              ; source\r
+       xor     ah,ah\r
+       shl     ax,1\r
+       mov     si,ax\r
+       mov     ax,[bp+si]              ; table shift into two bytes\r
+       or      [di],al                 ; or with first byte\r
+       inc     di\r
+       mov     [di],ah                 ; replace next byte\r
+       inc     bx                              ; next source byte\r
+ENDM\r
+\r
+MACRO  SHIFTWITHXOR\r
+       mov     al,[es:bx]              ; source\r
+       xor     ah,ah\r
+       shl     ax,1\r
+       mov     si,ax\r
+       mov     ax,[bp+si]              ; table shift into two bytes\r
+       not     ax\r
+       and     [di],al                 ; and with first byte\r
+       inc     di\r
+       mov     [di],ah                 ; replace next byte\r
+       inc     bx                              ; next source byte\r
+ENDM\r
+\r
+\r
+;=======================\r
+;\r
+; BufferToScreen\r
+;\r
+; Pass buffer start in SI (somewhere in databuffer)\r
+; Draws the buffer to the EGA screen in the current write mode\r
+;\r
+;========================\r
+\r
+PROC   BufferToScreen  NEAR\r
+\r
+       mov     es,[screenseg]\r
+       mov     di,[screenspot]\r
+\r
+       mov     bx,[bufferwidth]                ;calculate offsets for end of each line\r
+       or      bx,bx\r
+       jnz     @@isthere\r
+       ret                                                     ;nothing to draw\r
+\r
+@@isthere:\r
+       mov     ax,[linewidth]\r
+       sub     ax,bx\r
+       mov     [screenextra],ax\r
+       mov     ax,BUFFWIDTH\r
+       sub     ax,bx\r
+       mov     [bufferextra],ax\r
+\r
+       mov     bx,[bufferheight]               ;lines to copy\r
+@@lineloop:\r
+       mov     cx,[bufferwidth]                ;bytes to copy\r
+@@byteloop:\r
+       lodsb                                           ;get a byte from the buffer\r
+       xchg    [es:di],al                      ;load latches and store back to screen\r
+       inc     di\r
+\r
+       loop    @@byteloop\r
+\r
+       add     si,[bufferextra]\r
+       add     di,[screenextra]\r
+\r
+       dec     bx\r
+       jnz     @@lineloop\r
+\r
+       ret\r
+ENDP\r
+\r
+\r
+;============================================================================\r
+;\r
+; NON MASKED FONT DRAWING ROUTINES\r
+;\r
+;============================================================================\r
+\r
+if numfont\r
+\r
+DATASEG\r
+\r
+shiftdrawtable dw      0,shift1wide,shift2wide,shift3wide,shift4wide\r
+                               dw              shift5wide\r
+\r
+CODESEG\r
+\r
+;==================\r
+;\r
+; ShiftPropChar\r
+;\r
+; Call with BX = character number (0-255)\r
+; Draws one character to the buffer at bufferbyte/bufferbit, and adjusts\r
+; them to the new position\r
+;\r
+;==================\r
+\r
+PROC   ShiftPropChar   NEAR\r
+\r
+       mov     si,[fontnumber]\r
+       shl     si,1\r
+       mov     es,[grsegs+STARTFONT*2+si]      ;segment of font to use\r
+\r
+;\r
+; find character location, width, and height\r
+;\r
+       mov     si,[es:charwidth+bx]\r
+       and     si,0ffh                                 ;SI hold width in pixels\r
+       shl     bx,1\r
+       mov     bx,[es:charloc+bx]              ;BX holds pointer to character data\r
+\r
+;\r
+; look up which shift table to use, based on bufferbit\r
+;\r
+       mov     di,[bufferbit]\r
+       shl     di,1\r
+       mov     bp,[shifttabletable+di] ;BP holds pointer to shift table\r
+\r
+       mov     di,OFFSET databuffer\r
+       add     di,[bufferbyte]                 ;DI holds pointer to buffer\r
+\r
+;\r
+; advance position by character width\r
+;\r
+       mov     cx,[bufferbit]\r
+       add     cx,si                                   ;new bit position\r
+       mov     ax,cx\r
+       and     ax,7\r
+       mov     [bufferbit],ax                  ;new bit position\r
+       mov     ax,cx\r
+       shr     ax,1\r
+       shr     ax,1\r
+       shr     ax,1\r
+       add     [bufferbyte],ax                 ;new byte position\r
+\r
+       add     si,7\r
+       shr     si,1\r
+       shr     si,1\r
+       shr     si,1                                    ;bytes the character is wide\r
+       shl     si,1                    ;*2 to look up in shiftdrawtable\r
+\r
+       mov     cx,[es:pcharheight]\r
+       mov     dx,BUFFWIDTH\r
+       jmp     [ss:shiftdrawtable+si]  ;procedure to draw this width\r
+\r
+;\r
+; one byte character\r
+;\r
+shift1wide:\r
+       dec     dx\r
+EVEN\r
+@@loop1:\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+       loop    @@loop1\r
+       ret\r
+\r
+;\r
+; two byte character\r
+;\r
+shift2wide:\r
+       dec     dx\r
+       dec     dx\r
+EVEN\r
+@@loop2:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+       loop    @@loop2\r
+       ret\r
+\r
+;\r
+; three byte character\r
+;\r
+shift3wide:\r
+       sub     dx,3\r
+EVEN\r
+@@loop3:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+       loop    @@loop3\r
+       ret\r
+\r
+;\r
+; four byte character\r
+;\r
+shift4wide:\r
+       sub     dx,4\r
+EVEN\r
+@@loop4:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+       loop    @@loop4\r
+       ret\r
+\r
+;\r
+; five byte character\r
+;\r
+shift5wide:\r
+       sub     dx,5\r
+EVEN\r
+@@loop5:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+       loop    @@loop5\r
+       ret\r
+\r
+\r
+\r
+ENDP\r
+\r
+;============================================================================\r
+\r
+;==================\r
+;\r
+; VW_DrawPropString\r
+;\r
+; Draws a C string of characters at px/py and advances px\r
+;\r
+; Assumes write mode 0\r
+;\r
+;==================\r
+\r
+CODESEG\r
+\r
+PROC   VW_DrawPropString       string:DWORD\r
+PUBLIC VW_DrawPropString\r
+USES   SI,DI\r
+\r
+;\r
+; proportional spaceing, which clears the buffer ahead of it, so only\r
+; clear the first collumn\r
+;\r
+       mov     al,0\r
+line   =       0\r
+REPT   BUFFHEIGHT\r
+       mov     [BYTE databuffer+BUFFWIDTH*line],al\r
+line   =       line+1\r
+ENDM\r
+\r
+;\r
+; shift the characters into the buffer\r
+;\r
+@@shiftchars:\r
+       mov     ax,[px]\r
+       and     ax,7\r
+       mov     [bufferbit],ax\r
+       mov     [bufferbyte],0\r
+\r
+       mov     ax,[WORD string]\r
+       mov     [stringptr],ax\r
+       mov     ax,[WORD string+2]\r
+       mov     [stringptr+2],ax\r
+\r
+@@shiftone:\r
+       mov     es,[stringptr+2]\r
+       mov     bx,[stringptr]\r
+       inc     [stringptr]\r
+       mov     bx,[es:bx]\r
+       xor     bh,bh\r
+       or      bl,bl\r
+       jz      @@allshifted\r
+       call    ShiftPropChar\r
+       jmp     @@shiftone\r
+\r
+@@allshifted:\r
+;\r
+; calculate position to draw buffer on screen\r
+;\r
+       mov     bx,[py]\r
+       shl     bx,1\r
+       mov     di,[ylookup+bx]\r
+       add     di,[bufferofs]\r
+       add     di,[panadjust]\r
+\r
+       mov     ax,[px]\r
+       shr     ax,1\r
+       shr     ax,1\r
+       shr     ax,1            ;x location in bytes\r
+       add     di,ax\r
+       mov     [screenspot],di\r
+\r
+;\r
+; advance px\r
+;\r
+       mov     ax,[bufferbyte]\r
+       shl     ax,1\r
+       shl     ax,1\r
+       shl     ax,1\r
+       or      ax,[bufferbit]\r
+       add     [px],ax\r
+\r
+;\r
+; draw it\r
+;\r
+\r
+; set xor/or mode\r
+       mov     dx,GC_INDEX\r
+       mov     al,GC_DATAROTATE\r
+       mov     ah,[pdrawmode]\r
+       WORDOUT\r
+\r
+; set mapmask to color\r
+       mov     dx,SC_INDEX\r
+       mov     al,SC_MAPMASK\r
+       mov     ah,[fontcolor]\r
+       WORDOUT\r
+\r
+       mov     ax,[bufferbyte]\r
+       test    [bufferbit],7\r
+       jz      @@go\r
+       inc     ax                              ;so the partial byte also gets drawn\r
+@@go:\r
+       mov     [bufferwidth],ax\r
+       mov     si,[fontnumber]\r
+       shl     si,1\r
+       mov     es,[grsegs+STARTFONT*2+si]\r
+       mov     ax,[es:pcharheight]\r
+       mov     [bufferheight],ax\r
+\r
+       mov     si,OFFSET databuffer\r
+       call    BufferToScreen\r
+\r
+; set copy mode\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_DATAROTATE\r
+       WORDOUT\r
+\r
+; set mapmask to all\r
+       mov     dx,SC_INDEX\r
+       mov     ax,SC_MAPMASK + 15*256\r
+       WORDOUT\r
+\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+endif  ;numfont\r
+\r
+;============================================================================\r
+;\r
+; MASKED FONT DRAWING ROUTINES\r
+;\r
+;============================================================================\r
+\r
+if     numfontm\r
+\r
+DATASEG\r
+\r
+mshiftdrawtable        dw      0,mshift1wide,mshift2wide,mshift3wide\r
+\r
+\r
+CODESEG\r
+\r
+;==================\r
+;\r
+; ShiftMPropChar\r
+;\r
+; Call with BX = character number (0-255)\r
+; Draws one character to the buffer at bufferbyte/bufferbit, and adjusts\r
+; them to the new position\r
+;\r
+;==================\r
+\r
+PROC   ShiftMPropChar  NEAR\r
+\r
+       mov     si,[fontnumber]\r
+       shl     si,1\r
+       mov     es,[grsegs+STARTFONTM*2+si]     ;segment of font to use\r
+\r
+;\r
+; find character location, width, and height\r
+;\r
+       mov     si,[es:charwidth+bx]\r
+       and     si,0ffh                                 ;SI hold width in pixels\r
+       shl     bx,1\r
+       mov     bx,[es:charloc+bx]              ;BX holds pointer to character data\r
+\r
+;\r
+; look up which shift table to use, based on bufferbit\r
+;\r
+       mov     di,[bufferbit]\r
+       shl     di,1\r
+       mov     bp,[shifttabletable+di] ;BP holds pointer to shift table\r
+\r
+       mov     di,OFFSET databuffer\r
+       add     di,[bufferbyte]                 ;DI holds pointer to buffer\r
+\r
+       mov     cx,[bufferbit]\r
+       add     cx,si                                   ;new bit position\r
+       mov     ax,cx\r
+       and     ax,7\r
+       mov     [bufferbit],ax                  ;new bit position\r
+       mov     ax,cx\r
+       shr     ax,1\r
+       shr     ax,1\r
+       shr     ax,1\r
+       add     [bufferbyte],ax                 ;new byte position\r
+\r
+       add     si,7\r
+       shr     si,1\r
+       shr     si,1\r
+       shr     si,1                                    ;bytes the character is wide\r
+       shl     si,1                    ;*2 to look up in shiftdrawtable\r
+\r
+       mov     cx,[es:pcharheight]\r
+       mov     dx,BUFFWIDTH\r
+       jmp     [ss:mshiftdrawtable+si] ;procedure to draw this width\r
+\r
+;\r
+; one byte character\r
+;\r
+mshift1wide:\r
+       dec     dx\r
+\r
+EVEN\r
+@@loop1m:\r
+       SHIFTWITHXOR\r
+       add     di,dx                   ; next line in buffer\r
+\r
+       loop    @@loop1m\r
+\r
+       mov     cx,[es:pcharheight]\r
+\r
+EVEN\r
+@@loop1:\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+       loop    @@loop1\r
+\r
+       ret\r
+\r
+;\r
+; two byte character\r
+;\r
+mshift2wide:\r
+       dec     dx\r
+       dec     dx\r
+EVEN\r
+@@loop2m:\r
+       SHIFTWITHXOR\r
+       SHIFTWITHXOR\r
+       add     di,dx                   ; next line in buffer\r
+\r
+       loop    @@loop2m\r
+\r
+       mov     cx,[es:pcharheight]\r
+\r
+EVEN\r
+@@loop2:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+       loop    @@loop2\r
+\r
+       ret\r
+\r
+;\r
+; three byte character\r
+;\r
+mshift3wide:\r
+       sub     dx,3\r
+EVEN\r
+@@loop3m:\r
+       SHIFTWITHXOR\r
+       SHIFTWITHXOR\r
+       SHIFTWITHXOR\r
+       add     di,dx                   ; next line in buffer\r
+\r
+       loop    @@loop3m\r
+\r
+       mov     cx,[es:pcharheight]\r
+\r
+EVEN\r
+@@loop3:\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       SHIFTNOXOR\r
+       add     di,dx                   ; next line in buffer\r
+       loop    @@loop3\r
+\r
+       ret\r
+\r
+\r
+ENDP\r
+\r
+;============================================================================\r
+\r
+;==================\r
+;\r
+; VW_DrawMPropString\r
+;\r
+; Draws a C string of characters at px/py and advances px\r
+;\r
+; Assumes write mode 0\r
+;\r
+;==================\r
+\r
+\r
+\r
+PROC   VW_DrawMPropString      string:DWORD\r
+PUBLIC VW_DrawMPropString\r
+USES   SI,DI\r
+\r
+;\r
+; clear out the first byte of the buffer, the rest will automatically be\r
+; cleared as characters are drawn into it\r
+;\r
+       mov     si,[fontnumber]\r
+       shl     si,1\r
+       mov     es,[grsegs+STARTFONTM*2+si]\r
+       mov     dx,[es:pcharheight]\r
+       mov     di,OFFSET databuffer\r
+       mov     ax,ds\r
+       mov     es,ax\r
+       mov     bx,BUFFWIDTH-1\r
+\r
+       mov     cx,dx\r
+       mov     al,0ffh\r
+@@maskfill:\r
+       stosb                           ; fill the mask part with $ff\r
+       add     di,bx\r
+       loop    @@maskfill\r
+\r
+       mov     cx,dx\r
+       xor     al,al\r
+@@datafill:\r
+       stosb                           ; fill the data part with $0\r
+       add     di,bx\r
+       loop    @@datafill\r
+\r
+;\r
+; shift the characters into the buffer\r
+;\r
+       mov     ax,[px]\r
+       and     ax,7\r
+       mov     [bufferbit],ax\r
+       mov     [bufferbyte],0\r
+\r
+       mov     ax,[WORD string]\r
+       mov     [stringptr],ax\r
+       mov     ax,[WORD string+2]\r
+       mov     [stringptr+2],ax\r
+\r
+@@shiftone:\r
+       mov     es,[stringptr+2]\r
+       mov     bx,[stringptr]\r
+       inc     [stringptr]\r
+       mov     bx,[es:bx]\r
+       xor     bh,bh\r
+       or      bl,bl\r
+       jz      @@allshifted\r
+       call    ShiftMPropChar\r
+       jmp     @@shiftone\r
+\r
+@@allshifted:\r
+;\r
+; calculate position to draw buffer on screen\r
+;\r
+       mov     bx,[py]\r
+       shl     bx,1\r
+       mov     di,[ylookup+bx]\r
+       add     di,[bufferofs]\r
+       add     di,[panadjust]\r
+\r
+       mov     ax,[px]\r
+       shr     ax,1\r
+       shr     ax,1\r
+       shr     ax,1            ;x location in bytes\r
+       add     di,ax\r
+       mov     [screenspot],di\r
+\r
+;\r
+; advance px\r
+;\r
+       mov     ax,[bufferbyte]\r
+       shl     ax,1\r
+       shl     ax,1\r
+       shl     ax,1\r
+       or      ax,[bufferbit]\r
+       add     [px],ax\r
+\r
+;\r
+; draw it\r
+;\r
+       mov     ax,[bufferbyte]\r
+       test    [bufferbit],7\r
+       jz      @@go\r
+       inc     ax                              ;so the partial byte also gets drawn\r
+@@go:\r
+       mov     [bufferwidth],ax\r
+       mov     es,[grsegs+STARTFONTM*2]\r
+       mov     ax,[es:pcharheight]\r
+       mov     [bufferheight],ax\r
+\r
+; set AND mode to punch out the mask\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_DATAROTATE + 8*256\r
+       WORDOUT\r
+\r
+; set mapmask to all\r
+       mov     dx,SC_INDEX\r
+       mov     ax,SC_MAPMASK + 15*256\r
+       WORDOUT\r
+\r
+       mov     si,OFFSET databuffer\r
+       call    BufferToScreen\r
+\r
+; set OR mode to fill in the color\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_DATAROTATE + 16*256\r
+       WORDOUT\r
+\r
+; set mapmask to color\r
+       mov     dx,SC_INDEX\r
+       mov     al,SC_MAPMASK\r
+       mov     ah,[fontcolor]\r
+       WORDOUT\r
+\r
+       call    BufferToScreen          ; SI is still in the right position in buffer\r
+\r
+; set copy mode\r
+       mov     dx,GC_INDEX\r
+       mov     ax,GC_DATAROTATE\r
+       WORDOUT\r
+\r
+; set mapmask to all\r
+       mov     dx,SC_INDEX\r
+       mov     ax,SC_MAPMASK + 15*256\r
+       WORDOUT\r
+\r
+\r
+       ret\r
+\r
+ENDP\r
+\r
+endif          ; if numfontm\r
+\r
+endif          ; if fonts\r
diff --git a/16/cawat/JABHACK.ASM b/16/cawat/JABHACK.ASM
new file mode 100644 (file)
index 0000000..1621367
--- /dev/null
@@ -0,0 +1,115 @@
+; Catacomb Armageddon Source Code\r
+; Copyright (C) 1993-2014 Flat Rock Software\r
+;\r
+; This program is free software; you can redistribute it and/or modify\r
+; it under the terms of the GNU General Public License as published by\r
+; the Free Software Foundation; either version 2 of the License, or\r
+; (at your option) any later version.\r
+;\r
+; This program is distributed in the hope that it will be useful,\r
+; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+; GNU General Public License for more details.\r
+;\r
+; You should have received a copy of the GNU General Public License along\r
+; with this program; if not, write to the Free Software Foundation, Inc.,\r
+; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+\r
+; JABHACK.ASM\r
+\r
+.386C\r
+IDEAL\r
+MODEL  MEDIUM\r
+\r
+EXTRN  LDIV@:far\r
+\r
+;============================================================================\r
+\r
+DATASEG\r
+\r
+EXTRN  _intaddr:word\r
+\r
+;============================================================================\r
+\r
+CODESEG\r
+\r
+;      Hacked up Juan Jimenez's code a bit to just return 386/not 386\r
+PROC   _CheckIs386\r
+PUBLIC _CheckIs386\r
+\r
+       pushf                   ; Save flag registers, we use them here\r
+       xor     ax,ax           ; Clear AX and...\r
+       push ax                 ; ...push it onto the stack\r
+       popf                    ; Pop 0 into flag registers (all bits to 0),\r
+       pushf                   ; attempting to set bits 12-15 of flags to 0's\r
+       pop     ax                      ; Recover the save flags\r
+       and     ax,08000h       ; If bits 12-15 of flags are set to\r
+       cmp     ax,08000h       ; zero then it's 8088/86 or 80188/186\r
+       jz      not386\r
+\r
+       mov     ax,07000h       ; Try to set flag bits 12-14 to 1's\r
+       push ax                 ; Push the test value onto the stack\r
+       popf                    ; Pop it into the flag register\r
+       pushf                   ; Push it back onto the stack\r
+       pop     ax                      ; Pop it into AX for check\r
+       and     ax,07000h       ; if bits 12-14 are cleared then\r
+       jz      not386          ; the chip is an 80286\r
+\r
+       mov     ax,1            ; We now assume it's a 80386 or better\r
+       popf\r
+       retf\r
+\r
+not386:\r
+       xor     ax,ax\r
+       popf\r
+       retf\r
+\r
+       ENDP\r
+\r
+\r
+PROC   _jabhack2\r
+PUBLIC _jabhack2\r
+\r
+       jmp     @@skip\r
+\r
+@@where:\r
+       int     060h\r
+       retf\r
+\r
+@@skip:\r
+       push    es\r
+\r
+       mov     ax,seg LDIV@\r
+       mov     es,ax\r
+       mov     ax,[WORD PTR @@where]\r
+       mov     [WORD FAR es:LDIV@],ax\r
+       mov     ax,[WORD PTR @@where+2]\r
+       mov     [WORD FAR es:LDIV@+2],ax\r
+\r
+       mov     ax,offset @@jabdiv\r
+       mov     [_intaddr],ax\r
+       mov     ax,seg @@jabdiv\r
+       mov     [_intaddr+2],ax\r
+\r
+       pop     es\r
+       retf\r
+\r
+@@jabdiv:\r
+       add     sp,4    ;Nuke IRET address, but leave flags\r
+       push bp\r
+       mov     bp,sp   ;Save BP, and set it equal to stack\r
+       cli\r
+\r
+       mov     eax,[bp+8]\r
+       cdq\r
+       idiv [DWORD PTR bp+12]\r
+       mov     edx,eax\r
+       shr     edx,16\r
+\r
+       pop     bp              ;Restore BP\r
+       popf            ;Restore flags (from INT)\r
+       retf    8       ;Return to original caller\r
+\r
+       ENDP\r
+\r
+       END\r
diff --git a/16/cawat/JAMPAK.C b/16/cawat/JAMPAK.C
new file mode 100644 (file)
index 0000000..c12e038
--- /dev/null
@@ -0,0 +1,1113 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#pragma inline\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <ctype.h>\r
+#include <alloc.h>\r
+#include <fcntl.h>\r
+#include <dos.h>\r
+#include <io.h>\r
+\r
+#include "def.h"\r
+#include "gelib.h"\r
+#include "jampak.h"\r
+\r
+\r
+\r
+//=========================================================================\r
+//\r
+//\r
+//                                                                     LOCAL DEFINATIONS\r
+//\r
+//\r
+//=========================================================================\r
+\r
+//#define COMPRESSION_CODE                             // Comment define in for COMPRESS routines\r
+\r
+\r
+\r
+\r
+\r
+\r
+//=========================================================================\r
+//\r
+//\r
+//                                                                     LOCAL VARIABLES\r
+//\r
+//\r
+//=========================================================================\r
+\r
+\r
+unsigned char far LZW_ring_buffer[LZW_N + LZW_F - 1];\r
+\r
+       // ring buffer of size LZW_N, with extra LZW_F-1 bytes to facilitate\r
+       //      string comparison\r
+\r
+\r
+#ifdef COMPRESSION_CODE\r
+\r
+int LZW_match_pos,\r
+        LZW_match_len,\r
+\r
+       // MAtchLength of longest match.  These are set by the InsertNode()\r
+       //      procedure.\r
+\r
+       // left & right children & parents -- These constitute binary search trees. */\r
+\r
+       far LZW_left_child[LZW_N + 1],\r
+       far LZW_right_child[LZW_N + 257],\r
+       far LZW_parent[LZW_N + 1];\r
+\r
+#endif\r
+\r
+memptr segptr;\r
+BufferedIO lzwBIO;\r
+\r
+\r
+\r
+\r
+\r
+//=========================================================================\r
+//\r
+//\r
+//                                                                     COMPRESSION SUPPORT ROUTINES\r
+//\r
+//\r
+//=========================================================================\r
+\r
+\r
+#ifdef COMPRESSION_CODE\r
+\r
+//---------------------------------------------------------------------------\r
+// InitLZWTree()\r
+//---------------------------------------------------------------------------\r
+void InitLZWTree(void)  /* initialize trees */\r
+{\r
+        int i;\r
+\r
+        /* For i = 0 to LZW_N - 1, LZW_right_child[i] and LZW_left_child[i] will be the right and\r
+                left children of node i.  These nodes need not be initialized.\r
+                Also, LZW_parent[i] is the parent of node i.  These are initialized to\r
+                LZW_NIL (= LZW_N), which stands for 'not used.'\r
+                For i = 0 to 255, LZW_right_child[LZW_N + i + 1] is the root of the tree\r
+                for strings that begin with character i.  These are initialized\r
+                to LZW_NIL.  Note there are 256 trees. */\r
+\r
+        for (i = LZW_N + 1; i <= LZW_N + 256; i++)\r
+               LZW_right_child[i] = LZW_NIL;\r
+\r
+        for (i = 0; i < LZW_N; i++)\r
+               LZW_parent[i] = LZW_NIL;\r
+}\r
+\r
+\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// InsertLZWNode()\r
+//---------------------------------------------------------------------------\r
+void InsertLZWNode(unsigned long r)\r
+\r
+        /* Inserts string of length LZW_F, LZW_ring_buffer[r..r+LZW_F-1], into one of the\r
+                trees (LZW_ring_buffer[r]'th tree) and returns the longest-match position\r
+                and length via the global variables LZW_match_pos and LZW_match_len.\r
+                If LZW_match_len = LZW_F, then removes the old node in favor of the new\r
+                one, because the old one will be deleted sooner.\r
+                Note r plays double role, as tree node and position in buffer. */\r
+{\r
+        int  i, p, cmp;\r
+        unsigned char *key;\r
+\r
+        cmp = 1;\r
+        key = &LZW_ring_buffer[r];\r
+        p = LZW_N + 1 + key[0];\r
+        LZW_right_child[r] = LZW_left_child[r] = LZW_NIL;\r
+        LZW_match_len = 0;\r
+\r
+       for ( ; ; )\r
+       {\r
+               if (cmp >= 0)\r
+               {\r
+                       if (LZW_right_child[p] != LZW_NIL)\r
+                               p = LZW_right_child[p];\r
+                       else\r
+                       {\r
+                               LZW_right_child[p] = r;\r
+                               LZW_parent[r] = p;\r
+                               return;\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       if (LZW_left_child[p] != LZW_NIL)\r
+                               p = LZW_left_child[p];\r
+                       else\r
+                       {\r
+                               LZW_left_child[p] = r;\r
+                               LZW_parent[r] = p;\r
+                               return;\r
+                       }\r
+               }\r
+\r
+               for (i = 1; i < LZW_F; i++)\r
+                       if ((cmp = key[i] - LZW_ring_buffer[p + i]) != 0)\r
+                               break;\r
+\r
+               if (i > LZW_match_len)\r
+               {\r
+                       LZW_match_pos = p;\r
+                       if ((LZW_match_len = i) >= LZW_F)\r
+                               break;\r
+               }\r
+       }\r
+\r
+       LZW_parent[r] = LZW_parent[p];\r
+       LZW_left_child[r] = LZW_left_child[p];\r
+       LZW_right_child[r] = LZW_right_child[p];\r
+       LZW_parent[LZW_left_child[p]] = r;\r
+       LZW_parent[LZW_right_child[p]] = r;\r
+\r
+       if (LZW_right_child[LZW_parent[p]] == p)\r
+               LZW_right_child[LZW_parent[p]] = r;\r
+       else\r
+               LZW_left_child[LZW_parent[p]] = r;\r
+\r
+       LZW_parent[p] = LZW_NIL;  /* remove p */\r
+}\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// DeleteLZWNode()\r
+//---------------------------------------------------------------------------\r
+void DeleteLZWNode(unsigned long p)  /* deletes node p from tree */\r
+{\r
+       int q;\r
+\r
+       if (LZW_parent[p] == LZW_NIL)\r
+               return;                                          /* not in tree */\r
+\r
+       if (LZW_right_child[p] == LZW_NIL)\r
+               q = LZW_left_child[p];\r
+       else\r
+       if (LZW_left_child[p] == LZW_NIL)\r
+               q = LZW_right_child[p];\r
+       else\r
+       {\r
+               q = LZW_left_child[p];\r
+               if (LZW_right_child[q] != LZW_NIL)\r
+               {\r
+                       do {\r
+\r
+                               q = LZW_right_child[q];\r
+\r
+                       } while (LZW_right_child[q] != LZW_NIL);\r
+\r
+                       LZW_right_child[LZW_parent[q]] = LZW_left_child[q];\r
+                       LZW_parent[LZW_left_child[q]] = LZW_parent[q];\r
+                       LZW_left_child[q] = LZW_left_child[p];\r
+                       LZW_parent[LZW_left_child[p]] = q;\r
+               }\r
+\r
+               LZW_right_child[q] = LZW_right_child[p];\r
+               LZW_parent[LZW_right_child[p]] = q;\r
+        }\r
+\r
+        LZW_parent[q] = LZW_parent[p];\r
+        if (LZW_right_child[LZW_parent[p]] == p)\r
+               LZW_right_child[LZW_parent[p]] = q;\r
+        else\r
+               LZW_left_child[LZW_parent[p]] = q;\r
+\r
+        LZW_parent[p] = LZW_NIL;\r
+}\r
+#endif\r
+\r
+\r
+\r
+\r
+//=========================================================================\r
+//\r
+//\r
+//                                                     GENERAL FILE to FILE compression routines\r
+//\r
+//      * Mainly for example usage of PTR/PTR (de)compression routines.\r
+//\r
+//\r
+//=========================================================================\r
+\r
+\r
+\r
+//////////////////////////////////////////////////////////////////////\r
+//\r
+//  CompressFILEtoFILE() -- Compresses one file stream to another file stream\r
+//\r
+\r
+#ifdef COMPRESSION_CODE\r
+\r
+unsigned long CompressFILEtoFILE(FILE *infile, FILE *outfile,unsigned long DataLength)\r
+{\r
+       unsigned long returnval;\r
+\r
+       fwrite(COMP,4,1,outfile);\r
+       fwrite((char *)&DataLength,4,1,outfile);\r
+\r
+       returnval = 8+lzwCompress(infile,outfile,DataLength,(SRC_FFILE|DEST_FFILE));\r
+\r
+       return(returnval);\r
+}\r
+\r
+#endif\r
+\r
+#if 0\r
+/////////////////////////////////////////////////////////////////////////////\r
+//\r
+//  DecompressFILEtoFILE()\r
+//\r
+void DecompressFILEtoFILE(FILE *infile, FILE *outfile)\r
+{\r
+       unsigned char Buffer[8];\r
+       unsigned long DataLength;\r
+\r
+       fread(Buffer,1,4,infile);\r
+\r
+       if (strncmp(Buffer,COMP,4))\r
+       {\r
+               printf("\nNot a JAM Compressed File!\n");\r
+               return;\r
+       }\r
+\r
+       fread((void *)&DataLength,1,4,infile);\r
+\r
+       lzwDecompress(infile,outfile,DataLength,(SRC_FFILE|DEST_FFILE));\r
+}\r
+#endif\r
+\r
+\r
+\r
+\r
+\r
+//==========================================================================\r
+//\r
+//\r
+//                                                     WRITE/READ PTR ROUTINES\r
+//\r
+//\r
+//==========================================================================\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// WritePtr()  -- Outputs data to a particular ptr type\r
+//\r
+//     PtrType MUST be of type DEST_TYPE.\r
+//\r
+// NOTE : For PtrTypes DEST_MEM a ZERO (0) is always returned.\r
+//\r
+//---------------------------------------------------------------------------\r
+int WritePtr(long outfile, unsigned char data, unsigned PtrType)\r
+{\r
+       int returnval = 0;\r
+\r
+       switch (PtrType & DEST_TYPES)\r
+       {\r
+               case DEST_FILE:\r
+                       write(*(int far *)outfile,(char *)&data,1);\r
+               break;\r
+\r
+               case DEST_FFILE:\r
+                       returnval = putc(data, *(FILE **)outfile);\r
+               break;\r
+\r
+               case DEST_MEM:\r
+//                     *(*(char far **)outfile++) = data;                                              // Do NOT delete\r
+                       *((char far *)*(char far **)outfile)++ = data;\r
+               break;\r
+\r
+               default:\r
+                       TrashProg("WritePtr() : Unknown DEST_PTR type");\r
+               break;\r
+       }\r
+\r
+       return(returnval);\r
+\r
+}\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// ReadPtr()  -- Reads data from a particular ptr type\r
+//\r
+//     PtrType MUST be of type SRC_TYPE.\r
+//\r
+// RETURNS :\r
+//             The char read in or EOF for SRC_FFILE type of reads.\r
+//\r
+//\r
+//---------------------------------------------------------------------------\r
+int ReadPtr(long infile, unsigned PtrType)\r
+{\r
+       int returnval = 0;\r
+\r
+       switch (PtrType & SRC_TYPES)\r
+       {\r
+               case SRC_FILE:\r
+                       read(*(int far *)infile,(char *)&returnval,1);\r
+               break;\r
+\r
+               case SRC_FFILE:\r
+// JIM - JTR - is the following correct? "fgetc()" uses a near pointer.\r
+//\r
+                       returnval = fgetc((FILE far *)*(FILE far **)infile);\r
+               break;\r
+\r
+               case SRC_BFILE:\r
+                       returnval = bio_readch((BufferedIO *)*(void far **)infile);\r
+               break;\r
+\r
+               case SRC_MEM:\r
+                       returnval = (char)*(*(char far **)infile++);\r
+//                     returnval = *((char far *)*(char far **)infile)++;      // DO NOT DELETE!\r
+               break;\r
+\r
+               default:\r
+                       TrashProg("ReadPtr() : Unknown SRC_PTR type");\r
+               break;\r
+       }\r
+\r
+       return(returnval);\r
+}\r
+\r
+\r
+\r
+\r
+//=========================================================================\r
+//\r
+//\r
+//                                                     COMPRESSION & DECOMPRESSION ROUTINES\r
+//\r
+//\r
+//=========================================================================\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+//\r
+// lzwCompress() - Compresses data from an input ptr to a dest ptr\r
+//\r
+// PARAMS:\r
+//              infile     - Pointer at the BEGINNING of the data to compress\r
+//              outfile    - Pointer to the destination (no header).\r
+//      DataLength - Number of bytes to compress.\r
+//     PtrTypes   - Type of pointers being used (SRC_FILE,DEST_FILE,SRC_MEM etc).\r
+//\r
+// RETURNS:\r
+//         Length of compressed data.\r
+//\r
+//     COMPTYPE : ct_LZW\r
+//\r
+// NOTES    : Does not write ANY header information!\r
+//\r
+#ifdef COMPRESSION_CODE\r
+unsigned long lzwCompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes)\r
+{\r
+       short i;\r
+       short c, len, r, s, last_LZW_match_len, code_buf_ptr;\r
+       unsigned char far code_buf[17], mask;\r
+       unsigned long complen = 0;\r
+\r
+       // initialize trees\r
+\r
+       InitLZWTree();\r
+\r
+       code_buf[0] = 0;\r
+\r
+       //\r
+       //  code_buf[1..16] saves eight units of code, and code_buf[0] works\r
+       //       as eight flags, "1" representing that the unit is an unencoded\r
+       //       letter (1 byte), "0" a position-and-length pair (2 bytes).  Thus,\r
+       //       eight units require at most 16 bytes of code.\r
+       //\r
+\r
+        code_buf_ptr = mask = 1;\r
+        s = 0;\r
+        r = LZW_N - LZW_F;\r
+\r
+       // Clear the buffer with any character that will appear often.\r
+       //\r
+\r
+        for (i = s; i < r; i++)\r
+                       LZW_ring_buffer[i] = ' ';\r
+\r
+       // Read LZW_F bytes into the last LZW_F bytes of the buffer\r
+       //\r
+\r
+        for (len = 0; (len < LZW_F) && DataLength; len++)\r
+        {\r
+               c = ReadPtr((long)&infile,PtrTypes);\r
+               DataLength--;\r
+\r
+               // text of size zero\r
+\r
+               LZW_ring_buffer[r + len] = c;\r
+        }\r
+\r
+        if (!(len && DataLength))\r
+               return(0);\r
+\r
+       //\r
+       // Insert the LZW_F strings, each of which begins with one or more\r
+       // 'space' characters.  Note the order in which these strings\r
+       // are inserted.  This way, degenerate trees will be less likely\r
+       // to occur.\r
+       //\r
+\r
+        for (i = 1; i <= LZW_F; i++)\r
+                       InsertLZWNode(r - i);\r
+\r
+       //\r
+       // Finally, insert the whole string just read.  The global\r
+       // variables LZW_match_len and LZW_match_pos are set. */\r
+       //\r
+\r
+        InsertLZWNode(r);\r
+\r
+        do {\r
+                       // LZW_match_len may be spuriously long near the end of text.\r
+                       //\r
+\r
+                       if (LZW_match_len > len)\r
+                               LZW_match_len = len;\r
+\r
+                       if (LZW_match_len <= LZW_THRESHOLD)\r
+                       {\r
+                                 // Not long enough match.  Send one byte.\r
+                                 //\r
+\r
+                                 LZW_match_len = 1;\r
+\r
+                                 // 'send one byte' flag\r
+                                 //\r
+\r
+                                 code_buf[0] |= mask;\r
+\r
+                                 // Send uncoded.\r
+                                 //\r
+\r
+                                 code_buf[code_buf_ptr++] = LZW_ring_buffer[r];\r
+                       }\r
+                       else\r
+                       {\r
+                               code_buf[code_buf_ptr++] = (unsigned char) LZW_match_pos;\r
+                               code_buf[code_buf_ptr++] = (unsigned char) (((LZW_match_pos >> 4) & 0xf0) | (LZW_match_len - (LZW_THRESHOLD + 1)));\r
+\r
+                               // Send position and length pair.\r
+                               // Note LZW_match_len > LZW_THRESHOLD.\r
+                       }\r
+\r
+                       if ((mask <<= 1) == 0)\r
+                       {\r
+                               // Shift mask left one bit.\r
+                               // Send at most 8 units of data\r
+\r
+                               for (i = 0; i < code_buf_ptr; i++)\r
+                                       WritePtr((long)&outfile,code_buf[i],PtrTypes);\r
+\r
+                               complen += code_buf_ptr;\r
+                               code_buf[0] = 0;\r
+                               code_buf_ptr = mask = 1;\r
+                       }\r
+\r
+                       last_LZW_match_len = LZW_match_len;\r
+\r
+                       for (i = 0; i < last_LZW_match_len && DataLength; i++)\r
+                       {\r
+                               c = ReadPtr((long)&infile,PtrTypes);\r
+                               DataLength--;\r
+\r
+                               DeleteLZWNode(s);                           // Delete old strings and\r
+                               LZW_ring_buffer[s] = c;                                          // read new bytes\r
+\r
+                               // If the position is near the end of buffer, extend the\r
+                               //      buffer to make string comparison easier.\r
+\r
+                               if (s < LZW_F - 1)\r
+                                       LZW_ring_buffer[s + LZW_N] = c;\r
+\r
+                               // Since this is a ring buffer, inc the position modulo LZW_N.\r
+                               //\r
+\r
+                               s = (s + 1) & (LZW_N - 1);\r
+                               r = (r + 1) & (LZW_N - 1);\r
+\r
+                               // Register the string in LZW_ring_buffer[r..r+LZW_F-1]\r
+                               //\r
+\r
+                               InsertLZWNode(r);\r
+                       }\r
+\r
+                       while (i++ < last_LZW_match_len)\r
+                       {\r
+                                                                                                                 // After the end of text,\r
+                               DeleteLZWNode(s);               // no need to read, but\r
+\r
+                               s = (s + 1) & (LZW_N - 1);\r
+                               r = (r + 1) & (LZW_N - 1);\r
+\r
+                               if (--len)\r
+                                       InsertLZWNode(r);          // buffer may not be empty.\r
+                       }\r
+\r
+        } while (len > 0);  // until length of string to be processed is zero\r
+\r
+\r
+        if (code_buf_ptr > 1)\r
+        {\r
+               // Send remaining code.\r
+               //\r
+\r
+               for (i = 0; i < code_buf_ptr; i++)\r
+                       WritePtr((long)&outfile,code_buf[i],PtrTypes);\r
+\r
+               complen += code_buf_ptr;\r
+        }\r
+\r
+        return(complen);\r
+}\r
+#endif\r
+\r
+\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+//\r
+// lzwDecompress() - Compresses data from an input ptr to a dest ptr\r
+//\r
+// PARAMS:\r
+//              infile     - Pointer at the BEGINNING of the compressed data (no header!)\r
+//              outfile    - Pointer to the destination.\r
+//      DataLength - Length of compressed data.\r
+//     PtrTypes   - Type of pointers being used (SRC_FILE,DEST_FILE,SRC_MEM etc).\r
+//\r
+// RETURNS:\r
+//         Length of compressed data.\r
+//\r
+//     COMPTYPE : ct_LZW\r
+//\r
+// NOTES    : Does not write ANY header information!\r
+//\r
+void far lzwDecompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes)\r
+{\r
+       int  i, j, k, r, c;\r
+       unsigned int flags;\r
+\r
+       for (i = 0; i < LZW_N - LZW_F; i++)\r
+               LZW_ring_buffer[i] = ' ';\r
+\r
+        r = LZW_N - LZW_F;\r
+        flags = 0;\r
+\r
+        for ( ; ; )\r
+        {\r
+                       if (((flags >>= 1) & 256) == 0)\r
+                       {\r
+                               c = ReadPtr((long)&infile,PtrTypes);\r
+                               if (!DataLength--)\r
+                                       return;\r
+\r
+                               flags = c | 0xff00;      // uses higher byte cleverly to count 8\r
+                       }\r
+\r
+                       if (flags & 1)\r
+                       {\r
+                               c = ReadPtr((long)&infile,PtrTypes);            // Could test for EOF iff FFILE type\r
+                               if (!DataLength--)\r
+                                       return;\r
+\r
+                               WritePtr((long)&outfile,c,PtrTypes);\r
+\r
+                               LZW_ring_buffer[r++] = c;\r
+                               r &= (LZW_N - 1);\r
+                       }\r
+                       else\r
+                       {\r
+                               i = ReadPtr((long)&infile,PtrTypes);\r
+                          if (!DataLength--)\r
+                                       return;\r
+\r
+                               j = ReadPtr((long)&infile,PtrTypes);\r
+                          if (!DataLength--)\r
+                                       return;\r
+\r
+                               i |= ((j & 0xf0) << 4);\r
+                               j = (j & 0x0f) + LZW_THRESHOLD;\r
+\r
+                               for (k = 0; k <= j; k++)\r
+                               {\r
+                                        c = LZW_ring_buffer[(i + k) & (LZW_N - 1)];\r
+\r
+                                        WritePtr((long)&outfile,c,PtrTypes);\r
+\r
+                                        LZW_ring_buffer[r++] = c;\r
+                                        r &= (LZW_N - 1);\r
+                               }\r
+                       }\r
+        }\r
+}\r
+\r
+\r
+\r
+#if 0\r
+//=========================================================================\r
+//\r
+//\r
+//                                                              BUFFERED I/O ROUTINES\r
+//\r
+//\r
+//=========================================================================\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+// InitBufferedIO()\r
+//--------------------------------------------------------------------------\r
+memptr InitBufferedIO(int handle, BufferedIO *bio)\r
+{\r
+       bio->handle = handle;\r
+       bio->offset = BIO_BUFFER_LEN;\r
+       bio->status = 0;\r
+       MM_GetPtr(&bio->buffer,BIO_BUFFER_LEN);\r
+\r
+       return(bio->buffer);\r
+}\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+// FreeBufferedIO()\r
+//--------------------------------------------------------------------------\r
+void FreeBufferedIO(BufferedIO *bio)\r
+{\r
+       if (bio->buffer)\r
+               MM_FreePtr(&bio->buffer);\r
+}\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+// bio_readch()\r
+//--------------------------------------------------------------------------\r
+byte bio_readch(BufferedIO *bio)\r
+{\r
+       byte far *buffer;\r
+\r
+       if (bio->offset == BIO_BUFFER_LEN)\r
+       {\r
+               bio->offset = 0;\r
+               bio_fillbuffer(bio);\r
+       }\r
+\r
+       buffer = MK_FP(bio->buffer,bio->offset++);\r
+\r
+       return(*buffer);\r
+}\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+// bio_fillbuffer()\r
+//\r
+// BUGS (Not really bugs... More like RULES!)\r
+//\r
+//    1) This code assumes BIO_BUFFER_LEN is no smaller than\r
+//       NEAR_BUFFER_LEN!!\r
+//\r
+//    2) BufferedIO.status should be altered by this code to report\r
+//       read errors, end of file, etc... If you know how big the file\r
+//       is you're reading, determining EOF should be no problem.\r
+//\r
+//--------------------------------------------------------------------------\r
+void bio_fillbuffer(BufferedIO *bio)\r
+{\r
+       #define NEAR_BUFFER_LEN (64)\r
+       byte near_buffer[NEAR_BUFFER_LEN];\r
+       short bio_length,bytes_read,bytes_requested;\r
+\r
+       bytes_read = 0;\r
+       bio_length = BIO_BUFFER_LEN;\r
+       while (bio_length)\r
+       {\r
+               if (bio_length > NEAR_BUFFER_LEN-1)\r
+                       bytes_requested = NEAR_BUFFER_LEN;\r
+               else\r
+                       bytes_requested = bio_length;\r
+\r
+               read(bio->handle,near_buffer,bytes_requested);\r
+               _fmemcpy(MK_FP(bio->buffer,bytes_read),near_buffer,bytes_requested);\r
+\r
+               bio_length -= bytes_requested;\r
+               bytes_read += bytes_requested;\r
+       }\r
+}\r
+\r
+\r
+#endif\r
+\r
+//=========================================================================\r
+//\r
+//\r
+//                                                             GENERAL LOAD ROUTINES\r
+//\r
+//\r
+//=========================================================================\r
+\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+// BLoad()\r
+//--------------------------------------------------------------------------\r
+unsigned long BLoad(char *SourceFile, memptr *DstPtr)\r
+{\r
+       int handle;\r
+\r
+       memptr SrcPtr;\r
+       longword i, j, k, r, c;\r
+       word flags;\r
+       byte Buffer[8];\r
+       longword DstLen, SrcLen;\r
+       boolean comp;\r
+\r
+       if ((handle = open(SourceFile, O_RDONLY|O_BINARY)) == -1)\r
+               return(0);\r
+\r
+       // Look for 'COMP' header\r
+       //\r
+       read(handle,Buffer,4);\r
+       comp = !strncmp(Buffer,COMP,4);\r
+\r
+       // Get source and destination length.\r
+       //\r
+       if (comp)\r
+       {\r
+               SrcLen = Verify(SourceFile);\r
+               read(handle,(void *)&DstLen,4);\r
+               MM_GetPtr(DstPtr,DstLen);\r
+               if (!*DstPtr)\r
+                       return(0);\r
+       }\r
+       else\r
+               DstLen = Verify(SourceFile);\r
+\r
+       // LZW decompress OR simply load the file.\r
+       //\r
+       if (comp)\r
+       {\r
+\r
+               if (MM_TotalFree() < SrcLen)\r
+               {\r
+                       if (!InitBufferedIO(handle,&lzwBIO))\r
+                               TrashProg("No memory for buffered I/O.");\r
+                       lzwDecompress(&lzwBIO,MK_FP(*DstPtr,0),SrcLen,(SRC_BFILE|DEST_MEM));\r
+                       FreeBufferedIO(&lzwBIO);\r
+               }\r
+               else\r
+               {\r
+                       CA_LoadFile(SourceFile,&SrcPtr);\r
+                       lzwDecompress(MK_FP(SrcPtr,8),MK_FP(*DstPtr,0),SrcLen,(SRC_MEM|DEST_MEM));\r
+                       MM_FreePtr(&SrcPtr);\r
+               }\r
+       }\r
+       else\r
+               CA_LoadFile(SourceFile,DstPtr);\r
+\r
+       close(handle);\r
+       return(DstLen);\r
+}\r
+\r
+\r
+\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// LoadLIBShape()\r
+//\r
+int LoadLIBShape(char *SLIB_Filename, char *Filename,struct Shape *SHP)\r
+{\r
+       #define CHUNK(Name)     (*ptr == *Name) &&                      \\r
+                                                               (*(ptr+1) == *(Name+1)) &&      \\r
+                                                               (*(ptr+2) == *(Name+2)) &&      \\r
+                                                               (*(ptr+3) == *(Name+3))\r
+\r
+\r
+       int RT_CODE;\r
+       FILE *fp;\r
+       char CHUNK[5];\r
+       char far *ptr;\r
+       memptr IFFfile = NULL;\r
+       unsigned long FileLen, size, ChunkLen;\r
+       int loop;\r
+\r
+\r
+       RT_CODE = 1;\r
+\r
+       // Decompress to ram and return ptr to data and return len of data in\r
+       //      passed variable...\r
+\r
+       if (!LoadLIBFile(SLIB_Filename,Filename,&IFFfile))\r
+               TrashProg("Error Loading Compressed lib shape!");\r
+\r
+       // Evaluate the file\r
+       //\r
+       ptr = MK_FP(IFFfile,0);\r
+       if (!CHUNK("FORM"))\r
+               goto EXIT_FUNC;\r
+       ptr += 4;\r
+\r
+       FileLen = *(long far *)ptr;\r
+       SwapLong((long far *)&FileLen);\r
+       ptr += 4;\r
+\r
+       if (!CHUNK("ILBM"))\r
+               goto EXIT_FUNC;\r
+       ptr += 4;\r
+\r
+       FileLen += 4;\r
+       while (FileLen)\r
+       {\r
+               ChunkLen = *(long far *)(ptr+4);\r
+               SwapLong((long far *)&ChunkLen);\r
+               ChunkLen = (ChunkLen+1) & 0xFFFFFFFE;\r
+\r
+               if (CHUNK("BMHD"))\r
+               {\r
+                       ptr += 8;\r
+                       SHP->bmHdr.w = ((struct BitMapHeader far *)ptr)->w;\r
+                       SHP->bmHdr.h = ((struct BitMapHeader far *)ptr)->h;\r
+                       SHP->bmHdr.x = ((struct BitMapHeader far *)ptr)->x;\r
+                       SHP->bmHdr.y = ((struct BitMapHeader far *)ptr)->y;\r
+                       SHP->bmHdr.d = ((struct BitMapHeader far *)ptr)->d;\r
+                       SHP->bmHdr.trans = ((struct BitMapHeader far *)ptr)->trans;\r
+                       SHP->bmHdr.comp = ((struct BitMapHeader far *)ptr)->comp;\r
+                       SHP->bmHdr.pad = ((struct BitMapHeader far *)ptr)->pad;\r
+                       SwapWord(&SHP->bmHdr.w);\r
+                       SwapWord(&SHP->bmHdr.h);\r
+                       SwapWord(&SHP->bmHdr.x);\r
+                       SwapWord(&SHP->bmHdr.y);\r
+                       ptr += ChunkLen;\r
+               }\r
+               else\r
+               if (CHUNK("BODY"))\r
+               {\r
+                       ptr += 4;\r
+                       size = *((long far *)ptr);\r
+                       ptr += 4;\r
+                       SwapLong((long far *)&size);\r
+                       SHP->BPR = (SHP->bmHdr.w+7) >> 3;\r
+                       MM_GetPtr(&SHP->Data,size);\r
+                       if (!SHP->Data)\r
+                               goto EXIT_FUNC;\r
+                       movedata(FP_SEG(ptr),FP_OFF(ptr),FP_SEG(SHP->Data),0,size);\r
+                       ptr += ChunkLen;\r
+\r
+                       break;\r
+               }\r
+               else\r
+                       ptr += ChunkLen+8;\r
+\r
+               FileLen -= ChunkLen+8;\r
+       }\r
+\r
+       RT_CODE = 0;\r
+\r
+EXIT_FUNC:;\r
+       if (IFFfile)\r
+       {\r
+//             segptr = (memptr)FP_SEG(IFFfile);\r
+               MM_FreePtr(&IFFfile);\r
+       }\r
+\r
+       return (RT_CODE);\r
+}\r
+\r
+\r
+\r
+\r
+\r
+//----------------------------------------------------------------------------\r
+// LoadLIBFile() -- Copies a file from an existing archive to dos.\r
+//\r
+// PARAMETERS :\r
+//\r
+//                     LibName         - Name of lib file created with SoftLib V1.0\r
+//\r
+//                     FileName - Name of file to load from lib file.\r
+//\r
+//                     MemPtr   - (IF !NULL) - Pointer to memory to load into ..\r
+//                                               (IF NULL)  - Routine allocates necessary memory and\r
+//                                                                                     returns a MEM(SEG) pointer to memory allocated.\r
+//\r
+// RETURN :\r
+//\r
+//             (IF !NULL) - A pointer to the loaded data.\r
+//                     (IF NULL)  - Error!\r
+//\r
+//----------------------------------------------------------------------------\r
+memptr LoadLIBFile(char *LibName,char *FileName,memptr *MemPtr)\r
+{\r
+       int handle;\r
+       unsigned long header;\r
+       struct ChunkHeader Header;\r
+       unsigned long ChunkLen;\r
+       short x;\r
+       struct FileEntryHdr FileEntry;                          // Storage for file once found\r
+       struct FileEntryHdr FileEntryHeader;            // Header used durring searching\r
+       struct SoftLibHdr LibraryHeader;                                // Library header - Version Checking\r
+       boolean FileFound = false;\r
+       unsigned long id_slib = ID_SLIB;\r
+       unsigned long id_chunk = ID_CHUNK;\r
+\r
+\r
+       //\r
+       // OPEN SOFTLIB FILE\r
+       //\r
+\r
+       if ((handle = open(LibName,O_RDONLY | O_BINARY, S_IREAD)) == -1)\r
+               return(NULL);\r
+\r
+\r
+       //\r
+       //      VERIFY it is a SOFTLIB (SLIB) file\r
+       //\r
+\r
+       if (read(handle,&header,4) == -1)\r
+       {\r
+               close(handle);\r
+               return(NULL);\r
+       }\r
+\r
+       if (header != id_slib)\r
+       {\r
+               close(handle);\r
+               return(NULL);\r
+       }\r
+\r
+\r
+       //\r
+       // CHECK LIBRARY HEADER VERSION NUMBER\r
+       //\r
+\r
+       if (read(handle, &LibraryHeader,sizeof(struct SoftLibHdr)) == -1)\r
+               TrashProg("read error in LoadSLIBFile()\n%c",7);\r
+\r
+       if (LibraryHeader.Version > SOFTLIB_VER)\r
+               TrashProg("Unsupported file ver %d",LibraryHeader.Version);\r
+\r
+\r
+       //\r
+       // MANAGE FILE ENTRY HEADERS...\r
+       //\r
+\r
+       for (x = 1;x<=LibraryHeader.FileCount;x++)\r
+       {\r
+               if (read(handle, &FileEntryHeader,sizeof(struct FileEntryHdr)) == -1)\r
+               {\r
+                       close(handle);\r
+                       return(NULL);\r
+               }\r
+\r
+               if (!stricmp(FileEntryHeader.FileName,FileName))\r
+               {\r
+                       FileEntry = FileEntryHeader;\r
+                       FileFound = true;\r
+               }\r
+       }\r
+\r
+       //\r
+       // IF FILE HAS BEEN FOUND THEN SEEK TO POSITION AND EXTRACT\r
+       //      ELSE RETURN WITH ERROR CODE...\r
+       //\r
+\r
+       if (FileFound)\r
+       {\r
+               if (lseek(handle,FileEntry.Offset,SEEK_CUR) == -1)\r
+               {\r
+                       close(handle);\r
+                       return(NULL);\r
+               }\r
+\r
+               //\r
+               // READ CHUNK HEADER - Verify we are at the beginning of a chunk..\r
+               //\r
+\r
+               if (read(handle,(char *)&Header,sizeof(struct ChunkHeader)) == -1)\r
+                       TrashProg("LIB File - Unable to read Header!");\r
+\r
+               if (Header.HeaderID != id_chunk)\r
+                       TrashProg("LIB File - BAD HeaderID!");\r
+\r
+               //\r
+               // Allocate memory if Necessary...\r
+               //\r
+\r
+\r
+               if (!*MemPtr)\r
+                       MM_GetPtr(MemPtr,FileEntry.OrginalLength);\r
+\r
+               //\r
+               //      Calculate the length of the data (without the chunk header).\r
+               //\r
+\r
+               ChunkLen = FileEntry.ChunkLen - sizeof(struct ChunkHeader);\r
+\r
+\r
+               //\r
+               // Extract Data from file\r
+               //\r
+\r
+               switch (Header.Compression)\r
+               {\r
+                       case ct_LZW:\r
+                               if (!InitBufferedIO(handle,&lzwBIO))\r
+                                       TrashProg("No memory for buffered I/O.");\r
+                               lzwDecompress(&lzwBIO,MK_FP(*MemPtr,0),ChunkLen,(SRC_BFILE|DEST_MEM));\r
+                               FreeBufferedIO(&lzwBIO);\r
+                               break;\r
+\r
+                       case ct_NONE:\r
+                               if (!CA_FarRead(handle,MK_FP(*MemPtr,0),ChunkLen))\r
+                               {\r
+                                       close(handle);\r
+                                       *MemPtr = NULL;\r
+                               }\r
+                               break;\r
+\r
+                       default:\r
+                               close(handle);\r
+                               TrashProg("Uknown Chunk.Compression Type!");\r
+                               break;\r
+               }\r
+       }\r
+       else\r
+               *MemPtr = NULL;\r
+\r
+       close(handle);\r
+       return(*MemPtr);\r
+}\r
+\r
+\r
+\r
+\r
diff --git a/16/cawat/JAMPAK.H b/16/cawat/JAMPAK.H
new file mode 100644 (file)
index 0000000..43f95ed
--- /dev/null
@@ -0,0 +1,118 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+//\r
+//\r
+//\r
+//\r
+\r
+\r
+#define LZW_N          4096\r
+#define LZW_F          18\r
+\r
+\r
+// LZW_THRESHOLD :encode string into position and length if match_length is\r
+// greater than this\r
+\r
+#define LZW_THRESHOLD                          2\r
+\r
+// index for root of binary search trees\r
+//\r
+\r
+#define LZW_NIL                        LZW_N\r
+\r
+\r
+//\r
+// FILE CHUNK IDs\r
+//\r
+// NOTE: The only reason for changing from COMP to CMP2 and having multi\r
+//                     comp header structs is for downward compatablity.\r
+//\r
+\r
+#define COMP                                   ("COMP")                // Comp type is ct_LZW ALWAYS!\r
+#define CMP2                                   ("CMP2")                // Comp type is determined in header.\r
+\r
+\r
+//\r
+//     COMPRESSION TYPES\r
+//\r
+\r
+#if 0\r
+//\r
+//     FILE CHUNK HEADER FORMATS\r
+//\r
+\r
+struct COMPStruct\r
+{\r
+       unsigned long DecompLen;\r
+\r
+};\r
+\r
+\r
+struct CMP2Header\r
+{\r
+       unsigned CompType;\r
+       unsigned long DecompLen;\r
+\r
+};\r
+#endif\r
+\r
+memptr segptr;\r
+extern BufferedIO lzwBIO;\r
+\r
+\r
+//\r
+//       PARAMETER PASSING TYPES (POINTER TYPES)\r
+//\r
+\r
+#define SRC_FILE                               (0x0001)                                // C's non-buffered file i/o\r
+#define SRC_FFILE                              (0x0002)                                // C's buffered ffile i/o\r
+#define SRC_MEM                                (0x0004)                                // FAR memory Ptrs\r
+#define SRC_BFILE                              (0x0008)                                // Buffered File I/O\r
+\r
+#define SRC_TYPES                      (SRC_FILE | SRC_FFILE | SRC_MEM | SRC_BFILE)\r
+\r
+#define DEST_FILE                              (0x0100)                                // C's non-buffered file i/o\r
+#define DEST_FFILE                     (0x0200)                                // C's buffered ffile i/o\r
+#define DEST_MEM                               (0x0400)                                // FAR memory Ptrs\r
+\r
+#define DEST_TYPES                     (DEST_FILE | DEST_FFILE | DEST_MEM)\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+//                                                             FUNCTION PROTOTYPEING\r
+//---------------------------------------------------------------------------\r
+\r
+//void DecompressFILEtoFILE(FILE *infile, FILE *outfile);\r
+//unsigned long CompressFILEtoFILE(FILE *infile, FILE *outfile,unsigned long DataLength);\r
+\r
+\r
+unsigned long lzwCompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes);\r
+void lzwDecompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes);\r
+\r
+int WritePtr(long outfile, unsigned char data, unsigned PtrType);\r
+int ReadPtr(long infile, unsigned PtrType);\r
+\r
+//memptr InitBufferedIO(int handle, BufferedIO *bio);\r
+//void FreeBufferedIO(BufferedIO *bio);\r
+//byte bio_readch(BufferedIO *bio);\r
+\r
+unsigned long BLoad(char *SourceFile, memptr *DstPtr);\r
+memptr LoadLIBFile(char *LibName,char *FileName,memptr *MemPtr);\r
+int LoadLIBShape(char *SLIB_Filename, char *Filename,struct Shape *SHP);\r
diff --git a/16/cawat/JAM_IO.C b/16/cawat/JAM_IO.C
new file mode 100644 (file)
index 0000000..d0b019a
--- /dev/null
@@ -0,0 +1,121 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <ctype.h>\r
+#include <alloc.h>\r
+#include <fcntl.h>\r
+#include <dos.h>\r
+#include <io.h>\r
+\r
+#include "def.h"\r
+#include "gelib.h"\r
+#include "jam_io.h"\r
+\r
+//----------------------------------------------------------------------------\r
+//\r
+//                                                     PTR/PTR COMPRESSION ROUTINES\r
+//\r
+//\r
+//----------------------------------------------------------------------------\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// WritePtr()  -- Outputs data to a particular ptr type\r
+//\r
+//     PtrType MUST be of type DEST_TYPE.\r
+//\r
+// NOTE : For PtrTypes DEST_MEM a ZERO (0) is always returned.\r
+//\r
+//---------------------------------------------------------------------------\r
+char WritePtr(long outfile, unsigned char data, unsigned PtrType)\r
+{\r
+       int returnval = 0;\r
+\r
+       switch (PtrType & DEST_TYPES)\r
+       {\r
+               case DEST_FILE:\r
+                       write(*(int far *)outfile,(char *)&data,1);\r
+               break;\r
+\r
+               case DEST_FFILE:\r
+                       returnval = putc(data, *(FILE **)outfile);\r
+               break;\r
+\r
+               case DEST_IMEM:\r
+                       printf("WritePtr - unsupported ptr type\n");\r
+                       exit(0);\r
+               break;\r
+\r
+               case DEST_MEM:\r
+                       *((char far *)*(char far **)outfile)++ = data;\r
+               break;\r
+       }\r
+\r
+       return(returnval);\r
+\r
+}\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// ReadPtr()  -- Reads data from a particular ptr type\r
+//\r
+//     PtrType MUST be of type SRC_TYPE.\r
+//\r
+// RETURNS :\r
+//             The char read in or EOF for SRC_FFILE type of reads.\r
+//\r
+//\r
+//---------------------------------------------------------------------------\r
+int ReadPtr(long infile, unsigned PtrType)\r
+{\r
+       int returnval = 0;\r
+\r
+       switch (PtrType & SRC_TYPES)\r
+       {\r
+               case SRC_FILE:\r
+                       read(*(int far *)infile,(char *)&returnval,1);\r
+               break;\r
+\r
+               case SRC_FFILE:\r
+                       returnval = getc(*(FILE far **)infile\r
+                       );\r
+               break;\r
+\r
+               case SRC_BFILE:\r
+                       returnval = bio_readch((BufferedIO *)*(void far **)infile);\r
+               break;\r
+\r
+//             case SRC_IMEM:\r
+//                     printf("WritePtr - unsupported ptr type\n");\r
+//                     exit(0);\r
+//             break;\r
+\r
+               case SRC_MEM:\r
+                       returnval = (unsigned char)*((char far *)*(char far **)infile)++;\r
+               break;\r
+       }\r
+\r
+       return(returnval);\r
+}\r
+\r
+\r
+\r
diff --git a/16/cawat/JAM_IO.H b/16/cawat/JAM_IO.H
new file mode 100644 (file)
index 0000000..77db01d
--- /dev/null
@@ -0,0 +1,107 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//\r
+// UNIT : JAM_IO.h\r
+//\r
+// FUNCTION : General defines, prototypes, typedefs used in all the\r
+//                               supported compression techniques used in JAMPAK Ver x.x\r
+//\r
+//\r
+\r
+\r
+\r
+\r
+//==========================================================================\r
+//\r
+//                                                             PARAMETER PASSING TYPES\r
+//\r
+//\r
+       // SOURCE PARAMS (LO BYTE)\r
+\r
+#define SRC_FILE                               (0x0001)                        // GE Buffered IO\r
+#define SRC_FFILE                              (0x0002)                        // Stdio File IO (fwrite etc.)\r
+#define SRC_MEM                                (0x0004)                        // Std void ptr (alloc etc)\r
+#define SRC_BFILE                              (0x0008)                                // Buffered File I/O\r
+\r
+#define SRC_TYPES                      (SRC_FILE | SRC_FFILE | SRC_MEM | SRC_BFILE)\r
+\r
+       // DESTINATION PARAMS (HI BYTE)\r
+\r
+#define DEST_FILE                              (0x0100)                        // GE Buffered IO\r
+#define DEST_FFILE                     (0x0200)                        // Stdio File IO (fwrite etc.)\r
+#define DEST_MEM                               (0x0400)                        // Std void ptr (alloc etc)\r
+#define DEST_IMEM                              (0x0800)                        // ID Memory alloc\r
+\r
+#define DEST_TYPES                     (DEST_FILE | DEST_FFILE | DEST_MEM | DEST_IMEM)\r
+\r
+\r
+\r
+//=========================================================================\r
+//\r
+//                                                             FILE CHUNK IDs\r
+//\r
+// NOTE: The only reason for changing from COMP to CMP1 and having multi\r
+//                     comp header structs is for downward compatablity.\r
+//\r
+\r
+#define COMP                                   ("COMP")                // Comp type is ct_LZW ALWAYS!\r
+#define CMP1                                   ("CMP1")                // Comp type is determined in header.\r
+\r
+\r
+//\r
+//     COMPRESSION TYPES\r
+//\r
+typedef enum ct_TYPES\r
+{\r
+               ct_NONE = 0,                                            // No compression - Just data..Rarely used!\r
+               ct_LZW,                                                         // LZW data compression\r
+               ct_LZH,\r
+\r
+} ct_TYPES;\r
+\r
+//\r
+//     FILE CHUNK HEADER FORMATS\r
+//\r
+\r
+struct COMPStruct\r
+{\r
+       unsigned long DecompLen;\r
+\r
+};\r
+\r
+\r
+struct CMP1Header\r
+{\r
+       unsigned CompType;                                      // SEE: ct_TYPES above for list of pos.\r
+       unsigned long OrginalLen;                       // Orginal FileLength of compressed Data.\r
+       unsigned long CompressLen;                      // Length of data after compression (A MUST for LZHUFF!)\r
+};\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+//\r
+//                                                             FUNCTION PROTOTYPEING\r
+//\r
+//---------------------------------------------------------------------------\r
+\r
+char WritePtr(long outfile, unsigned char data, unsigned PtrType);\r
+int ReadPtr(long infile, unsigned PtrType);\r
+\r
+\r
diff --git a/16/cawat/LZHUF.C b/16/cawat/LZHUF.C
new file mode 100644 (file)
index 0000000..bca738e
--- /dev/null
@@ -0,0 +1,1069 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//===========================================================================\r
+//\r
+//                                                              LZHUFF COMPRESSION ROUTINES\r
+//                                                                               VERSION 1.0\r
+//\r
+//                             Compression algrythim by Haruhiko OKUMURA\r
+//                                             Implementation by Jim T. Row\r
+//\r
+//\r
+//   Copyright (c) 1992 - Softdisk Publishing inc. - All rights reserved\r
+//\r
+//===========================================================================\r
+//\r
+// Compiler #ifdef switches\r
+//\r
+//     LZHUFF_COMPRESSION & LZHUFF_DECOMPRESSION               - not yet functional!\r
+//\r
+// Usage Explanition :\r
+//\r
+//    if LZHUFF_COMPRESSION is defined then the compression code & data is\r
+//    compiled and so-forth for the decompression code.\r
+//\r
+//---------------------------------------------------------------------------\r
+\r
+\r
+\r
+#include <fcntl.h>\r
+#include <io.h>\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <ctype.h>\r
+#include <alloc.h>\r
+#include <dos.h>\r
+\r
+#include "lzhuff.h"\r
+#include "jam_io.h"\r
+\r
+\r
+\r
+//===========================================================================\r
+//\r
+//                                                                                     SWITCHES\r
+//\r
+// NOTE : Make sure the appropriate switches are set in SOFT.c for Softlib\r
+//                      archive support.\r
+//\r
+//===========================================================================\r
+\r
+\r
+#define INCLUDE_LZH_COMP                       0\r
+#define INCLUDE_LZH_DECOMP                     1\r
+\r
+\r
+\r
+\r
+\r
+//===========================================================================\r
+//\r
+//                                                                                     DEFINES\r
+//\r
+//===========================================================================\r
+\r
+\r
+#define EXIT_OK                        0\r
+#define EXIT_FAILED            -1\r
+\r
+/* LZSS Parameters */\r
+\r
+#define N                              4096                                                            /* Size of string buffer */\r
+#define F                              30                                                                      /* Size of look-ahead buffer */\r
+#define THRESHOLD              2\r
+#define NIL                            N                                                                       /* End of tree's node  */\r
+\r
+/* Huffman coding parameters */\r
+\r
+#define N_CHAR                 (256 - THRESHOLD + F)           /* character code (= 0..N_CHAR-1) */\r
+#define T                              (N_CHAR * 2 - 1)                                /* Size of table */\r
+#define R                              (T - 1)                                                 /* root position */\r
+#define MAX_FREQ               0x8000                     /* update when cumulative frequency */\r
+                                                                                                                               /* reaches to this value */\r
+\r
+\r
+//==========================================================================\r
+//\r
+//                                                             LOCAL PROTOTYPES\r
+//\r
+//==========================================================================\r
+\r
+\r
+static void StartHuff();\r
+static void reconst();\r
+static void update(int c);\r
+\r
+\r
+static void DeleteNode(int p);  /* Deleting node from the tree */\r
+static void InsertNode(int r);  /* Inserting node to the tree */\r
+static void InitTree(void);  /* Initializing tree */\r
+static void Putcode(long outfile_ptr, int l, unsigned c,unsigned PtrTypes);            /* output c bits */\r
+static void EncodeChar(long outfile_ptr, unsigned c, unsigned PtrTypes);\r
+static void EncodePosition(long outfile_ptr, unsigned c, unsigned PtrTypes);\r
+static void EncodeEnd(long outfile_ptr,unsigned PtrTypes);\r
+\r
+\r
+static int GetByte(long infile_ptr, unsigned long *CompressLength, unsigned PtrTypes);\r
+static int GetBit(long infile_ptr, unsigned long *CompressLength, unsigned PtrTypes);  /* get one bit */\r
+static int DecodeChar(long infile_ptr, unsigned long *CompressLength, unsigned PtrTypes);\r
+static int DecodePosition(long infile_ptr,unsigned long *CompressLength, unsigned PtrTypes);\r
+\r
+\r
+\r
+\r
+//==========================================================================\r
+//\r
+//                                                             USER AVAILABLE VECTORS\r
+//\r
+//==========================================================================\r
+\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+//\r
+//                                                             LZHUFF DISPLAY VECTORS\r
+//\r
+// These vectors allow you to hook up any form of display you desire for\r
+// displaying the compression/decompression status.\r
+//\r
+// These routines are called inside of the compression/decompression routines\r
+// and pass the orginal size of data and current position within that\r
+// data.  This allows for any kind of "% Done" messages.\r
+//\r
+// Your functions MUST have the following parameters in this order...\r
+//\r
+//   void VectorRoutine(unsigned long OrginalSize,unsigned long CurPosition)\r
+//\r
+//\r
+\r
+#if INCLUDE_LZH_COMP\r
+void (*LZH_CompressDisplayVector)() = NULL;\r
+#endif\r
+\r
+#if INCLUDE_LZH_DECOMP\r
+void (*LZH_DecompressDisplayVector)() = NULL;\r
+#endif\r
+\r
+\r
+\r
+\r
+//===========================================================================\r
+//\r
+//                                                                                     GLOBAL VARIABLES\r
+//\r
+//===========================================================================\r
+       /* pointing children nodes (son[], son[] + 1)*/\r
+\r
+int far son[T];\r
+unsigned code, len;\r
+\r
+       //\r
+       // pointing parent nodes.\r
+       // area [T..(T + N_CHAR - 1)] are pointers for leaves\r
+       //\r
+\r
+int far prnt[T + N_CHAR];\r
+\r
+unsigned far freq[T + 1];      /* cumulative freq table */\r
+\r
+unsigned long textsize = 0, codesize = 0, printcount = 0,datasize;\r
+unsigned char far text_buf[N + F - 1];\r
+\r
+\r
+\r
+       //\r
+       // COMPRESSION VARIABLES\r
+       //\r
+\r
+#if INCLUDE_LZH_COMP\r
+\r
+static int match_position,match_length, lson[N + 1], rson[N + 257], dad[N + 1];\r
+unsigned putbuf = 0;\r
+unsigned char putlen = 0;\r
+\r
+       //\r
+       // Tables for encoding/decoding upper 6 bits of\r
+       // sliding dictionary pointer\r
+       //\r
+\r
+       //\r
+       // encoder table\r
+       //\r
+\r
+unsigned char far p_len[64] = {\r
+       0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05,\r
+       0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06,\r
+       0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
+       0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
+       0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
+       0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
+       0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
+       0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08\r
+};\r
+\r
+unsigned char far p_code[64] = {\r
+       0x00, 0x20, 0x30, 0x40, 0x50, 0x58, 0x60, 0x68,\r
+       0x70, 0x78, 0x80, 0x88, 0x90, 0x94, 0x98, 0x9C,\r
+       0xA0, 0xA4, 0xA8, 0xAC, 0xB0, 0xB4, 0xB8, 0xBC,\r
+       0xC0, 0xC2, 0xC4, 0xC6, 0xC8, 0xCA, 0xCC, 0xCE,\r
+       0xD0, 0xD2, 0xD4, 0xD6, 0xD8, 0xDA, 0xDC, 0xDE,\r
+       0xE0, 0xE2, 0xE4, 0xE6, 0xE8, 0xEA, 0xEC, 0xEE,\r
+       0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,\r
+       0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF\r
+};\r
+#endif\r
+\r
+\r
+       //\r
+       // DECOMPRESSION VARIABLES\r
+       //\r
+\r
+\r
+       //\r
+       // decoder table\r
+       //\r
+\r
+#if INCLUDE_LZH_DECOMP\r
+\r
+unsigned char far d_code[256] = {\r
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
+       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\r
+       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\r
+       0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,\r
+       0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,\r
+       0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
+       0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
+       0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
+       0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
+       0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
+       0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
+       0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
+       0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,\r
+       0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A,\r
+       0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,\r
+       0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D,\r
+       0x0E, 0x0E, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F,\r
+       0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11,\r
+       0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13,\r
+       0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15,\r
+       0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17,\r
+       0x18, 0x18, 0x19, 0x19, 0x1A, 0x1A, 0x1B, 0x1B,\r
+       0x1C, 0x1C, 0x1D, 0x1D, 0x1E, 0x1E, 0x1F, 0x1F,\r
+       0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23,\r
+       0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27,\r
+       0x28, 0x28, 0x29, 0x29, 0x2A, 0x2A, 0x2B, 0x2B,\r
+       0x2C, 0x2C, 0x2D, 0x2D, 0x2E, 0x2E, 0x2F, 0x2F,\r
+       0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\r
+       0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,\r
+};\r
+\r
+unsigned char far d_len[256] = {\r
+       0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
+       0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
+       0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
+       0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,\r
+       0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
+       0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
+       0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
+       0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
+       0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
+       0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\r
+       0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
+       0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
+       0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
+       0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
+       0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
+       0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
+       0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
+       0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,\r
+       0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
+       0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
+       0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
+       0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
+       0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
+       0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,\r
+       0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
+       0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
+       0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
+       0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
+       0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
+       0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,\r
+       0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
+       0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,\r
+};\r
+\r
+unsigned getbuf = 0;\r
+unsigned char getlen = 0;\r
+\r
+#endif\r
+\r
+\r
+\r
+//===========================================================================\r
+//\r
+//                                                     COMPRESSION & DECOMPRESSION ROUTINES\r
+//\r
+//===========================================================================\r
+\r
+\r
+\r
+\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+//  StartHuff    /* initialize freq tree */\r
+//---------------------------------------------------------------------------\r
+static void StartHuff()\r
+{\r
+       int i, j;\r
+\r
+       for (i = 0; i < N_CHAR; i++) {\r
+               freq[i] = 1;\r
+               son[i] = i + T;\r
+               prnt[i + T] = i;\r
+       }\r
+       i = 0; j = N_CHAR;\r
+       while (j <= R) {\r
+               freq[j] = freq[i] + freq[i + 1];\r
+               son[j] = i;\r
+               prnt[i] = prnt[i + 1] = j;\r
+               i += 2; j++;\r
+       }\r
+       freq[T] = 0xffff;\r
+       prnt[R] = 0;\r
+}\r
+\r
+\r
+\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+//   reconst        /* reconstruct freq tree */\r
+//---------------------------------------------------------------------------\r
+static void reconst()\r
+{\r
+       int i, j, k;\r
+       unsigned f, l;\r
+\r
+       /* halven cumulative freq for leaf nodes */\r
+\r
+       j = 0;\r
+\r
+       for (i = 0; i < T; i++)\r
+       {\r
+               if (son[i] >= T)\r
+               {\r
+                       freq[j] = (freq[i] + 1) / 2;\r
+                       son[j] = son[i];\r
+                       j++;\r
+               }\r
+       }\r
+\r
+       /* make a tree : first, connect children nodes */\r
+\r
+       for (i = 0, j = N_CHAR; j < T; i += 2, j++)\r
+       {\r
+               k = i + 1;\r
+               f = freq[j] = freq[i] + freq[k];\r
+\r
+               for (k = j - 1;f < freq[k]; k--);\r
+\r
+               k++;\r
+               l = (j - k) * 2;\r
+\r
+               (void)memmove(&freq[k + 1], &freq[k], l);\r
+               freq[k] = f;\r
+\r
+               (void)memmove(&son[k + 1], &son[k], l);\r
+               son[k] = i;\r
+       }\r
+\r
+       /* connect parent nodes */\r
+\r
+       for (i = 0; i < T; i++)\r
+       {\r
+               if ((k = son[i]) >= T)\r
+               {\r
+                       prnt[k] = i;\r
+               }\r
+               else\r
+               {\r
+                       prnt[k] = prnt[k + 1] = i;\r
+               }\r
+       }\r
+}\r
+\r
+\r
+\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+//  update()    update freq tree\r
+//---------------------------------------------------------------------------\r
+static void update(int c)\r
+{\r
+       int i, j, k, l;\r
+\r
+       if (freq[R] == MAX_FREQ)\r
+       {\r
+               reconst();\r
+       }\r
+\r
+       c = prnt[c + T];\r
+\r
+       do {\r
+               k = ++freq[c];\r
+\r
+               //\r
+               // swap nodes to keep the tree freq-ordered\r
+               //\r
+\r
+               if (k > freq[l = c + 1])\r
+               {\r
+                       while (k > freq[++l]);\r
+\r
+                       l--;\r
+                       freq[c] = freq[l];\r
+                       freq[l] = k;\r
+\r
+                       i = son[c];\r
+                       prnt[i] = l;\r
+                       if (i < T)\r
+                               prnt[i + 1] = l;\r
+\r
+                       j = son[l];\r
+                       son[l] = i;\r
+\r
+                       prnt[j] = c;\r
+                       if (j < T)\r
+                               prnt[j + 1] = c;\r
+\r
+                       son[c] = j;\r
+\r
+                       c = l;\r
+               }\r
+       } while ((c = prnt[c]) != 0);   /* do it until reaching the root */\r
+}\r
+\r
+\r
+\r
+\r
+//===========================================================================\r
+//\r
+//                                                                      COMPRESSION ROUTINES\r
+//\r
+//===========================================================================\r
+\r
+\r
+\r
+\r
+\r
+\r
+#if INCLUDE_LZH_COMP\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// DeleteNode\r
+//---------------------------------------------------------------------------\r
+static void DeleteNode(int p)  /* Deleting node from the tree */\r
+{\r
+       int  q;\r
+\r
+       if (dad[p] == NIL)\r
+               return;                 /* unregistered */\r
+\r
+       if (rson[p] == NIL)\r
+               q = lson[p];\r
+       else\r
+       if (lson[p] == NIL)\r
+               q = rson[p];\r
+       else\r
+       {\r
+               q = lson[p];\r
+               if (rson[q] != NIL)\r
+               {\r
+                       do {\r
+                               q = rson[q];\r
+                       } while (rson[q] != NIL);\r
+\r
+                       rson[dad[q]] = lson[q];\r
+                       dad[lson[q]] = dad[q];\r
+                       lson[q] = lson[p];\r
+                       dad[lson[p]] = q;\r
+               }\r
+\r
+               rson[q] = rson[p];\r
+               dad[rson[p]] = q;\r
+       }\r
+\r
+       dad[q] = dad[p];\r
+\r
+       if (rson[dad[p]] == p)\r
+               rson[dad[p]] = q;\r
+       else\r
+               lson[dad[p]] = q;\r
+\r
+       dad[p] = NIL;\r
+}\r
+\r
+\r
+\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+//  InsertNode\r
+//---------------------------------------------------------------------------\r
+static void InsertNode(int r)  /* Inserting node to the tree */\r
+{\r
+       int  i, p, cmp;\r
+       unsigned char  *key;\r
+       unsigned c;\r
+\r
+       cmp = 1;\r
+       key = &text_buf[r];\r
+       p = N + 1 + key[0];\r
+       rson[r] = lson[r] = NIL;\r
+       match_length = 0;\r
+       for ( ; ; )\r
+       {\r
+               if (cmp >= 0)\r
+               {\r
+                       if (rson[p] != NIL)\r
+                               p = rson[p];\r
+                       else\r
+                       {\r
+                               rson[p] = r;\r
+                               dad[r] = p;\r
+                               return;\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       if (lson[p] != NIL)\r
+                               p = lson[p];\r
+                       else\r
+                       {\r
+                               lson[p] = r;\r
+                               dad[r] = p;\r
+                               return;\r
+                       }\r
+               }\r
+\r
+\r
+               for (i = 1; i < F; i++)\r
+                       if ((cmp = key[i] - text_buf[p + i]) != 0)\r
+                               break;\r
+\r
+               if (i > THRESHOLD)\r
+               {\r
+                       if (i > match_length)\r
+                       {\r
+                               match_position = ((r - p) & (N - 1)) - 1;\r
+                               if ((match_length = i) >= F)\r
+                                       break;\r
+                       }\r
+\r
+                       if (i == match_length)\r
+                       {\r
+                               if ((c = ((r - p) & (N - 1)) - 1) < match_position)\r
+                               {\r
+                                       match_position = c;\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       dad[r] = dad[p];\r
+       lson[r] = lson[p];\r
+       rson[r] = rson[p];\r
+       dad[lson[p]] = r;\r
+       dad[rson[p]] = r;\r
+\r
+       if (rson[dad[p]] == p)\r
+               rson[dad[p]] = r;\r
+       else\r
+               lson[dad[p]] = r;\r
+\r
+       dad[p] = NIL;  /* remove p */\r
+}\r
+\r
+\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// InitTree\r
+//---------------------------------------------------------------------------\r
+static void InitTree(void)  /* Initializing tree */\r
+{\r
+       int  i;\r
+\r
+       for (i = N + 1; i <= N + 256; i++)\r
+               rson[i] = NIL;                  /* root */\r
+\r
+       for (i = 0; i < N; i++)\r
+               dad[i] = NIL;                   /* node */\r
+}\r
+\r
+\r
+\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+//  Putcode\r
+//---------------------------------------------------------------------------\r
+static void Putcode(long outfile_ptr, int l, unsigned c,unsigned PtrTypes)             /* output c bits */\r
+{\r
+       putbuf |= c >> putlen;\r
+\r
+       if ((putlen += l) >= 8)\r
+       {\r
+               WritePtr(outfile_ptr, putbuf >> 8, PtrTypes);\r
+               codesize++;\r
+\r
+               if ((putlen -= 8) >= 8)\r
+               {\r
+                       WritePtr(outfile_ptr, putbuf, PtrTypes);\r
+                       codesize++;\r
+\r
+                       putlen -= 8;\r
+                       putbuf = c << (l - putlen);\r
+               }\r
+               else\r
+               {\r
+                       putbuf <<= 8;\r
+               }\r
+       }\r
+}\r
+\r
+\r
+\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+//  EncodeChar\r
+//---------------------------------------------------------------------------\r
+static void EncodeChar(long outfile_ptr, unsigned c, unsigned PtrTypes)\r
+{\r
+       unsigned i;\r
+       int j, k;\r
+\r
+       i = 0;\r
+       j = 0;\r
+       k = prnt[c + T];\r
+\r
+       /* search connections from leaf node to the root */\r
+\r
+       do {\r
+               i >>= 1;\r
+\r
+               //\r
+               // if node's address is odd, output 1 else output 0\r
+               //\r
+\r
+               if (k & 1)\r
+                       i += 0x8000;\r
+\r
+               j++;\r
+       } while ((k = prnt[k]) != R);\r
+\r
+       Putcode(outfile_ptr, j, i, PtrTypes);\r
+\r
+       code = i;\r
+       len = j;\r
+       update(c);\r
+}\r
+\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// EncodePosition\r
+//---------------------------------------------------------------------------\r
+static void EncodePosition(long outfile_ptr, unsigned c, unsigned PtrTypes)\r
+{\r
+       unsigned i;\r
+\r
+       //\r
+       // output upper 6 bits with encoding\r
+       //\r
+\r
+       i = c >> 6;\r
+       Putcode(outfile_ptr, p_len[i], (unsigned)p_code[i] << 8,PtrTypes);\r
+\r
+       //\r
+       // output lower 6 bits directly\r
+       //\r
+\r
+       Putcode(outfile_ptr, 6, (c & 0x3f) << 10,PtrTypes);\r
+}\r
+\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// EncodeEnd\r
+//---------------------------------------------------------------------------\r
+static void EncodeEnd(long outfile_ptr,unsigned PtrTypes)\r
+{\r
+       if (putlen)\r
+       {\r
+               WritePtr(outfile_ptr,(putbuf >> 8),PtrTypes);\r
+               codesize++;\r
+       }\r
+}\r
+\r
+#endif\r
+\r
+\r
+\r
+\r
+\r
+//===========================================================================\r
+//\r
+//                                                                     DECOMPRESSION ROUTINES\r
+//\r
+//===========================================================================\r
+\r
+\r
+\r
+#if INCLUDE_LZH_DECOMP\r
+\r
+//---------------------------------------------------------------------------\r
+// GetByte\r
+//---------------------------------------------------------------------------\r
+static int GetByte(long infile_ptr, unsigned long *CompressLength, unsigned PtrTypes)\r
+{\r
+       unsigned i;\r
+\r
+       while (getlen <= 8)\r
+       {\r
+               if (*CompressLength)\r
+               {\r
+                       i = ReadPtr(infile_ptr,PtrTypes);\r
+                       (*CompressLength)--;\r
+               }\r
+               else\r
+                       i = 0;\r
+\r
+               getbuf |= i << (8 - getlen);\r
+               getlen += 8;\r
+       }\r
+\r
+       i = getbuf;\r
+       getbuf <<= 8;\r
+       getlen -= 8;\r
+       return i>>8;\r
+}\r
+\r
+\r
+\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// GetBit\r
+//---------------------------------------------------------------------------\r
+static int GetBit(long infile_ptr, unsigned long *CompressLength, unsigned PtrTypes)   /* get one bit */\r
+{\r
+       int i;\r
+\r
+       while (getlen <= 8)\r
+       {\r
+               if (*CompressLength)\r
+               {\r
+                       i = ReadPtr(infile_ptr,PtrTypes);\r
+                       (*CompressLength)--;\r
+               }\r
+               else\r
+                       i = 0;\r
+\r
+               getbuf |= i << (8 - getlen);\r
+               getlen += 8;\r
+       }\r
+\r
+       i = getbuf;\r
+       getbuf <<= 1;\r
+       getlen--;\r
+       return (i < 0);\r
+}\r
+\r
+\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// DecodeChar\r
+//---------------------------------------------------------------------------\r
+static int DecodeChar(long infile_ptr, unsigned long *CompressLength, unsigned PtrTypes)\r
+{\r
+       unsigned c;\r
+\r
+       c = son[R];\r
+\r
+       /*\r
+        * start searching tree from the root to leaves.\r
+        * choose node #(son[]) if input bit == 0\r
+        * else choose #(son[]+1) (input bit == 1)\r
+        */\r
+\r
+       while (c < T)\r
+       {\r
+               c += GetBit(infile_ptr,CompressLength,PtrTypes);\r
+               c = son[c];\r
+       }\r
+\r
+       c -= T;\r
+       update(c);\r
+       return c;\r
+}\r
+\r
+\r
+\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+// DecodePosition\r
+//---------------------------------------------------------------------------\r
+static int DecodePosition(long infile_ptr,unsigned long *CompressLength, unsigned PtrTypes)\r
+{\r
+       unsigned i, j, c;\r
+\r
+       //\r
+       // decode upper 6 bits from given table\r
+       //\r
+\r
+       i = GetByte(infile_ptr, CompressLength, PtrTypes);\r
+       c = (unsigned)d_code[i] << 6;\r
+       j = d_len[i];\r
+\r
+       //\r
+       // input lower 6 bits directly\r
+       //\r
+\r
+       j -= 2;\r
+       while (j--)\r
+       {\r
+               i = (i << 1) + GetBit(infile_ptr, CompressLength, PtrTypes);\r
+       }\r
+\r
+       return c | i & 0x3f;\r
+}\r
+\r
+#endif\r
+\r
+\r
+\r
+\r
+\r
+//===========================================================================\r
+//\r
+//                                                                     EXTERNAL REFERENCED\r
+//                                                       COMPRESSION & DECOMPRESSION\r
+//                                                                          ROUTINES\r
+//\r
+//===========================================================================\r
+\r
+\r
+\r
+\r
+#if INCLUDE_LZH_DECOMP\r
+\r
+//---------------------------------------------------------------------------\r
+// lzhDecompress()\r
+//---------------------------------------------------------------------------\r
+long lzhDecompress(void far *infile, void far *outfile, unsigned long OrginalLength, unsigned long CompressLength, unsigned PtrTypes)\r
+{\r
+       int  i, j, k, r, c;\r
+       long count;\r
+\r
+       datasize = textsize = OrginalLength;\r
+       getbuf = 0;\r
+       getlen = 0;\r
+\r
+       if (textsize == 0)\r
+               return;\r
+\r
+       StartHuff();\r
+       for (i = 0; i < N - F; i++)\r
+               text_buf[i] = ' ';\r
+\r
+       r = N - F;\r
+\r
+       for (count = 0; count < textsize; )\r
+       {\r
+               c = DecodeChar((long)&infile,&CompressLength,PtrTypes);\r
+\r
+               if (c < 256)\r
+               {\r
+                       WritePtr((long)&outfile,c,PtrTypes);\r
+                       datasize--;                                                             // Dec # of bytes to write\r
+\r
+                       text_buf[r++] = c;\r
+                       r &= (N - 1);\r
+                       count++;                                                                        // inc count of bytes written\r
+               }\r
+               else\r
+               {\r
+                       i = (r - DecodePosition((long)&infile,&CompressLength,PtrTypes) - 1) & (N - 1);\r
+                       j = c - 255 + THRESHOLD;\r
+\r
+                       for (k = 0; k < j; k++)\r
+                       {\r
+                               c = text_buf[(i + k) & (N - 1)];\r
+\r
+                               WritePtr((long)&outfile,c,PtrTypes);\r
+                               datasize--;                                                     // dec count of bytes to write\r
+\r
+                               text_buf[r++] = c;\r
+                               r &= (N - 1);\r
+                               count++;                                                                // inc count of bytes written\r
+                       }\r
+               }\r
+\r
+               if (LZH_DecompressDisplayVector && (count > printcount))\r
+               {\r
+                       LZH_DecompressDisplayVector(OrginalLength,OrginalLength-datasize);\r
+                       printcount += 1024;\r
+               }\r
+       }\r
+\r
+//     printf("%12ld\n", count);\r
+\r
+       return(count);\r
+}\r
+\r
+#endif\r
+\r
+\r
+\r
+\r
+\r
+#if INCLUDE_LZH_COMP\r
+\r
+//---------------------------------------------------------------------------\r
+// lzhCompress()\r
+//---------------------------------------------------------------------------\r
+long lzhCompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes)\r
+{\r
+       int  i, c, len, r, s, last_match_length;\r
+\r
+       textsize = DataLength;\r
+\r
+       if (textsize == 0)\r
+               return;\r
+\r
+       getbuf = 0;\r
+       getlen = 0;\r
+       textsize = 0;                   /* rewind and rescan */\r
+       codesize = 0;\r
+       datasize = 0;                   // Init our counter of ReadData...\r
+       StartHuff();\r
+       InitTree();\r
+\r
+       s = 0;\r
+       r = N - F;\r
+\r
+       for (i = s; i < r; i++)\r
+               text_buf[i] = ' ';\r
+\r
+       for (len = 0; len < F && (DataLength > datasize); len++)\r
+       {\r
+               c = ReadPtr((long)&infile,PtrTypes);\r
+               datasize++;                                                     // Dec num of bytes to compress\r
+               text_buf[r + len] = c;\r
+       }\r
+\r
+       textsize = len;\r
+\r
+       for (i = 1; i <= F; i++)\r
+               InsertNode(r - i);\r
+\r
+       InsertNode(r);\r
+\r
+       do {\r
+               if (match_length > len)\r
+                       match_length = len;\r
+\r
+               if (match_length <= THRESHOLD)\r
+               {\r
+                       match_length = 1;\r
+                       EncodeChar((long)&outfile,text_buf[r],PtrTypes);\r
+               }\r
+               else\r
+               {\r
+                       EncodeChar((long)&outfile, 255 - THRESHOLD + match_length,PtrTypes);\r
+                       EncodePosition((long)&outfile, match_position,PtrTypes);\r
+               }\r
+\r
+               last_match_length = match_length;\r
+\r
+               for (i = 0; i < last_match_length && (DataLength > datasize); i++)\r
+               {\r
+                       c = ReadPtr((long)&infile,PtrTypes);\r
+                       datasize++;\r
+\r
+                       DeleteNode(s);\r
+                       text_buf[s] = c;\r
+\r
+                       if (s < F - 1)\r
+                               text_buf[s + N] = c;\r
+\r
+                       s = (s + 1) & (N - 1);\r
+                       r = (r + 1) & (N - 1);\r
+                       InsertNode(r);\r
+               }\r
+\r
+               if (LZH_CompressDisplayVector && ((textsize += i) > printcount))\r
+               {\r
+                       LZH_CompressDisplayVector(DataLength,datasize);\r
+                       printcount += 1024;\r
+               }\r
+\r
+\r
+               while (i++ < last_match_length)\r
+               {\r
+                       DeleteNode(s);\r
+                       s = (s + 1) & (N - 1);\r
+                       r = (r + 1) & (N - 1);\r
+                       if (--len)\r
+                               InsertNode(r);\r
+               }\r
+\r
+       } while (len > 0);\r
+\r
+       EncodeEnd((long)&outfile,PtrTypes);\r
+\r
+       return(codesize);\r
+\r
+}\r
+\r
+\r
+#endif
\ No newline at end of file
diff --git a/16/cawat/LZHUFF.H b/16/cawat/LZHUFF.H
new file mode 100644 (file)
index 0000000..1e4395e
--- /dev/null
@@ -0,0 +1,35 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+extern void (*LZH_CompressDisplayVector)();\r
+extern void (*LZH_DecompressDisplayVector)();\r
+\r
+\r
+\r
+//===========================================================================\r
+//\r
+//                                                                                     PROTOTYPES\r
+//\r
+//===========================================================================\r
+\r
+\r
+long lzhCompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes);\r
+long lzhDecompress(void far *infile, void far *outfile, unsigned long OrginalLength, unsigned long CompressLength, unsigned PtrTypes);\r
+\r
+\r
+\r
diff --git a/16/cawat/LZW.C b/16/cawat/LZW.C
new file mode 100644 (file)
index 0000000..540cc7d
--- /dev/null
@@ -0,0 +1,608 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//===========================================================================\r
+//\r
+//                                                               LZW COMPRESSION ROUTINES\r
+//                                                                               VERSION 1.1\r
+//\r
+//                             Compression algrythim by Haruhiko OKUMURA\r
+//                                             Implementation by Jim T. Row\r
+//\r
+//\r
+//   Copyright (c) 1992 - Softdisk Publishing inc. - All rights reserved\r
+//\r
+//===========================================================================\r
+//\r
+//\r
+//---------------------------------------------------------------------------\r
+\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <ctype.h>\r
+#include <alloc.h>\r
+#include <fcntl.h>\r
+#include <dos.h>\r
+#include <io.h>\r
+\r
+#include "lzw.h"\r
+#include "jam_io.h"\r
+\r
+\r
+//===========================================================================\r
+//\r
+//                                                                                     SWITCHES\r
+//\r
+// NOTE : Make sure the appropriate switches are set in SOFT.c for Softlib\r
+//                      archive support.\r
+//\r
+//===========================================================================\r
+\r
+#define INCLUDE_LZW_COMP                       0\r
+#define INCLUDE_LZW_DECOMP                     1\r
+\r
+\r
+//===========================================================================\r
+//\r
+//                                                                                     DEFINES\r
+//\r
+//===========================================================================\r
+\r
+\r
+#define LZW_N                                                  4096\r
+#define LZW_F                                                  18\r
+\r
+#define LZW_THRESHOLD                          2               // encode string into position and\r
+                                                                                                       // length if match_length is greater\r
+                                                                                                       // than this\r
+\r
+#define LZW_NIL                        LZW_N    // index for root of bin search trees\r
+\r
+\r
+//============================================================================\r
+//\r
+//                                                             GLOBAL VARIABLES NECESSARY FOR\r
+//\r
+//                                                      COMP/DECOMP ROUTINES.\r
+//\r
+//============================================================================\r
+\r
+\r
+\r
+unsigned char far LZW_ring_buffer[LZW_N + LZW_F - 1];   // ring buffer of size\r
+                                                                                                                                                 // LZW_N, with extra\r
+                                                                                                                                                 // LZW_F-1 bytes to\r
+                                                                                                                                                 // facilitate\r
+                                                                                                                                                 // string comparison\r
+\r
+#if INCLUDE_LZW_COMP\r
+\r
+int LZW_match_pos,     // MAtchLength of longest match.  These are set by the\r
+                                                       // InsertNode() procedure.\r
+        LZW_match_len,\r
+\r
+       // left & right children & parents -- These constitute binary search trees. */\r
+\r
+       LZW_left_child[LZW_N + 1],\r
+       LZW_right_child[LZW_N + 257],\r
+       LZW_parent[LZW_N + 1];\r
+\r
+#endif\r
+\r
+\r
+//============================================================================\r
+//\r
+//                                                                     LZW DISPLAY VECTORS\r
+//\r
+// These vectors allow you to hook up any form of display you desire for\r
+// displaying the compression/decompression status.\r
+//\r
+// These routines are called inside of the compression/decompression routines\r
+// and pass the orginal size of data and current position within that\r
+// data.  This allows for any kind of "% Done" messages.\r
+//\r
+// Your functions MUST have the following parameters in this order...\r
+//\r
+//   void VectorRoutine(unsigned long OrginalSize,unsigned long CurPosition)\r
+//\r
+//\r
+\r
+void (*LZW_CompressDisplayVector)() = NULL;\r
+void (*LZW_DecompressDisplayVector)() = NULL;\r
+\r
+\r
+\r
+\r
+//============================================================================\r
+//\r
+//                                                     SUPPORT ROUTINES FOR LZW COMPRESSION\r
+//\r
+//============================================================================\r
+\r
+\r
+#if INCLUDE_LZW_COMP\r
+\r
+static void InitLZWTree(void)  /* initialize trees */\r
+{\r
+        int i;\r
+\r
+        /* For i = 0 to LZW_N - 1, LZW_right_child[i] and LZW_left_child[i] will be the right and\r
+                left children of node i.  These nodes need not be initialized.\r
+                Also, LZW_parent[i] is the parent of node i.  These are initialized to\r
+                LZW_NIL (= LZW_N), which stands for 'not used.'\r
+                For i = 0 to 255, LZW_right_child[LZW_N + i + 1] is the root of the tree\r
+                for strings that begin with character i.  These are initialized\r
+                to LZW_NIL.  Note there are 256 trees. */\r
+\r
+        for (i = LZW_N + 1; i <= LZW_N + 256; i++)\r
+               LZW_right_child[i] = LZW_NIL;\r
+\r
+        for (i = 0; i < LZW_N; i++)\r
+               LZW_parent[i] = LZW_NIL;\r
+}\r
+\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+\r
+static void InsertLZWNode(unsigned long r)\r
+\r
+        /* Inserts string of length LZW_F, LZW_ring_buffer[r..r+LZW_F-1], into one of the\r
+                trees (LZW_ring_buffer[r]'th tree) and returns the longest-match position\r
+                and length via the global variables LZW_match_pos and LZW_match_len.\r
+                If LZW_match_len = LZW_F, then removes the old node in favor of the new\r
+                one, because the old one will be deleted sooner.\r
+                Note r plays double role, as tree node and position in buffer. */\r
+{\r
+        int  i, p, cmp;\r
+        unsigned char *key;\r
+\r
+        cmp = 1;\r
+        key = &LZW_ring_buffer[r];\r
+        p = LZW_N + 1 + key[0];\r
+        LZW_right_child[r] = LZW_left_child[r] = LZW_NIL;\r
+        LZW_match_len = 0;\r
+\r
+       for ( ; ; )\r
+       {\r
+               if (cmp >= 0)\r
+               {\r
+                       if (LZW_right_child[p] != LZW_NIL)\r
+                               p = LZW_right_child[p];\r
+                       else\r
+                       {\r
+                               LZW_right_child[p] = r;\r
+                               LZW_parent[r] = p;\r
+                               return;\r
+                       }\r
+               }\r
+               else\r
+               {\r
+                       if (LZW_left_child[p] != LZW_NIL)\r
+                               p = LZW_left_child[p];\r
+                       else\r
+                       {\r
+                               LZW_left_child[p] = r;\r
+                               LZW_parent[r] = p;\r
+                               return;\r
+                       }\r
+               }\r
+\r
+               for (i = 1; i < LZW_F; i++)\r
+                       if ((cmp = key[i] - LZW_ring_buffer[p + i]) != 0)\r
+                               break;\r
+\r
+               if (i > LZW_match_len)\r
+               {\r
+                       LZW_match_pos = p;\r
+                       if ((LZW_match_len = i) >= LZW_F)\r
+                               break;\r
+               }\r
+       }\r
+\r
+       LZW_parent[r] = LZW_parent[p];\r
+       LZW_left_child[r] = LZW_left_child[p];\r
+       LZW_right_child[r] = LZW_right_child[p];\r
+       LZW_parent[LZW_left_child[p]] = r;\r
+       LZW_parent[LZW_right_child[p]] = r;\r
+\r
+       if (LZW_right_child[LZW_parent[p]] == p)\r
+               LZW_right_child[LZW_parent[p]] = r;\r
+       else\r
+               LZW_left_child[LZW_parent[p]] = r;\r
+\r
+       LZW_parent[p] = LZW_NIL;  /* remove p */\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+\r
+static void DeleteLZWNode(unsigned long p)  /* deletes node p from tree */\r
+{\r
+       int q;\r
+\r
+       if (LZW_parent[p] == LZW_NIL)\r
+               return;                                          /* not in tree */\r
+\r
+       if (LZW_right_child[p] == LZW_NIL)\r
+               q = LZW_left_child[p];\r
+       else\r
+       if (LZW_left_child[p] == LZW_NIL)\r
+               q = LZW_right_child[p];\r
+       else\r
+       {\r
+               q = LZW_left_child[p];\r
+               if (LZW_right_child[q] != LZW_NIL)\r
+               {\r
+                       do {\r
+\r
+                               q = LZW_right_child[q];\r
+\r
+                       } while (LZW_right_child[q] != LZW_NIL);\r
+\r
+                       LZW_right_child[LZW_parent[q]] = LZW_left_child[q];\r
+                       LZW_parent[LZW_left_child[q]] = LZW_parent[q];\r
+                       LZW_left_child[q] = LZW_left_child[p];\r
+                       LZW_parent[LZW_left_child[p]] = q;\r
+               }\r
+\r
+               LZW_right_child[q] = LZW_right_child[p];\r
+               LZW_parent[LZW_right_child[p]] = q;\r
+        }\r
+\r
+        LZW_parent[q] = LZW_parent[p];\r
+        if (LZW_right_child[LZW_parent[p]] == p)\r
+               LZW_right_child[LZW_parent[p]] = q;\r
+        else\r
+               LZW_left_child[LZW_parent[p]] = q;\r
+\r
+        LZW_parent[p] = LZW_NIL;\r
+}\r
+\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+//\r
+// lzwCompress() - Compresses data from an input ptr to a dest ptr\r
+//\r
+// PARAMS:\r
+//              infile     - Pointer at the BEGINNING of the data to compress\r
+//              outfile    - Pointer to the destination (no header).\r
+//      DataLength - Number of bytes to compress.\r
+//     PtrTypes   - Type of pointers being used (SRC_FILE,DEST_FILE,SRC_MEM etc).\r
+//\r
+// RETURNS:\r
+//         Length of compressed data.\r
+//\r
+//     COMPTYPE : ct_LZW\r
+//\r
+// NOTES    : Does not write ANY header information!\r
+//\r
+unsigned long lzwCompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes)\r
+{\r
+       short i;\r
+       short c, len, r, s, last_LZW_match_len, code_buf_ptr;\r
+       unsigned char code_buf[17], mask;\r
+       unsigned long complen = 0;\r
+\r
+       unsigned CodeCount = 0;\r
+       unsigned long OrgDataLen = DataLength;\r
+\r
+       // initialize trees\r
+\r
+       InitLZWTree();\r
+\r
+       code_buf[0] = 0;\r
+\r
+       //\r
+       //  code_buf[1..16] saves eight units of code, and code_buf[0] works\r
+       //       as eight flags, "1" representing that the unit is an unencoded\r
+       //       letter (1 byte), "0" a position-and-length pair (2 bytes).  Thus,\r
+       //       eight units require at most 16 bytes of code.\r
+       //\r
+\r
+        code_buf_ptr = mask = 1;\r
+        s = 0;\r
+        r = LZW_N - LZW_F;\r
+\r
+       // Clear the buffer with any character that will appear often.\r
+       //\r
+\r
+        for (i = s; i < r; i++)\r
+                       LZW_ring_buffer[i] = ' ';\r
+\r
+       // Read LZW_F bytes into the last LZW_F bytes of the buffer\r
+       //\r
+\r
+        for (len = 0; (len < LZW_F) && DataLength; len++)\r
+        {\r
+               c = ReadPtr((long)&infile,PtrTypes);\r
+               DataLength--;\r
+\r
+               // text of size zero\r
+\r
+               LZW_ring_buffer[r + len] = c;\r
+        }\r
+\r
+        if (!(len && DataLength))\r
+               return(0);\r
+\r
+       //\r
+       // Insert the LZW_F strings, each of which begins with one or more\r
+       // 'space' characters.  Note the order in which these strings\r
+       // are inserted.  This way, degenerate trees will be less likely\r
+       // to occur.\r
+       //\r
+\r
+        for (i = 1; i <= LZW_F; i++)\r
+                       InsertLZWNode(r - i);\r
+\r
+       //\r
+       // Finally, insert the whole string just read.  The global\r
+       // variables LZW_match_len and LZW_match_pos are set. */\r
+       //\r
+\r
+        InsertLZWNode(r);\r
+\r
+        do {\r
+                       // LZW_match_len may be spuriously long near the end of text.\r
+                       //\r
+\r
+                       if (LZW_match_len > len)\r
+                               LZW_match_len = len;\r
+\r
+                       if (LZW_match_len <= LZW_THRESHOLD)\r
+                       {\r
+                                 // Not long enough match.  Send one byte.\r
+                                 //\r
+\r
+                                 LZW_match_len = 1;\r
+\r
+                                 // 'send one byte' flag\r
+                                 //\r
+\r
+                                 code_buf[0] |= mask;\r
+\r
+                                 // Send uncoded.\r
+                                 //\r
+\r
+                                 code_buf[code_buf_ptr++] = LZW_ring_buffer[r];\r
+                       }\r
+                       else\r
+                       {\r
+                               code_buf[code_buf_ptr++] = (unsigned char) LZW_match_pos;\r
+                               code_buf[code_buf_ptr++] = (unsigned char) (((LZW_match_pos >> 4) & 0xf0) | (LZW_match_len - (LZW_THRESHOLD + 1)));\r
+\r
+                               // Send position and length pair.\r
+                               // Note LZW_match_len > LZW_THRESHOLD.\r
+                       }\r
+\r
+                       if ((mask <<= 1) == 0)\r
+                       {\r
+                               // Shift mask left one bit.\r
+                               // Send at most 8 units of data\r
+\r
+                               for (i = 0; i < code_buf_ptr; i++)\r
+                                       WritePtr((long)&outfile,code_buf[i],PtrTypes);\r
+\r
+                               complen += code_buf_ptr;\r
+                               code_buf[0] = 0;\r
+                               code_buf_ptr = mask = 1;\r
+                       }\r
+\r
+                       last_LZW_match_len = LZW_match_len;\r
+\r
+                       for (i = 0; i < last_LZW_match_len && DataLength; i++)\r
+                       {\r
+                               c = ReadPtr((long)&infile,PtrTypes);\r
+                               DataLength--;\r
+\r
+                               DeleteLZWNode(s);                           // Delete old strings and\r
+                               LZW_ring_buffer[s] = c;                                          // read new bytes\r
+\r
+                               // If the position is near the end of buffer, extend the\r
+                               //      buffer to make string comparison easier.\r
+\r
+                               if (s < LZW_F - 1)\r
+                                       LZW_ring_buffer[s + LZW_N] = c;\r
+\r
+                               // Since this is a ring buffer, inc the position modulo LZW_N.\r
+                               //\r
+\r
+                               s = (s + 1) & (LZW_N - 1);\r
+                               r = (r + 1) & (LZW_N - 1);\r
+\r
+                               // Register the string in LZW_ring_buffer[r..r+LZW_F-1]\r
+                               //\r
+\r
+                               InsertLZWNode(r);\r
+                       }\r
+\r
+\r
+                       //\r
+                       // MANAGE DISPLAY VECTOR\r
+                       //\r
+\r
+                       if (LZW_CompressDisplayVector)\r
+                       {\r
+                               // Update display every 1k!\r
+                               //\r
+\r
+                               if ((CodeCount += i) > 1024)\r
+                               {\r
+                                       LZW_CompressDisplayVector(OrgDataLen,OrgDataLen-DataLength);\r
+                                       CodeCount = 0;\r
+                               }\r
+                       }\r
+\r
+\r
+                       //\r
+                       // Manage Compression tree..\r
+                       //\r
+\r
+                       while (i++ < last_LZW_match_len)\r
+                       {\r
+                                                                                                                 // After the end of text,\r
+                               DeleteLZWNode(s);               // no need to read, but\r
+\r
+                               s = (s + 1) & (LZW_N - 1);\r
+                               r = (r + 1) & (LZW_N - 1);\r
+\r
+                               if (--len)\r
+                                       InsertLZWNode(r);          // buffer may not be empty.\r
+                       }\r
+\r
+        } while (len > 0);  // until length of string to be processed is zero\r
+\r
+\r
+        if (code_buf_ptr > 1)\r
+        {\r
+               // Send remaining code.\r
+               //\r
+\r
+               for (i = 0; i < code_buf_ptr; i++)\r
+                       WritePtr((long)&outfile,code_buf[i],PtrTypes);\r
+\r
+               complen += code_buf_ptr;\r
+        }\r
+\r
+       if (LZW_CompressDisplayVector)\r
+       {\r
+               if ((CodeCount += i) > 1024)\r
+               {\r
+                       LZW_CompressDisplayVector(OrgDataLen,OrgDataLen-DataLength);\r
+               }\r
+       }\r
+\r
+        return(complen);\r
+}\r
+\r
+#endif\r
+\r
+\r
+\r
+\r
+//============================================================================\r
+//\r
+//                                                     SUPPORT ROUTINES FOR LZW DECOMPRESSION\r
+//\r
+//============================================================================\r
+\r
+#if INCLUDE_LZW_DECOMP\r
+\r
+//--------------------------------------------------------------------------\r
+//\r
+// lzwDecompress() - Compresses data from an input ptr to a dest ptr\r
+//\r
+// PARAMS:\r
+//              infile     - Pointer at the BEGINNING of the compressed data (no header!)\r
+//              outfile    - Pointer to the destination.\r
+//      DataLength - Number of bytes to decompress.\r
+//     PtrTypes   - Type of pointers being used (SRC_FILE,DEST_FILE,SRC_MEM etc).\r
+//\r
+// RETURNS:\r
+//         Length of compressed data.\r
+//\r
+//     COMPTYPE : ct_LZW\r
+//\r
+// NOTES    : Does not write ANY header information!\r
+//\r
+void lzwDecompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes)\r
+{\r
+       int  i, j, k, r, c;\r
+       unsigned int flags;\r
+       unsigned char Buffer[8];\r
+//     unsigned char LZW_ring_buffer[LZW_N + LZW_F - 1];\r
+\r
+       unsigned CodeCount = 0;\r
+       unsigned long OrgDataLen = DataLength;\r
+\r
+       for (i = 0; i < LZW_N - LZW_F; i++)\r
+               LZW_ring_buffer[i] = ' ';\r
+\r
+        r = LZW_N - LZW_F;\r
+        flags = 0;\r
+\r
+        for ( ; ; )\r
+        {\r
+                       if (((flags >>= 1) & 256) == 0)\r
+                       {\r
+                               c = ReadPtr((long)&infile,PtrTypes);\r
+\r
+                               flags = c | 0xff00;      // uses higher byte cleverly to count 8\r
+                       }\r
+\r
+                       if (flags & 1)\r
+                       {\r
+                               c = ReadPtr((long)&infile,PtrTypes);            // Could test for EOF iff FFILE type\r
+\r
+                               WritePtr((long)&outfile,c,PtrTypes);\r
+\r
+                               if (!--DataLength)\r
+                                       return;\r
+\r
+                               LZW_ring_buffer[r++] = c;\r
+                               r &= (LZW_N - 1);\r
+                       }\r
+                       else\r
+                       {\r
+                               i = ReadPtr((long)&infile,PtrTypes);\r
+\r
+                               j = ReadPtr((long)&infile,PtrTypes);\r
+\r
+                               i |= ((j & 0xf0) << 4);\r
+                               j = (j & 0x0f) + LZW_THRESHOLD;\r
+\r
+                               for (k = 0; k <= j; k++)\r
+                               {\r
+                                        c = LZW_ring_buffer[(i + k) & (LZW_N - 1)];\r
+\r
+                                        WritePtr((long)&outfile,c,PtrTypes);\r
+\r
+                                        if (!--DataLength)\r
+                                               return;\r
+\r
+                                        LZW_ring_buffer[r++] = c;\r
+                                        r &= (LZW_N - 1);\r
+                               }\r
+                       }\r
+\r
+\r
+\r
+                       //\r
+                       //      MANAGE DISPLAY VECTOR\r
+                       //\r
+\r
+                       if (LZW_DecompressDisplayVector)\r
+                       {\r
+                               //\r
+                               // Update DisplayVector every 1K\r
+                               //\r
+\r
+                               if ((CodeCount+=k) > 1024)\r
+                               {\r
+                                       LZW_DecompressDisplayVector(OrgDataLen,OrgDataLen-DataLength);\r
+                                       CodeCount = 0;\r
+                               }\r
+                       }\r
+\r
+        }\r
+}\r
+\r
+#endif\r
+\r
diff --git a/16/cawat/LZW.H b/16/cawat/LZW.H
new file mode 100644 (file)
index 0000000..6279e25
--- /dev/null
@@ -0,0 +1,40 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//--------------------------------------------------------------------------\r
+//\r
+//                                     EXTERN DEFINITIONS FOR DISPLAY VEC ROUTINES\r
+//\r
+//--------------------------------------------------------------------------\r
+\r
+\r
+extern void (*LZW_CompressDisplayVector)();\r
+extern void (*LZW_DecompressDisplayVector)();\r
+\r
+\r
+//---------------------------------------------------------------------------\r
+//\r
+//                                                             FUNCTION PROTOTYPEING\r
+//\r
+//---------------------------------------------------------------------------\r
+\r
+\r
+\r
+unsigned long lzwCompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes);\r
+void lzwDecompress(void far *infile, void far *outfile,unsigned long DataLength,unsigned PtrTypes);\r
+\r
diff --git a/16/cawat/MAPSARM.H b/16/cawat/MAPSARM.H
new file mode 100644 (file)
index 0000000..b1de541
--- /dev/null
@@ -0,0 +1,53 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+///////////////////////////////////////\r
+//\r
+// TED5 Map Header for ARM\r
+//\r
+///////////////////////////////////////\r
+\r
+//\r
+// Map Names\r
+//\r
+typedef enum {\r
+               TOWN_MORBIDITY_MAP,      // 0\r
+               DARK_FOREST_MAP,         // 1\r
+               GARDEN_OF_SOULS_MAP,     // 2\r
+               LOST_CITY_DAMND_MAP,     // 3\r
+               TEMPLE_OF_VIPER_MAP,     // 4\r
+               TORTURE_CHAMBER_MAP,     // 5\r
+               DEMONS_HOLD_MAP,         // 6\r
+               COLONY_FIRE_ANT_MAP,     // 7\r
+               HALL_WRETCH_POX_MAP,     // 8\r
+               LAIR_OF_SUCUBUS_MAP,     // 9\r
+               BLOOD_CHAMB_EYE_MAP,     // 10\r
+               FLAMING_INFERNO_MAP,     // 11\r
+               SUBTERR_RIVER_MAP,       // 12\r
+               CRYSTAL_MAZE_MG_MAP,     // 13\r
+               RAMPARTS_OF_NEM_MAP,     // 14\r
+               FORTRESS_OF_NEM_MAP,     // 15\r
+               PASSAGE_TO_SURF_MAP,     // 16\r
+               LASTMAP\r
+            } mapnames;\r
+\r
+//\r
+// TILEINFO offsets\r
+//\r
+#define ANIM           402\r
+#define FLAGS          (ANIM+NUMTILE16)\r
diff --git a/16/cawat/README.md b/16/cawat/README.md
new file mode 100644 (file)
index 0000000..680f55a
--- /dev/null
@@ -0,0 +1,16 @@
+The Catacomb Armageddon
+=======================
+
+This repository contains the source code for The Catacomb Armageddon. The
+source code is designed for Borland C++ 2.0, but compiled fine with Borland C++
+3.1 at the time of this release.
+
+It is released under the GNU GPLv2. Please see COPYING for license details.
+
+This release does not affect the licensing for the game data files. You will
+need to legally acquire the game data in order to use the exe built from this
+source code.
+
+To run the executable, the following command must be used or the check must be
+disabled in C5_MAIN.C:
+    armgame.exe ^(a@&r`
diff --git a/16/cawat/SL_FILE.H b/16/cawat/SL_FILE.H
new file mode 100644 (file)
index 0000000..24849b1
--- /dev/null
@@ -0,0 +1,109 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#ifndef _SL_FILE_H\r
+#define _SL_FILE_H\r
+\r
+\r
+//==========================================================================\r
+//\r
+//                                                                             DEFINES\r
+//\r
+//==========================================================================\r
+\r
+#ifndef MakeID\r
+#define MakeID(a,b,c,d)                        (((long)(d)<<24L)|((long)(c)<<16L)|((long)(b)<<8L)|(long)(a))\r
+#endif\r
+\r
+\r
+#define ID_SLIB                                        MakeID('S','L','I','B')\r
+#define SLIB                                           ("SLIB")\r
+#define SOFTLIB_VER                            2\r
+#define ID_CHUNK                                       MakeID('C','U','N','K')\r
+\r
+\r
+\r
+//==========================================================================\r
+//\r
+//                                                                             TYPES\r
+//\r
+//==========================================================================\r
+\r
+\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+//                                                     SOFTLIB File Entry Types\r
+//--------------------------------------------------------------------------\r
+typedef enum LibFileTypes\r
+{\r
+       lib_DATA =0,                                    // Just streight raw data\r
+//     lib_AUDIO,                                              // Multi chunk audio file\r
+\r
+} LibFileTypes;\r
+\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+//                                                     SOFTLIB Library File header..\r
+//\r
+//                                                * This header will NEVER change! *\r
+//--------------------------------------------------------------------------\r
+\r
+typedef struct SoftLibHdr\r
+{\r
+       unsigned Version;                                                                       // Library Version Num\r
+       unsigned FileCount;\r
+} SoftlibHdr;\r
+\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+//                                                     SOFTLIB Directory Entry Hdr\r
+//\r
+// This can change according to Version of SoftLib (Make sure we are\r
+// always downward compatable!\r
+//--------------------------------------------------------------------------\r
+\r
+#define SL_FILENAMESIZE                16\r
+\r
+typedef struct FileEntryHdr\r
+{\r
+       char FileName[SL_FILENAMESIZE];                 // NOTE : May not be null terminated!\r
+       unsigned long Offset;\r
+       unsigned long ChunkLen;\r
+       unsigned long OrginalLength;\r
+       short Compression;                                                      // ct_TYPES\r
+} FileEntryHdr;\r
+\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+//                                                        SOFTLIB Entry Chunk Header\r
+//--------------------------------------------------------------------------\r
+\r
+typedef struct ChunkHeader\r
+{\r
+       unsigned long HeaderID;\r
+       unsigned long OrginalLength;\r
+       short Compression;                                                              // ct_TYPES\r
+} ChunkHeader;\r
+\r
+\r
+\r
+#endif
\ No newline at end of file
diff --git a/16/cawat/SOFT.C b/16/cawat/SOFT.C
new file mode 100644 (file)
index 0000000..f86481e
--- /dev/null
@@ -0,0 +1,479 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <ctype.h>\r
+#include <alloc.h>\r
+#include <fcntl.h>\r
+#include <dos.h>\r
+#include <io.h>\r
+\r
+#include "def.h"\r
+#include "gelib.h"\r
+#include "soft.h"\r
+#include "lzw.h"\r
+#include "lzhuff.h"\r
+#include "jam_io.h"\r
+\r
+\r
+\r
+\r
+\r
+\r
+//===========================================================================\r
+//\r
+//                                                                             SWITCHES\r
+//\r
+//===========================================================================\r
+\r
+#define LZH_SUPPORT            1\r
+#define LZW_SUPPORT            0\r
+\r
+\r
+\r
+\r
+//=========================================================================\r
+//\r
+//\r
+//                                                             GENERAL LOAD ROUTINES\r
+//\r
+//\r
+//=========================================================================\r
+\r
+\r
+\r
+//--------------------------------------------------------------------------\r
+// BLoad()                     -- THIS HAS NOT BEEN FULLY TESTED!\r
+//\r
+// NOTICE : This version of BLOAD is compatable with JAMPak V3.0 and the\r
+//                             new fileformat...\r
+//--------------------------------------------------------------------------\r
+unsigned long BLoad(char *SourceFile, memptr *DstPtr)\r
+{\r
+       int handle;\r
+\r
+       memptr SrcPtr;\r
+       unsigned long i, j, k, r, c;\r
+       word flags;\r
+       byte Buffer[8];\r
+       unsigned long SrcLen,DstLen;\r
+       struct CMP1Header CompHeader;\r
+       boolean Compressed = false;\r
+\r
+\r
+       memset((void *)&CompHeader,0,sizeof(struct CMP1Header));\r
+\r
+       //\r
+       // Open file to load....\r
+       //\r
+\r
+       if ((handle = open(SourceFile, O_RDONLY|O_BINARY)) == -1)\r
+               return(0);\r
+\r
+       //\r
+       // Look for JAMPAK headers\r
+       //\r
+\r
+       read(handle,Buffer,4);\r
+\r
+       if (!strncmp(Buffer,COMP,4))\r
+       {\r
+               //\r
+               // Compressed under OLD file format\r
+               //\r
+\r
+               Compressed = true;\r
+               SrcLen = Verify(SourceFile);\r
+\r
+               read(handle,(void *)&CompHeader.OrginalLen,4);\r
+               CompHeader.CompType = ct_LZW;\r
+               MM_GetPtr(DstPtr,CompHeader.OrginalLen);\r
+               if (!*DstPtr)\r
+                       return(0);\r
+       }\r
+       else\r
+       if (!strncmp(Buffer,CMP1,4))\r
+       {\r
+               //\r
+               // Compressed under new file format...\r
+               //\r
+\r
+               Compressed = true;\r
+               SrcLen = Verify(SourceFile);\r
+\r
+               read(handle,(void *)&CompHeader,sizeof(struct CMP1Header));\r
+               MM_GetPtr(DstPtr,CompHeader.OrginalLen);\r
+               if (!*DstPtr)\r
+                       return(0);\r
+       }\r
+       else\r
+               DstLen = Verify(SourceFile);\r
+\r
+\r
+       //\r
+       // Load the file in memory...\r
+       //\r
+\r
+       if (Compressed)\r
+       {\r
+               DstLen = CompHeader.OrginalLen;\r
+\r
+               if ((MM_TotalFree() < SrcLen) && (CompHeader.CompType))\r
+               {\r
+                       if (!InitBufferedIO(handle,&lzwBIO))\r
+                               TrashProg("No memory for buffered I/O.");\r
+\r
+                       switch (CompHeader.CompType)\r
+                       {\r
+                               #if LZW_SUPPORT\r
+                               case ct_LZW:\r
+                                       lzwDecompress(&lzwBIO,MK_FP(*DstPtr,0),CompHeader.OrginalLen,(SRC_BFILE|DEST_MEM));\r
+                               break;\r
+                               #endif\r
+\r
+                               #if LZH_SUPPORT\r
+                               case ct_LZH:\r
+                                       lzhDecompress(&lzwBIO,MK_FP(*DstPtr,0),CompHeader.OrginalLen,CompHeader.CompressLen,(SRC_BFILE|DEST_MEM));\r
+                               break;\r
+                               #endif\r
+\r
+                               default:\r
+                                       TrashProg("BLoad() - Unrecognized/Supported compression");\r
+                               break;\r
+                       }\r
+\r
+                       FreeBufferedIO(&lzwBIO);\r
+               }\r
+               else\r
+               {\r
+                       CA_LoadFile(SourceFile,&SrcPtr);\r
+                       switch (CompHeader.CompType)\r
+                       {\r
+                               #if LZW_SUPPORT\r
+                               case ct_LZW:\r
+                                       lzwDecompress(MK_FP(SrcPtr,8),MK_FP(*DstPtr,0),CompHeader.OrginalLen,(SRC_MEM|DEST_MEM));\r
+                               break;\r
+                               #endif\r
+\r
+                               #if LZH_SUPPORT\r
+                               case ct_LZH:\r
+                                       lzhDecompress(MK_FP(SrcPtr,8),MK_FP(*DstPtr,0),CompHeader.OrginalLen,CompHeader.CompressLen,(SRC_MEM|DEST_MEM));\r
+                               break;\r
+                               #endif\r
+\r
+                               default:\r
+                                       TrashProg("BLoad() - Unrecognized/Supported compression");\r
+                               break;\r
+                       }\r
+                       MM_FreePtr(&SrcPtr);\r
+               }\r
+       }\r
+       else\r
+               CA_LoadFile(SourceFile,DstPtr);\r
+\r
+       close(handle);\r
+       return(DstLen);\r
+}\r
+\r
+\r
+\r
+\r
+////////////////////////////////////////////////////////////////////////////\r
+//\r
+// LoadLIBShape()\r
+//\r
+int LoadLIBShape(char *SLIB_Filename, char *Filename,struct Shape *SHP)\r
+{\r
+       #define CHUNK(Name)     (*ptr == *Name) &&                      \\r
+                                                               (*(ptr+1) == *(Name+1)) &&      \\r
+                                                               (*(ptr+2) == *(Name+2)) &&      \\r
+                                                               (*(ptr+3) == *(Name+3))\r
+\r
+\r
+       int RT_CODE;\r
+       FILE *fp;\r
+       char CHUNK[5];\r
+       char far *ptr;\r
+       memptr IFFfile = NULL;\r
+       unsigned long FileLen, size, ChunkLen;\r
+       int loop;\r
+\r
+\r
+       RT_CODE = 1;\r
+\r
+       // Decompress to ram and return ptr to data and return len of data in\r
+       //      passed variable...\r
+\r
+       if (!LoadLIBFile(SLIB_Filename,Filename,&IFFfile))\r
+               TrashProg("Error Loading Compressed lib shape!");\r
+\r
+       // Evaluate the file\r
+       //\r
+       ptr = MK_FP(IFFfile,0);\r
+       if (!CHUNK("FORM"))\r
+               goto EXIT_FUNC;\r
+       ptr += 4;\r
+\r
+       FileLen = *(long far *)ptr;\r
+       SwapLong((long far *)&FileLen);\r
+       ptr += 4;\r
+\r
+       if (!CHUNK("ILBM"))\r
+               goto EXIT_FUNC;\r
+       ptr += 4;\r
+\r
+       FileLen += 4;\r
+       while (FileLen)\r
+       {\r
+               ChunkLen = *(long far *)(ptr+4);\r
+               SwapLong((long far *)&ChunkLen);\r
+               ChunkLen = (ChunkLen+1) & 0xFFFFFFFE;\r
+\r
+               if (CHUNK("BMHD"))\r
+               {\r
+                       ptr += 8;\r
+                       SHP->bmHdr.w = ((struct BitMapHeader far *)ptr)->w;\r
+                       SHP->bmHdr.h = ((struct BitMapHeader far *)ptr)->h;\r
+                       SHP->bmHdr.x = ((struct BitMapHeader far *)ptr)->x;\r
+                       SHP->bmHdr.y = ((struct BitMapHeader far *)ptr)->y;\r
+                       SHP->bmHdr.d = ((struct BitMapHeader far *)ptr)->d;\r
+                       SHP->bmHdr.trans = ((struct BitMapHeader far *)ptr)->trans;\r
+                       SHP->bmHdr.comp = ((struct BitMapHeader far *)ptr)->comp;\r
+                       SHP->bmHdr.pad = ((struct BitMapHeader far *)ptr)->pad;\r
+                       SwapWord(&SHP->bmHdr.w);\r
+                       SwapWord(&SHP->bmHdr.h);\r
+                       SwapWord(&SHP->bmHdr.x);\r
+                       SwapWord(&SHP->bmHdr.y);\r
+                       ptr += ChunkLen;\r
+               }\r
+               else\r
+               if (CHUNK("BODY"))\r
+               {\r
+                       ptr += 4;\r
+                       size = *((long far *)ptr);\r
+                       ptr += 4;\r
+                       SwapLong((long far *)&size);\r
+                       SHP->BPR = (SHP->bmHdr.w+7) >> 3;\r
+                       MM_GetPtr(&SHP->Data,size);\r
+                       if (!SHP->Data)\r
+                               goto EXIT_FUNC;\r
+                       movedata(FP_SEG(ptr),FP_OFF(ptr),FP_SEG(SHP->Data),0,size);\r
+                       ptr += ChunkLen;\r
+\r
+                       break;\r
+               }\r
+               else\r
+                       ptr += ChunkLen+8;\r
+\r
+               FileLen -= ChunkLen+8;\r
+       }\r
+\r
+       RT_CODE = 0;\r
+\r
+EXIT_FUNC:;\r
+       if (IFFfile)\r
+       {\r
+//             segptr = (memptr)FP_SEG(IFFfile);\r
+               MM_FreePtr(&IFFfile);\r
+       }\r
+\r
+       return (RT_CODE);\r
+}\r
+\r
+\r
+\r
+\r
+\r
+//----------------------------------------------------------------------------\r
+// LoadLIBFile() -- Copies a file from an existing archive to dos.\r
+//\r
+// PARAMETERS :\r
+//\r
+//                     LibName         - Name of lib file created with SoftLib V1.0\r
+//\r
+//                     FileName - Name of file to load from lib file.\r
+//\r
+//                     MemPtr   - (IF !NULL) - Pointer to memory to load into ..\r
+//                                               (IF NULL)  - Routine allocates necessary memory and\r
+//                                                                                     returns a MEM(SEG) pointer to memory allocated.\r
+//\r
+// RETURN :\r
+//\r
+//             (IF !NULL) - A pointer to the loaded data.\r
+//                     (IF NULL)  - Error!\r
+//\r
+//----------------------------------------------------------------------------\r
+memptr LoadLIBFile(char *LibName,char *FileName,memptr *MemPtr)\r
+{\r
+       int handle;\r
+       unsigned long header;\r
+       struct ChunkHeader Header;\r
+       unsigned long ChunkLen;\r
+       short x;\r
+       struct FileEntryHdr FileEntry;                          // Storage for file once found\r
+       struct FileEntryHdr FileEntryHeader;            // Header used durring searching\r
+       struct SoftLibHdr LibraryHeader;                                // Library header - Version Checking\r
+       boolean FileFound = false;\r
+       unsigned long id_slib = ID_SLIB;\r
+       unsigned long id_chunk = ID_CHUNK;\r
+\r
+\r
+       //\r
+       // OPEN SOFTLIB FILE\r
+       //\r
+\r
+       if ((handle = open(LibName,O_RDONLY|O_BINARY, S_IREAD)) == -1)\r
+               return(NULL);\r
+\r
+\r
+       //\r
+       //      VERIFY it is a SOFTLIB (SLIB) file\r
+       //\r
+\r
+       if (read(handle,&header,4) == -1)\r
+       {\r
+               close(handle);\r
+               return(NULL);\r
+       }\r
+\r
+       if (header != id_slib)\r
+       {\r
+               close(handle);\r
+               return(NULL);\r
+       }\r
+\r
+\r
+       //\r
+       // CHECK LIBRARY HEADER VERSION NUMBER\r
+       //\r
+\r
+       if (read(handle, &LibraryHeader,sizeof(struct SoftLibHdr)) == -1)\r
+               TrashProg("read error in LoadSLIBFile()\n%c",7);\r
+\r
+       if (LibraryHeader.Version > SOFTLIB_VER)\r
+               TrashProg("Unsupported file ver %d",LibraryHeader.Version);\r
+\r
+\r
+       //\r
+       // MANAGE FILE ENTRY HEADERS...\r
+       //\r
+\r
+       for (x = 1;x<=LibraryHeader.FileCount;x++)\r
+       {\r
+               if (read(handle, &FileEntryHeader,sizeof(struct FileEntryHdr)) == -1)\r
+               {\r
+                       close(handle);\r
+                       return(NULL);\r
+               }\r
+\r
+               if (!stricmp(FileEntryHeader.FileName,FileName))\r
+               {\r
+                       FileEntry = FileEntryHeader;\r
+                       FileFound = true;\r
+               }\r
+       }\r
+\r
+       //\r
+       // IF FILE HAS BEEN FOUND THEN SEEK TO POSITION AND EXTRACT\r
+       //      ELSE RETURN WITH ERROR CODE...\r
+       //\r
+\r
+       if (FileFound)\r
+       {\r
+               if (lseek(handle,FileEntry.Offset,SEEK_CUR) == -1)\r
+               {\r
+                       close(handle);\r
+                       return(NULL);\r
+               }\r
+\r
+               //\r
+               // READ CHUNK HEADER - Verify we are at the beginning of a chunk..\r
+               //\r
+\r
+               if (read(handle,(char *)&Header,sizeof(struct ChunkHeader)) == -1)\r
+                       TrashProg("LIB File - Unable to read Header!");\r
+\r
+               if (Header.HeaderID != id_chunk)\r
+                       TrashProg("LIB File - BAD HeaderID!");\r
+\r
+               //\r
+               // Allocate memory if Necessary...\r
+               //\r
+\r
+\r
+               if (!*MemPtr)\r
+                       MM_GetPtr(MemPtr,FileEntry.OrginalLength);\r
+\r
+               //\r
+               //      Calculate the length of the data (without the chunk header).\r
+               //\r
+\r
+               ChunkLen = FileEntry.ChunkLen - sizeof(struct ChunkHeader);\r
+\r
+\r
+               //\r
+               // Extract Data from file\r
+               //\r
+\r
+               switch (Header.Compression)\r
+               {\r
+\r
+                       #if LZW_SUPPORT\r
+                       case ct_LZW:\r
+                               if (!InitBufferedIO(handle,&lzwBIO))\r
+                                       TrashProg("No memory for buffered I/O.");\r
+                               lzwDecompress(&lzwBIO,MK_FP(*MemPtr,0),FileEntry.OrginalLength,(SRC_BFILE|DEST_MEM));\r
+                               FreeBufferedIO(&lzwBIO);\r
+                               break;\r
+                       #endif\r
+\r
+                       #if LZH_SUPPORT\r
+                       case ct_LZH:\r
+                               if (!InitBufferedIO(handle,&lzwBIO))\r
+                                       TrashProg("No memory for buffered I/O.");\r
+                               lzhDecompress(&lzwBIO, MK_FP(*MemPtr,0), FileEntry.OrginalLength, ChunkLen, (SRC_BFILE|DEST_MEM));\r
+                               FreeBufferedIO(&lzwBIO);\r
+                               break;\r
+                       #endif\r
+\r
+                       case ct_NONE:\r
+                               if (!CA_FarRead(handle,MK_FP(*MemPtr,0),ChunkLen))\r
+                               {\r
+//                                     close(handle);\r
+                                       *MemPtr = NULL;\r
+                               }\r
+                               break;\r
+\r
+                       default:\r
+                               close(handle);\r
+                               TrashProg("Unknown Chunk.Compression Type!");\r
+                               break;\r
+               }\r
+       }\r
+       else\r
+               *MemPtr = NULL;\r
+\r
+       close(handle);\r
+       return(*MemPtr);\r
+}\r
+\r
+\r
+\r
+\r
diff --git a/16/cawat/SOFT.H b/16/cawat/SOFT.H
new file mode 100644 (file)
index 0000000..2226de6
--- /dev/null
@@ -0,0 +1,25 @@
+/* Catacomb Armageddon Source Code\r
+ * Copyright (C) 1993-2014 Flat Rock Software\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+ */\r
+\r
+//memptr InitBufferedIO(int handle, BufferedIO *bio);\r
+//void FreeBufferedIO(BufferedIO *bio);\r
+//byte bio_readch(BufferedIO *bio);\r
+\r
+unsigned long BLoad(char *SourceFile, memptr *DstPtr);\r
+memptr LoadLIBFile(char *LibName,char *FileName,memptr *MemPtr);\r
+int LoadLIBShape(char *SLIB_Filename, char *Filename,struct Shape *SHP);\r
diff --git a/16/cawat/TC0000.SWP b/16/cawat/TC0000.SWP
new file mode 100644 (file)
index 0000000..d2453e0
Binary files /dev/null and b/16/cawat/TC0000.SWP differ
diff --git a/16/cawat/TD.TR b/16/cawat/TD.TR
new file mode 100644 (file)
index 0000000..2e7787f
Binary files /dev/null and b/16/cawat/TD.TR differ
diff --git a/16/cawat/TDCONFIG.TD b/16/cawat/TDCONFIG.TD
new file mode 100644 (file)
index 0000000..dfbed30
Binary files /dev/null and b/16/cawat/TDCONFIG.TD differ
diff --git a/16/cawat/cawat.bfproject b/16/cawat/cawat.bfproject
new file mode 100644 (file)
index 0000000..253f8c7
--- /dev/null
@@ -0,0 +1,103 @@
+c2e.convert_special: 0
+e2c.convert_num: 0
+openfiles: /dos/z/cawat/ID_HEADS.H:1260:233:0:
+openfiles: /dos/z/cawat/16_mm.c:3604:3205:0:
+openfiles: /dos/z/cawat/16_mm.h:1364:1519:0:
+openfiles: /dos/z/cawat/ID_CA.C:1203:822:0:
+openfiles: /dos/z/cawat/ID_CA.H:857:343:0:
+openfiles: /dos/z/cawat/16_in.c:1341:982:0:
+openfiles: /dos/z/cawat/16_in.h:4659:4181:1:
+openfiles: /dos/z/cawat/lib_head.h:1091:173:0:
+openfiles: /dos/z/cawat/types.h:240:0:0:
+openfiles: /dos/z/16/src/lib/ems.c:3881:957:0:
+openfiles: /dos/z/16/src/lib/ems.h:944:379:0:
+openfiles: /dos/z/keen-src/id_mm.c:3423:3343:0:
+snr_recursion_level: 0
+convertcolumn_horizontally: 0
+adv_open_matchname: 0
+show_mbhl: 1
+view_line_numbers: 1
+fb_show_backup_f: 0
+htmlbar_thumbnailwidth: 300
+view_left_panel: 0
+default_mime_type: text/plain
+e2c.convert_xml: 1
+c2e.convert_iso: 0
+opendir: file:///dos/z
+wrap_text_default: 0
+bookmarks_filename_mode: 1
+ssearch_text: _seg
+snr_casesens: 0
+view_blocks: 1
+name: cawatcom
+replacelist: /*_1seg*/
+replacelist:  /*_1seg*/
+replacelist: \t\t
+fb_show_hidden_f: 0
+editor_tab_width: 4
+show_visible_spacing: 0
+view_statusbar: 1
+display_right_margin: 0
+c2e.IE_apos_workaround: 0
+outputb_scroll_mode: 0
+leftpanel_active_tab: 0
+enable_syntax_scan: 1
+ssearch_regex: 0
+e2c.convert_iso: 0
+ssearch_casesens: 0
+charmap_block: 1
+recent_files: file:///dos/z/16/src/lib/ems.c
+recent_files: file:///dos/z/16/src/lib/ems.h
+recent_files: file:///dos/z/keen-src/id_mm.c
+recent_files: file:///dos/z/bakapee.asm
+recent_files: file:///dos/z/cawat/types.h
+recent_files: file:///dos/z/cawat/ID_CA.C
+recent_files: file:///dos/z/cawat/lib_head.h
+snr_replacetype: 0
+savedir: file:///dos/z/cawat
+spell_check_default: 1
+spell_insert_entities: 0
+last_filefilter: 
+htmlbar_notebooktab: 0
+view_blockstack: 1
+snr_escape_chars: 1
+htmlbar_view: 1
+spell_lang: en
+ssearch_dotmatchall: 0
+searchlist: _seg
+searchlist: _1seg
+searchlist: p_1seg
+searchlist: p1seg
+searchlist: pseg
+searchlist: peg
+searchlist: pg
+searchlist: _1
+searchlist:  _seg
+searchlist: asm\t
+searchlist: lo
+searchlist: offset
+searchlist: emmname
+searchlist: seg
+searchlist: _seg
+autocomplete: 1
+outputb_show_all_output: 0
+bookmarks_show_mode: 0
+snippets_show_as_menu: 1
+adv_open_recursive: 0
+encoding: SHIFT_JIS
+e2c.convert_special: 0
+autoindent: 1
+fb_viewmode: 0
+recent_dirs: file:///dos/z/cawat
+fb_focus_follow: 1
+ssearch_unescape: 0
+c2e.convert_symbol: 0
+snr_dotmatchall: 0
+c2e.convert_xml: 1
+editor_indent_wspaces: 0
+view_cline: 0
+snr_type: 0
+snr_scope: 0
+bmarksearchmode: 0
+view_main_toolbar: 1
+e2c.convert_symbol: 0
diff --git a/16/cawat/lib_head.h b/16/cawat/lib_head.h
new file mode 100644 (file)
index 0000000..90f69d1
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2012-2015 sparky4 & pngwen & andrius4669
+ *
+ * This file is part of Project 16.
+ *
+ * Project 16 is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Project 16 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>, or
+ * write to the Free Software Foundation, Inc., 51 Franklin Street,
+ * Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+/*\r
+ * Just some handy typedefs that make it easier to think about the low\r
+ * level code\r
+ */
+
+#ifndef _LIB_HEAD_H_
+#define _LIB_HEAD_H_
+
+#include <malloc.h>
+#include <dos.h>
+//#include ""
+//#include ""
+//#include "ID_CA.H"
+//#include "AUDIOARM.H"
+#include "types.h"\r
+
+#endif/*_LIB_HEAD_H_*/
diff --git a/16/cawat/port.h b/16/cawat/port.h
new file mode 100644 (file)
index 0000000..1ada6a5
--- /dev/null
@@ -0,0 +1,135 @@
+/*======================================================================
+   portable.h v1.00 Written by Scott Robert Ladd.
+
+   _MSC_VER    Microsoft C 6.0 and later
+   _QC         Microsoft Quick C 2.51 and later
+   __TURBOC__  Borland Turbo C and Turbo C++
+   __ZTC_      Zortech C and C++
+   __WATCOMC__ WATCOM C
+=========================================================================*/
+
+/* prevent multiple inclusions of this header file*/
+#if !defined(PORTABLE_H)
+#define PORTABLE_H
+
+/*-----------------------------------------------------------------------
+   Pointer-related macros
+
+   MK_FP   creates a far pointer from segment and offset values
+------------------------------------------------------------------------*/
+
+#if !defined(MK_FP)
+  #define MK_FP(seg,off) ((void far *)(((long)(seg) << 16)|(unsigned)(off)))
+#endif
+
+/*-----------------------------------------------------------------------
+   Directory search macros and data structures
+
+   DOSFileData     MS-DOS file data structure
+   FIND_FIRST      MS-DOS function 0x4E -- find first file matching spec
+   FIND_NEXT       MS-DOS function 0x4F -- find subsequent files
+-----------------------------------------------------------------------*/
+
+/* make sure the structure is packed on byte boundary */
+#if defined(_MSC_VER)  || defined(_QC) || defined(__WATCOMC__)
+    #pragma pack(1)
+#elif defined(__ZTC__)
+    #pragma align 1
+#elif defined(__TURBOC__)
+    #pragma option -a-
+#endif
+
+/* use this structure in place of compiler-defined file structure */
+typedef struct
+    {
+    char     reserved[21];
+    char     attrib;
+    unsigned time;
+    unsigned date;
+    long     size;
+    char     name[13];
+    }
+    DOSFileData;
+
+/* set structure alignment to default */
+#if defined (_MSC_VER)  || defined(_QC) || defined(__WATCOMC__)
+    #pragma pack()
+#elif defined(__ZTC__)
+    #pragma align
+#elif defined(__TURBOC__)
+    #pragma option -a.
+#endif
+
+/* include proper header files and create macros */
+#if defined(_MSC_VER) || defined(_QC) || defined(__WATCOMC__)
+    #include "direct.h"
+    #define FIND_FIRST(spec, attr, buf) \
+_dos_findfirst(spec, attr, (struct find_t *)buf) \
+    #define FIND_NEXT(buf) _dos_findnex((struct find_t *)buf)
+#elif defined(__TURBOC__)
+    #include "dir.h"
+    #define FIND_FIRST(spec, attr, buf)\
+findfirst(spec, (struct ffblk *)buf, attr)
+    #define FIND_NEXT(buf) findnext((struct ffblk *)buf)
+#elif defined(__ZTC__)
+    #include "dos.h"
+    #define FIND_FIRST(spec, attr, buf) \
+dos_findfirst(spec, attr, (struct DOS_FIND *)buf)
+    #define FIND_NEXT(buf) dos_findnext((struct DOS_FIND *)buf)
+#endif
+
+/*-----------------------------------------------------------------------
+    I/O Port Macros
+
+    IN_PORT     read  byte from I/O port
+    IN_PORTW    read  word from I/O port
+    OUT_PORT    write byte  to  I/O port
+    OUT_PORTW   write word  to  I/O port
+-----------------------------------------------------------------------*/
+
+#if defined(__TURBOC__)
+    #include "dos.h"
+    #define IN_PORT(port)        inportb(port)
+    #define IN_PORTW(port)       inport(port)
+    #define OUT_PORT(port,val)   noutportb(port,val)
+    #define OUT_PORTW(port,val)  outport(port,val)
+#else
+    #include "conio.h"
+
+    #define IN_PORT(port)        inp(port)
+    #define IN_PORTW(port)       inpw(port)
+    #define OUT_PORT(port,val)   outp(port,val)
+    #define OUT_PORTW(port,val)  outpw(port,val)
+#endif
+
+/*-----------------------------------------------------------------------
+    Borland psuedo register macros
+
+    These macros replace references to Borland's psuedo register
+    variables and geninterrupt() function with traditional struct
+    REGS/int86 references.
+-----------------------------------------------------------------------*/
+
+#if !defined(__TURBOC__)
+    #include "dos.h"
+
+    struct REGS CPURegs;
+
+    #define _AX CPURegs.x.ax
+    #define _BX CPURegs.x.bx
+    #define _CX CPURegs.x.cx
+    #define _DX CPURegs.x.dx
+
+    #define _AH CPURegs.h.ah
+    #define _AL CPURegs.h.al
+    #define _BH CPURegs.h.bh
+    #define _BL CPURegs.h.bl
+    #define _CH CPURegs.h.ch
+    #define _CL CPURegs.h.cl
+    #define _DH CPURegs.h.dh
+    #define _DL CPURegs.h.dl
+
+    #define geninterrupt(n) int86(n,&CPURegs,&CPURegs);
+#endif
+
+#endif
\ No newline at end of file
diff --git a/16/cawat/types.h b/16/cawat/types.h
new file mode 100644 (file)
index 0000000..51b7d91
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef _TYPE_H_
+#define _TYPE_H_
+
+typedef        enum    {false,true}    boolean;
+#define        nil     ((void *)0)
+
+typedef unsigned char byte;
+typedef unsigned short word;
+typedef unsigned long  dword;
+typedef signed char sbyte;
+typedef signed short sword;
+typedef signed long sdword;
+
+#endif