// "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 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 4096

static long cachestart = 0, cachesize = 0, cacheplc = 0;
static long cachelistplc = 0, cachelistend = 0;
static long *handleptr[MAXCACHEOBJECTS], cacheleng[MAXCACHEOBJECTS];
static char *cachelock[MAXCACHEOBJECTS];

initcache(long dacachestart, long dacachesize)
{
	uninitcache(1);
	cachestart = dacachestart;
	cachesize = dacachesize;
	cacheplc = 0;
	cachelistplc = 0;
	cachelistend = 0;
}

uninitcache(char lockstat)   //lockstat: 0-don't free locked, 1-free all
{
	long *longptr, i, startcachelistend;

		//Suck everything out of cache
	startcachelistend = cachelistend;
	while (cachelistplc != startcachelistend)
	{
		if ((*cachelock[cachelistplc] == 0) || (lockstat != 0))
		{
			*handleptr[cachelistplc] = 0;
		}
		else
		{
			handleptr[cachelistend] = handleptr[cachelistplc];
			cacheleng[cachelistend] = cacheleng[cachelistplc];
			cachelock[cachelistend] = cachelock[cachelistplc];
			cachelistend = ((cachelistend+1)&(MAXCACHEOBJECTS-1));
		}
		cachelistplc = ((cachelistplc+1)&(MAXCACHEOBJECTS-1));
	}
}

allocache (long *newhandle, long newbytes, char *newlockptr)
{
	long i, startcachelistplc;

	if (cachesize < newbytes)
	{
		printf("BUFFER TOO BIG TO FIT IN CACHE!\n");
		exit(0);
	}

	startcachelistplc = cachelistplc;
	while (cachelistplc != cachelistend)
	{
		if (cacheplc+newbytes > cachesize)
		{
			while (cachelistplc != cachelistend)
			{
				if (*handleptr[cachelistplc]-cachestart < cacheplc) break;
				cachelistplc = ((cachelistplc+1)&(MAXCACHEOBJECTS-1));
			}
			cacheplc = 0;
		}
		i = *handleptr[cachelistplc]-cachestart;
		if ((i+cacheleng[cachelistplc] <= cacheplc) || (i >= cacheplc+newbytes)) break;
		if (*cachelock[cachelistplc] != 0) cacheplc = i+cacheleng[cachelistplc];
		cachelistplc = ((cachelistplc+1)&(MAXCACHEOBJECTS-1));
	}

		//actually remove from cache here
	while (startcachelistplc != cachelistplc)
	{
		i = *handleptr[startcachelistplc]-cachestart;
		if ((i+cacheleng[startcachelistplc] > cacheplc) && (i < cacheplc+newbytes))
		{
			if (*cachelock[startcachelistplc] != 0)
			{
				printf("CACHE SPACE ALL LOCKED UP!\n");
				exit(0);
			}
			*handleptr[startcachelistplc] = 0;
		}
		else
		{
			 handleptr[cachelistend] = handleptr[startcachelistplc];
			 cacheleng[cachelistend] = cacheleng[startcachelistplc];
			 cachelock[cachelistend] = cachelock[startcachelistplc];
			 cachelistend = ((cachelistend+1)&(MAXCACHEOBJECTS-1));
		}
		startcachelistplc = ((startcachelistplc+1)&(MAXCACHEOBJECTS-1));
	}

	handleptr[cachelistend] = newhandle; *newhandle = cachestart+cacheplc;
	cacheleng[cachelistend] = newbytes;
	cachelock[cachelistend] = newlockptr;
	cachelistend = ((cachelistend+1)&(MAXCACHEOBJECTS-1));

	cacheplc += newbytes;
}




#include <fcntl.h>
#include <io.h>
#include <sys\types.h>
#include <sys\stat.h>

#define MAXOPENFILES 64

static long numfiles, groupfil = -1, groupfilpos;
static char *filelist;
static long *fileoffs;

static long filehandle[MAXOPENFILES], filepos[MAXOPENFILES];

