--- /dev/null
+/* Catacomb Apocalypse 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 "ID_HEADS.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
--- /dev/null
+/* Catacomb Apocalypse 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 "ID_HEADS.H"\r
+#pragma hdrstop\r
+\r
+#pragma warn -pro\r
+#pragma warn -use\r
+\r
+\r
+#if 0 // 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 "\n" \\r
+ "You need more memory to run CATACOMB APOCALYPSE. Read the INSTRUCTION\n" \\r
+ "section of the START program for tips on getting more memory.\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!")\\r
+// ;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
+\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
+char emmname[9] = "EMMXXXX0";\r
+\r
+boolean MML_CheckForEMS (void)\r
+{\r
+asm mov dx,OFFSET emmname[0]\r
+asm mov ax,0x3d00\r
+asm int 0x21 // try to open EMMXXXX0 device\r
+asm jc error\r
+\r
+asm mov bx,ax\r
+asm mov ax,0x4400\r
+\r
+asm int 0x21 // get device info\r
+asm jc error\r
+\r
+asm and dx,0x80\r
+asm jz error\r
+\r
+asm mov ax,0x4407\r
+\r
+asm int 0x21 // get status\r
+asm jc error\r
+asm or al,al\r
+asm jz error\r
+\r
+asm mov ah,0x3e\r
+asm int 0x21 // close handle\r
+asm jc error\r
+\r
+//\r
+// EMS is good\r
+//\r
+ return true;\r
+\r
+error:\r
+//\r
+// EMS is bad\r
+//\r
+ return false;\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
+asm {\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
+ asm {\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
+asm {\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
+asm {\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
+asm {\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
+asm mov ah,XMS_FREEUMB\r
+asm mov dx,[base]\r
+asm 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