1
0
Fork 0
forked from len0rd/rockbox
foxbox/apps/plugins/sdl/progs/duke3d/Engine/src/cache.c
Franklin Wei a855d62025 Port of Duke Nukem 3D
This ports Fabien Sanglard's Chocolate Duke to run on a version of SDL
for Rockbox.

Change-Id: I8f2c4c78af19de10c1633ed7bb7a997b43256dd9
2017-12-23 21:01:26 -05:00

225 lines
6 KiB
C

/*
* "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.
* This file has been modified from Ken Silverman's original release
*/
#include "platform.h"
#include "display.h"
#include "fixedPoint_math.h"
#include "cache.h"
#include "build.h"
#include "../../Game/src/cvar_defs.h"
#include "../../Game/src/types.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(int32_t cachestart, int32_t cachesize) where
*
* cachestart = (int32_t )(pointer to start of BIG buffer)
* cachesize = length of BIG buffer
*
* Step 2: Call allocache(int32_t *bufptr, int32_t bufsiz, uint8_t *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 uint8_t 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 int32_t cachesize = 0;
int32_t cachecount = 0;
uint8_t zerochar = 0;
uint8_t* cachestart = NULL;
int32_t cacnum = 0, agecount = 0;
typedef struct {
uint8_t** hand;
int32_t leng;
uint8_t *lock; }
cactype;
cactype cac[MAXCACHEOBJECTS];
int32_t lockrecip[200];
// TC game directory
char game_dir[512] = "/.rockbox/duke3d";
void initcache(uint8_t* dacachestart, int32_t dacachesize)
{
int32_t 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;
}
void allocache (uint8_t** newhandle, int32_t newbytes, uint8_t *newlockptr)
{
int32_t i, z, zz, bestz=0, daval, bestval, besto=0, o1, o2, sucklen, suckz;
newbytes = newbytes+15;
if ((uint32_t)newbytes > (uint32_t)cachesize)
{
printf("Cachesize: %d\n",cachesize);
printf("*Newhandle: 0x%x, Newbytes: %d, *Newlock: %d\n",(unsigned int)newhandle,newbytes,*newlockptr);
reportandexit("BUFFER TOO BIG TO FIT IN CACHE!\n");
}
if (*newlockptr == 0)
{
reportandexit("ALLOCACHE CALLED WITH LOCK OF 0!\n");
}
/* 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 += (int32_t ) 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!\n");
/* 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)\n");
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)\n");
for(z=cacnum-1;z>bestz;z--) cac[z] = cac[z-1];
cac[bestz].leng = sucklen;
cac[bestz].lock = &zerochar;
}
void suckcache (int32_t *suckptr)
{
int32_t i;
/* Can't exit early, because invalid pointer might be same even though lock = 0 */
for(i=0;i<cacnum;i++)
if ((int32_t )(*cac[i].hand) == (int32_t )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));
}
}
}
void agecache(void)
{
int32_t cnt;
uint8_t ch;
if (agecount >= cacnum) agecount = cacnum-1;
assert(agecount >= 0);
for(cnt=(cacnum>>4);cnt>=0;cnt--)
{
ch = (*cac[agecount].lock);
if (((ch-2)&255) < 198)
(*cac[agecount].lock) = (uint8_t ) (ch-1);
agecount--; if (agecount < 0) agecount = cacnum-1;
}
}
void reportandexit(char *errormessage)
{
int32_t i, j;
setvmode(0x3);
j = 0;
for(i=0;i<cacnum;i++)
{
printf("%d- ",i);
printf("ptr: 0x%x, ",(int8_t)*cac[i].hand);
printf("leng: %d, ",cac[i].leng);
printf("lock: %d\n",*cac[i].lock);
j += cac[i].leng;
}
printf("Cachesize = %d\n",cachesize);
printf("Cacnum = %d\n",cacnum);
printf("Cache length sum = %d\n",j);
printf("ERROR: %s",errormessage);
Error(EXIT_FAILURE, "");
}