initgroupfile(char *filename)
{
	char buf[16];
	long i, j, k;

	uninitgroupfile();

	for(i=0;i<MAXOPENFILES;i++) filehandle[i] = -1;

	groupfil = open(filename,O_BINARY|O_RDWR,S_IREAD);
	if (groupfil != -1)
	{
		groupfilpos = 0;
		read(groupfil,buf,16);
		if ((buf[0] != 'K') || (buf[1] != 'e') || (buf[2] != 'n') ||
			 (buf[3] != 'S') || (buf[4] != 'i') || (buf[5] != 'l') ||
			 (buf[6] != 'v') || (buf[7] != 'e') || (buf[8] != 'r') ||
			 (buf[9] != 'm') || (buf[10] != 'a') || (buf[11] != 'n'))
		{
			close(groupfil);
			groupfil = -1;
			return(-1);
		}
		numfiles = ((long)buf[12])+(((long)buf[13])<<8)+(((long)buf[14])<<16)+(((long)buf[15])<<24);

		if ((filelist = (char *)malloc(numfiles<<4)) == 0)
			{ printf("Not enough memory for file grouping system\n"); exit(0); }
		if ((fileoffs = (long *)malloc((numfiles+1)<<2)) == 0)
			{ printf("Not enough memory for file grouping system\n"); exit(0); }

		read(groupfil,filelist,numfiles<<4);

		j = 0;
		for(i=0;i<numfiles;i++)
		{
			k = ((long)filelist[(i<<4)+12])+(((long)filelist[(i<<4)+13])<<8)+(((long)filelist[(i<<4)+14])<<16)+(((long)filelist[(i<<4)+15])<<24);
			filelist[(i<<4)+12] = 0;
			fileoffs[i] = j;
			j += k;
		}
		fileoffs[numfiles] = j;
	}
	return(groupfil);
}

uninitgroupfile()
{
	if (groupfil != -1)
	{
		free(filelist); free(fileoffs);
		close(groupfil);
		groupfil = -1;
	}
}

kopen4load(char *filename, char searchfirst)
{
	long i, j, fil, newhandle;
	char ch1, ch2, bad;

	newhandle = MAXOPENFILES-1;
	while (filehandle[newhandle] != -1)
	{
		newhandle--;
		if (newhandle < 0)
		{
			printf("TOO MANY FILES OPEN IN FILE GROUPING SYSTEM!");
			exit(0);
		}
	}

	if (searchfirst == 0)
		if ((fil = open(filename,O_BINARY|O_RDONLY)) != -1)
		{
			filehandle[newhandle] = fil;
			filepos[newhandle] = -1;
			return(newhandle);
		}
	if (groupfil != -1)
	{
		for(i=numfiles-1;i>=0;i--)
		{
			j = 0; bad = 0;
			while ((filename[j] != 0) && (j < 13))
			{
				ch1 = filename[j]; if ((ch1 >= 97) && (ch1 <= 123)) ch1 -= 32;
				ch2 = filelist[(i<<4)+j]; if ((ch2 >= 97) && (ch2 <= 123)) ch2 -= 32;
				if (ch1 != ch2) { bad = 1; break; }
				j++;
			}
			if (bad != 0) continue;

			filepos[newhandle] = 0;
			filehandle[newhandle] = i;
			return(newhandle);
		}
	}
	return(-1);
}

kread(long handle, void *buffer, long leng)
{
	long i, filenum;

	filenum = filehandle[handle];
	if (filepos[handle] < 0) return(read(filenum,buffer,leng));

	if (groupfil != -1)
	{
		i = fileoffs[filenum]+filepos[handle];
		if (i != groupfilpos)
		{
			lseek(groupfil,i+((numfiles+1)<<4),SEEK_SET);
			groupfilpos = i;
		}
		leng = min(leng,(fileoffs[filenum+1]-fileoffs[filenum])-filepos[handle]);
		leng = read(groupfil,buffer,leng);
		filepos[handle] += leng;
		groupfilpos += leng;
		return(leng);
	}

	return(0);
}

klseek(long handle, long offset, long whence)
{
	long i;

	if (filepos[handle] < 0) return(lseek(filehandle[handle],offset,whence));
	if (groupfil != -1)
	{
		switch(whence)
		{
			case SEEK_SET: filepos[handle] = offset; break;
			case SEEK_END: i = filehandle[handle];
								filepos[handle] = (fileoffs[i+1]-fileoffs[i])+offset;
								break;
			case SEEK_CUR: filepos[handle] += offset; break;
		}
		return(filepos[handle]);
	}
	return(-1);
}

kfilelength(long handle)
{
	long i;

	if (filepos[handle] < 0) return(filelength(filehandle[handle]));
	i = filehandle[handle];
	return(fileoffs[i+1]-fileoffs[i]);
}

kclose(long handle)
{
	if (filepos[handle] < 0) close(filehandle[handle]);
	filehandle[handle] = -1;
}
