You could try to work out what the heck Ken's memory allocation system is supposed to be doing. It's in the file CACHE1D.C, and I really can't make any sense out of most of it. I wanted to try to do a slightly cleaner re-implementation of some (most?) of Ken's engine code, since it might make porting it somewhat easier, especially if I'm going to try to do an OpenGL rendering engine.
initcache and allocache are obvious, but the rest aren't so obvious. uninitcache isn't anywhere in the source code, nor is it used by Build or Duke3D. agecache is called periodically, but it doesn't seem to do accomplish anything.
The rest of the file CACHE1D.C is quite easy. The GRP file loader and filesystem layer is pretty simple, and I've build code like that many times before. One weird bit is LZW compression functions at the bottom. Unless I'm mistaken, isn't that patented by Unisys? I don't think software patents apply in Australia, but I know they do in the US. Might be worth replacing that with Zlib-based functions, since it only seems to be used for save games.
Code: Select all
// "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman
// Ken Silverman's official web site: "http://www.advsys.net/ken"
// See the included license file "BUILDLIC.TXT" for license info.
#include <dos.h>
#include <stdio.h>
#include "pragmas.h"
// This module keeps track of a standard linear cacheing system.
// To use this module, here's all you need to do:
//
// Step 1: Allocate a nice BIG buffer, like from 1MB-4MB and
// Call initcache(long cachestart, long cachesize) where
//
// cachestart = (long)(pointer to start of BIG buffer)
// cachesize = length of BIG buffer
//
// Step 2: Call allocache(long *bufptr, long bufsiz, char *lockptr)
// whenever you need to allocate a buffer, where:
//
// *bufptr = pointer to 4-byte pointer to buffer
// Confused? Using this method, cache2d can remove
// previously allocated things from the cache safely by
// setting the 4-byte pointer to 0.
// bufsiz = number of bytes to allocate
// *lockptr = pointer to locking char which tells whether
// the region can be removed or not. If *lockptr = 0 then
// the region is not locked else its locked.
//
// Step 3: If you need to remove everything from the cache, or every
// unlocked item from the cache, you can call uninitcache();
// Call uninitcache(0) to remove all unlocked items, or
// Call uninitcache(1) to remove everything.
// After calling uninitcache, it is still ok to call allocache
// without first calling initcache.
#define MAXCACHEOBJECTS 9216
static long cachesize = 0;
long cachecount = 0;
char zerochar = 0;
long cachestart = 0, cacnum = 0, agecount = 0;
typedef struct { long *hand, leng; char *lock; } cactype;
cactype cac[MAXCACHEOBJECTS];
static long lockrecip[200];
initcache(long dacachestart, long dacachesize)
{
long i;
for(i=1;i<200;i++) lockrecip[i] = (1<<28)/(200-i);
cachestart = dacachestart;
cachesize = dacachesize;
cac[0].leng = cachesize;
cac[0].lock = &zerochar;
cacnum = 1;
}
allocache (long *newhandle, long newbytes, char *newlockptr)
{
long i, j, z, zz, bestz, daval, bestval, besto, o1, o2, sucklen, suckz;
newbytes = ((newbytes+15)&0xfffffff0);
if ((unsigned)newbytes > (unsigned)cachesize)
{
printf("Cachesize: %ld\n",cachesize);
printf("*Newhandle: 0x%x, Newbytes: %ld, *Newlock: %d\n",newhandle,newbytes,*newlockptr);
reportandexit("BUFFER TOO BIG TO FIT IN CACHE!");
}
if (*newlockptr == 0)
{
reportandexit("ALLOCACHE CALLED WITH LOCK OF 0!");
}
//Find best place
bestval = 0x7fffffff; o1 = cachesize;
for(z=cacnum-1;z>=0;z--)
{
o1 -= cac[z].leng;
o2 = o1+newbytes; if (o2 > cachesize) continue;
daval = 0;
for(i=o1,zz=z;i<o2;i+=cac[zz++].leng)
{
if (*cac[zz].lock == 0) continue;
if (*cac[zz].lock >= 200) { daval = 0x7fffffff; break; }
daval += mulscale32(cac[zz].leng+65536,lockrecip[*cac[zz].lock]);
if (daval >= bestval) break;
}
if (daval < bestval)
{
bestval = daval; besto = o1; bestz = z;
if (bestval == 0) break;
}
}
//printf("%ld %ld %ld\n",besto,newbytes,*newlockptr);
if (bestval == 0x7fffffff)
reportandexit("CACHE SPACE ALL LOCKED UP!");
//Suck things out
for(sucklen=-newbytes,suckz=bestz;sucklen<0;sucklen+=cac[suckz++].leng)
if (*cac[suckz].lock) *cac[suckz].hand = 0;
//Remove all blocks except 1
suckz -= (bestz+1); cacnum -= suckz;
copybufbyte(&cac[bestz+suckz],&cac[bestz],(cacnum-bestz)*sizeof(cactype));
cac[bestz].hand = newhandle; *newhandle = cachestart+besto;
cac[bestz].leng = newbytes;
cac[bestz].lock = newlockptr;
cachecount++;
//Add new empty block if necessary
if (sucklen <= 0) return;
bestz++;
if (bestz == cacnum)
{
cacnum++; if (cacnum > MAXCACHEOBJECTS) reportandexit("Too many objects in cache! (cacnum > MAXCACHEOBJECTS)");
cac[bestz].leng = sucklen;
cac[bestz].lock = &zerochar;
return;
}
if (*cac[bestz].lock == 0) { cac[bestz].leng += sucklen; return; }
cacnum++; if (cacnum > MAXCACHEOBJECTS) reportandexit("Too many objects in cache! (cacnum > MAXCACHEOBJECTS)");
for(z=cacnum-1;z>bestz;z--) cac[z] = cac[z-1];
cac[bestz].leng = sucklen;
cac[bestz].lock = &zerochar;
}
suckcache (long *suckptr)
{
long i;
//Can't exit early, because invalid pointer might be same even though lock = 0
for(i=0;i<cacnum;i++)
if ((long)(*cac[i].hand) == (long)suckptr)
{
if (*cac[i].lock) *cac[i].hand = 0;
cac[i].lock = &zerochar;
cac[i].hand = 0;
//Combine empty blocks
if ((i > 0) && (*cac[i-1].lock == 0))
{
cac[i-1].leng += cac[i].leng;
cacnum--; copybuf(&cac[i+1],&cac[i],(cacnum-i)*sizeof(cactype));
}
else if ((i < cacnum-1) && (*cac[i+1].lock == 0))
{
cac[i+1].leng += cac[i].leng;
cacnum--; copybuf(&cac[i+1],&cac[i],(cacnum-i)*sizeof(cactype));
}
}
}
agecache()
{
long cnt;
char ch;
if (agecount >= cacnum) agecount = cacnum-1;
for(cnt=(cacnum>>4);cnt>=0;cnt--)
{
ch = (*cac[agecount].lock);
if (((ch-2)&255) < 198)
(*cac[agecount].lock) = ch-1;
agecount--; if (agecount < 0) agecount = cacnum-1;
}
}
reportandexit(char *errormessage)
{
long i, j;
setvmode(0x3);
j = 0;
for(i=0;i<cacnum;i++)
{
printf("%ld- ",i);
printf("ptr: 0x%x, ",*cac[i].hand);
printf("leng: %ld, ",cac[i].leng);
printf("lock: %ld\n",*cac[i].lock);
j += cac[i].leng;
}
printf("Cachesize = %ld\n",cachesize);
printf("Cacnum = %ld\n",cacnum);
printf("Cache length sum = %ld\n",j);
printf("ERROR: %s",errormessage);
exit(0);
}