// ID_CA.C // this has been customized for WOLF /* ============================================================================= Id Software Caching Manager --------------------------- Must be started BEFORE the memory manager, because it needs to get the headers loaded into the data segment ============================================================================= */ #include "ID_HEADS.H" #pragma hdrstop #pragma warn -pro #pragma warn -use #define THREEBYTEGRSTARTS /* ============================================================================= LOCAL CONSTANTS ============================================================================= */ typedef struct { unsigned bit0,bit1; // 0-255 is a character, > is a pointer to a node } huffnode; typedef struct { unsigned RLEWtag; long headeroffsets[100]; byte tileinfo[]; } mapfiletype; /* ============================================================================= GLOBAL VARIABLES ============================================================================= */ byte _seg *tinf; int mapon; unsigned _seg *mapsegs[MAPPLANES]; maptype _seg *mapheaderseg[NUMMAPS]; byte _seg *audiosegs[NUMSNDCHUNKS]; void _seg *grsegs[NUMCHUNKS]; byte far grneeded[NUMCHUNKS]; byte ca_levelbit,ca_levelnum; int profilehandle,debughandle; char audioname[13]="AUDIO."; /* ============================================================================= LOCAL VARIABLES ============================================================================= */ extern long far CGAhead; extern long far EGAhead; extern byte CGAdict; extern byte EGAdict; extern byte far maphead; extern byte mapdict; extern byte far audiohead; extern byte audiodict; char extension[5], // Need a string, not constant to change cache files gheadname[10]=GREXT"HEAD.", gfilename[10]=GREXT"GRAPH.", gdictname[10]=GREXT"DICT.", mheadname[10]="MAPHEAD.", mfilename[10]="MAPTEMP.", aheadname[10]="AUDIOHED.", afilename[10]="AUDIOT."; void CA_CannotOpen(char *string); long _seg *grstarts; // array of offsets in egagraph, -1 for sparse long _seg *audiostarts; // array of offsets in audio / audiot #ifdef GRHEADERLINKED huffnode *grhuffman; #else huffnode grhuffman[255]; #endif #ifdef AUDIOHEADERLINKED huffnode *audiohuffman; #else huffnode audiohuffman[255]; #endif int grhandle; // handle to EGAGRAPH int maphandle; // handle to MAPTEMP / GAMEMAPS int audiohandle; // handle to AUDIOT / AUDIO long chunkcomplen,chunkexplen; SDMode oldsoundmode; void CAL_CarmackExpand (unsigned far *source, unsigned far *dest, unsigned length); #ifdef THREEBYTEGRSTARTS #define FILEPOSSIZE 3 //#define GRFILEPOS(c) (*(long far *)(((byte far *)grstarts)+(c)*3)&0xffffff) long GRFILEPOS(int c) { long value; int offset; offset = c*3; value = *(long far *)(((byte far *)grstarts)+offset); value &= 0x00ffffffl; if (value == 0xffffffl) value = -1; return value; }; #else #define FILEPOSSIZE 4 #define GRFILEPOS(c) (grstarts[c]) #endif /* ============================================================================= LOW LEVEL ROUTINES ============================================================================= */ /* ============================ = = CA_OpenDebug / CA_CloseDebug = = Opens a binary file with the handle "debughandle" = ============================ */ void CA_OpenDebug (void) { unlink ("DEBUG.TXT"); debughandle = open("DEBUG.TXT", O_CREAT | O_WRONLY | O_TEXT); } void CA_CloseDebug (void) { close (debughandle); } /* ============================ = = CAL_GetGrChunkLength = = Gets the length of an explicit length chunk (not tiles) = The file pointer is positioned so the compressed data can be read in next. = ============================ */ void CAL_GetGrChunkLength (int chunk) { lseek(grhandle,GRFILEPOS(chunk),SEEK_SET); read(grhandle,&chunkexplen,sizeof(chunkexplen)); chunkcomplen = GRFILEPOS(chunk+1)-GRFILEPOS(chunk)-4; } /* ========================== = = CA_FarRead = = Read from a file to a far pointer = ========================== */ boolean CA_FarRead (int handle, byte far *dest, long length) { if (length>0xffffl) Quit ("CA_FarRead doesn't support 64K reads yet!"); asm push ds asm mov bx,[handle] asm mov cx,[WORD PTR length] asm mov dx,[WORD PTR dest] asm mov ds,[WORD PTR dest+2] asm mov ah,0x3f // READ w/handle asm int 21h asm pop ds asm jnc good errno = _AX; return false; good: asm cmp ax,[WORD PTR length] asm je done errno = EINVFMT; // user manager knows this is bad read return false; done: return true; } /* ========================== = = CA_SegWrite = = Write from a file to a far pointer = ========================== */ boolean CA_FarWrite (int handle, byte far *source, long length) { if (length>0xffffl) Quit ("CA_FarWrite doesn't support 64K reads yet!"); asm push ds asm mov bx,[handle] asm mov cx,[WORD PTR length] asm mov dx,[WORD PTR source] asm mov ds,[WORD PTR source+2] asm mov ah,0x40 // WRITE w/handle asm int 21h asm pop ds asm jnc good errno = _AX; return false; good: asm cmp ax,[WORD PTR length] asm je done errno = ENOMEM; // user manager knows this is bad write return false; done: return true; } /* ========================== = = CA_ReadFile = = Reads a file into an allready allocated buffer = ========================== */ boolean CA_ReadFile (char *filename, memptr *ptr) { int handle; long size; if ((handle = open(filename,O_RDONLY | O_BINARY, S_IREAD)) == -1) return false; size = filelength (handle); if (!CA_FarRead (handle,*ptr,size)) { close (handle); return false; } close (handle); return true; } /* ========================== = = CA_WriteFile = = Writes a file from a memory buffer = ========================== */ boolean CA_WriteFile (char *filename, void far *ptr, long length) { int handle; long size; handle = open(filename,O_CREAT | O_BINARY | O_WRONLY, S_IREAD | S_IWRITE | S_IFREG); if (handle == -1) return false; if (!CA_FarWrite (handle,ptr,length)) { close (handle); return false; } close (handle); return true; } /* ========================== = = CA_LoadFile = = Allocate space for and load a file = ========================== */ boolean CA_LoadFile (char *filename, memptr *ptr) { int handle; long size; if ((handle = open(filename,O_RDONLY | O_BINARY, S_IREAD)) == -1) return false; size = filelength (handle); MM_GetPtr (ptr,size); if (!CA_FarRead (handle,*ptr,size)) { close (handle); return false; } close (handle); return true; } /* ============================================================================ COMPRESSION routines, see JHUFF.C for more ============================================================================ */ /* =============== = = CAL_OptimizeNodes = = Goes through a huffman table and changes the 256-511 node numbers to the = actular address of the node. Must be called before CAL_HuffExpand = =============== */ void CAL_OptimizeNodes (huffnode *table) { huffnode *node; int i; node = table; for (i=0;i<255;i++) { if (node->bit0 >= 256) node->bit0 = (unsigned)(table+(node->bit0-256)); if (node->bit1 >= 256) node->bit1 = (unsigned)(table+(node->bit1-256)); node++; } } /* ====================== = = CAL_HuffExpand = = Length is the length of the EXPANDED data = If screenhack, the data is decompressed in four planes directly = to the screen = ====================== */ void CAL_HuffExpand (byte huge *source, byte huge *dest, long length,huffnode *hufftable, boolean screenhack) { // unsigned bit,byte,node,code; unsigned sourceseg,sourceoff,destseg,destoff,endoff; huffnode *headptr; byte mapmask; // huffnode *nodeon; headptr = hufftable+254; // head node is allways node 254 source++; // normalize source--; dest++; dest--; if (screenhack) { mapmask = 1; asm mov dx,SC_INDEX asm mov ax,SC_MAPMASK + 256 asm out dx,ax length >>= 2; } sourceseg = FP_SEG(source); sourceoff = FP_OFF(source); destseg = FP_SEG(dest); destoff = FP_OFF(dest); endoff = destoff+length; // // ds:si source // es:di dest // ss:bx node pointer // if (length <0xfff0) { //-------------------------- // expand less than 64k of data //-------------------------- asm mov bx,[headptr] asm mov si,[sourceoff] asm mov di,[destoff] asm mov es,[destseg] asm mov ds,[sourceseg] asm mov ax,[endoff] asm mov ch,[si] // load first byte asm inc si asm mov cl,1 expandshort: asm test ch,cl // bit set? asm jnz bit1short asm mov dx,[ss:bx] // take bit0 path from node asm shl cl,1 // advance to next bit position asm jc newbyteshort asm jnc sourceupshort bit1short: asm mov dx,[ss:bx+2] // take bit1 path asm shl cl,1 // advance to next bit position asm jnc sourceupshort newbyteshort: asm mov ch,[si] // load next byte asm inc si asm mov cl,1 // back to first bit sourceupshort: asm or dh,dh // if dx<256 its a byte, else move node asm jz storebyteshort asm mov bx,dx // next node = (huffnode *)code asm jmp expandshort storebyteshort: asm mov [es:di],dl asm inc di // write a decopmpressed byte out asm mov bx,[headptr] // back to the head node for next bit asm cmp di,ax // done? asm jne expandshort // // perform screenhack if needed // asm test [screenhack],1 asm jz notscreen asm shl [mapmask],1 asm mov ah,[mapmask] asm cmp ah,16 asm je notscreen // all four planes done asm mov dx,SC_INDEX asm mov al,SC_MAPMASK asm out dx,ax asm mov di,[destoff] asm mov ax,[endoff] asm jmp expandshort notscreen:; } else { //-------------------------- // expand more than 64k of data //-------------------------- length--; asm mov bx,[headptr] asm mov cl,1 asm mov si,[sourceoff] asm mov di,[destoff] asm mov es,[destseg] asm mov ds,[sourceseg] asm lodsb // load first byte expand: asm test al,cl // bit set? asm jnz bit1 asm mov dx,[ss:bx] // take bit0 path from node asm jmp gotcode bit1: asm mov dx,[ss:bx+2] // take bit1 path gotcode: asm shl cl,1 // advance to next bit position asm jnc sourceup asm lodsb asm cmp si,0x10 // normalize ds:si asm jb sinorm asm mov cx,ds asm inc cx asm mov ds,cx asm xor si,si sinorm: asm mov cl,1 // back to first bit sourceup: asm or dh,dh // if dx<256 its a byte, else move node asm jz storebyte asm mov bx,dx // next node = (huffnode *)code asm jmp expand storebyte: asm mov [es:di],dl asm inc di // write a decopmpressed byte out asm mov bx,[headptr] // back to the head node for next bit asm cmp di,0x10 // normalize es:di asm jb dinorm asm mov dx,es asm inc dx asm mov es,dx asm xor di,di dinorm: asm sub [WORD PTR ss:length],1 asm jnc expand asm dec [WORD PTR ss:length+2] asm jns expand // when length = ffff ffff, done } asm mov ax,ss asm mov ds,ax } /* ====================== = = CAL_CarmackExpand = = Length is the length of the EXPANDED data = ====================== */ #define NEARTAG 0xa7 #define FARTAG 0xa8 void CAL_CarmackExpand (unsigned far *source, unsigned far *dest, unsigned length) { unsigned ch,chhigh,count,offset; unsigned far *copyptr, far *inptr, far *outptr; length/=2; inptr = source; outptr = dest; while (length) { ch = *inptr++; chhigh = ch>>8; if (chhigh == NEARTAG) { count = ch&0xff; if (!count) { // have to insert a word containing the tag byte ch |= *((unsigned char far *)inptr)++; *outptr++ = ch; length--; } else { offset = *((unsigned char far *)inptr)++; copyptr = outptr - offset; length -= count; while (count--) *outptr++ = *copyptr++; } } else if (chhigh == FARTAG) { count = ch&0xff; if (!count) { // have to insert a word containing the tag byte ch |= *((unsigned char far *)inptr)++; *outptr++ = ch; length --; } else { offset = *inptr++; copyptr = dest + offset; length -= count; while (count--) *outptr++ = *copyptr++; } } else { *outptr++ = ch; length --; } } } /* ====================== = = CA_RLEWcompress = ====================== */ long CA_RLEWCompress (unsigned huge *source, long length, unsigned huge *dest, unsigned rlewtag) { long complength; unsigned value,count,i; unsigned huge *start,huge *end; start = dest; end = source + (length+1)/2; // // compress it // do { count = 1; value = *source++; while (*source == value && source3 || value == rlewtag) { // // send a tag / count / value string // *dest++ = rlewtag; *dest++ = count; *dest++ = value; } else { // // send word without compressing // for (i=1;i<=count;i++) *dest++ = value; } } while (sourceheaderoffsets[i]; if (pos<0) // $FFFFFFFF start is a sparse map continue; MM_GetPtr(&(memptr)mapheaderseg[i],sizeof(maptype)); MM_SetLock(&(memptr)mapheaderseg[i],true); lseek(maphandle,pos,SEEK_SET); CA_FarRead (maphandle,(memptr)mapheaderseg[i],sizeof(maptype)); } // // allocate space for 3 64*64 planes // for (i=0;iBUFFERSIZE) MM_FreePtr(&bigbufferseg); #endif } //=========================================================================== /* ====================== = = CA_LoadAllSounds = = Purges all sounds, then loads all new ones (mode switch) = ====================== */ void CA_LoadAllSounds (void) { unsigned start,i; switch (oldsoundmode) { case sdm_Off: goto cachein; case sdm_PC: start = STARTPCSOUNDS; break; case sdm_AdLib: start = STARTADLIBSOUNDS; break; } for (i=0;i= STARTTILE8 && chunk < STARTEXTERNS) { // // expanded sizes of tile8/16/32 are implicit // #define BLOCK 64 #define MASKBLOCK 128 if (chunkBUFFERSIZE) MM_FreePtr(&bigbufferseg); } //========================================================================== /* ====================== = = CA_CacheScreen = = Decompresses a chunk from disk straight onto the screen = ====================== */ void CA_CacheScreen (int chunk) { long pos,compressed,expanded; memptr bigbufferseg; byte far *source; int next; // // load the chunk into a buffer // pos = GRFILEPOS(chunk); next = chunk +1; while (GRFILEPOS(next) == -1) // skip past any sparse tiles next++; compressed = GRFILEPOS(next)-pos; lseek(grhandle,pos,SEEK_SET); MM_GetPtr(&bigbufferseg,compressed); MM_SetLock (&bigbufferseg,true); CA_FarRead(grhandle,bigbufferseg,compressed); source = bigbufferseg; expanded = *(long far *)source; source += 4; // skip over length // // allocate final space, decompress it, and free bigbuffer // Sprites need to have shifts made and various other junk // CAL_HuffExpand (source,MK_FP(SCREENSEG,bufferofs),expanded,grhuffman,true); VW_MarkUpdateBlock (0,0,319,199); MM_FreePtr(&bigbufferseg); } //========================================================================== /* ====================== = = CA_CacheMap = = WOLF: This is specialized for a 64*64 map size = ====================== */ void CA_CacheMap (int mapnum) { long pos,compressed; int plane; memptr *dest,bigbufferseg; unsigned size; unsigned far *source; #ifdef CARMACIZED memptr buffer2seg; long expanded; #endif mapon = mapnum; // // load the planes into the allready allocated buffers // size = 64*64*2; for (plane = 0; planeplanestart[plane]; compressed = mapheaderseg[mapnum]->planelength[plane]; dest = &(memptr)mapsegs[plane]; lseek(maphandle,pos,SEEK_SET); if (compressed<=BUFFERSIZE) source = bufferseg; else { MM_GetPtr(&bigbufferseg,compressed); MM_SetLock (&bigbufferseg,true); source = bigbufferseg; } CA_FarRead(maphandle,(byte far *)source,compressed); #ifdef CARMACIZED // // unhuffman, then unRLEW // The huffman'd chunk has a two byte expanded length first // The resulting RLEW chunk also does, even though it's not really // needed // expanded = *source; source++; MM_GetPtr (&buffer2seg,expanded); CAL_CarmackExpand (source, (unsigned far *)buffer2seg,expanded); CA_RLEWexpand (((unsigned far *)buffer2seg)+1,*dest,size, ((mapfiletype _seg *)tinf)->RLEWtag); MM_FreePtr (&buffer2seg); #else // // unRLEW, skipping expanded length // CA_RLEWexpand (source+1, *dest,size, ((mapfiletype _seg *)tinf)->RLEWtag); #endif if (compressed>BUFFERSIZE) MM_FreePtr(&bigbufferseg); } } //=========================================================================== /* ====================== = = CA_UpLevel = = Goes up a bit level in the needed lists and clears it out. = Everything is made purgable = ====================== */ void CA_UpLevel (void) { int i; if (ca_levelnum==7) Quit ("CA_UpLevel: Up past level 7!"); for (i=0;i>=1; ca_levelnum--; CA_CacheMarks(); } //=========================================================================== /* ====================== = = CA_ClearMarks = = Clears out all the marks at the current level = ====================== */ void CA_ClearMarks (void) { int i; for (i=0;i= endpos) { // data is allready in buffer source = (byte _seg *)bufferseg+(pos-bufferstart); } else { // load buffer with a new block from disk // try to get as many of the needed blocks in as possible while ( next < NUMCHUNKS ) { while (next < NUMCHUNKS && !(grneeded[next]&ca_levelbit && !grsegs[next])) next++; if (next == NUMCHUNKS) continue; nextpos = GRFILEPOS(next); while (GRFILEPOS(++next) == -1) // skip past any sparse tiles ; nextendpos = GRFILEPOS(next); if (nextpos - endpos <= MAXEMPTYREAD && nextendpos-pos <= BUFFERSIZE) endpos = nextendpos; else next = NUMCHUNKS; // read pos to posend } lseek(grhandle,pos,SEEK_SET); CA_FarRead(grhandle,bufferseg,endpos-pos); bufferstart = pos; bufferend = endpos; source = bufferseg; } } else { // big chunk, allocate temporary buffer MM_GetPtr(&bigbufferseg,compressed); if (mmerror) return; MM_SetLock (&bigbufferseg,true); lseek(grhandle,pos,SEEK_SET); CA_FarRead(grhandle,bigbufferseg,compressed); source = bigbufferseg; } CAL_ExpandGrChunk (i,source); if (mmerror) return; if (compressed>BUFFERSIZE) MM_FreePtr(&bigbufferseg); } } void CA_CannotOpen(char *string) { char str[30]; strcpy(str,"Can't open "); strcat(str,string); strcat(str,"!\n"); Quit (str); }