raze-gles/source/build/src/cache1d.cpp
Christoph Oelckers d058084c10 - added an engine resource file.
Currently this only contains the main Polymost shaders and the resources from nblood.pk3.
The latter cannot be used yet because the Build resource management system is too stubborn to add the newly added file without some changes.
It's better to refactor the entire system instead.
2019-10-04 23:29:00 +02:00

1729 lines
49 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
// by Jonathon Fowler (jf@jonof.id.au)
// by the EDuke32 team (development@voidpoint.com)
#include "compat.h"
#ifdef _WIN32
// for FILENAME_CASE_CHECK
# define NEED_SHELLAPI_H
# include "windows_inc.h"
#endif
#include "cache1d.h"
#include "pragmas.h"
#include "baselayer.h"
#include "lz4.h"
#include "vfs.h"
#ifdef WITHKPLIB
#include "kplib.h"
#include "zstring.h"
//Insert '|' in front of filename
//Doing this tells kzopen to load the file only if inside a .ZIP file
static intptr_t kzipopen(const char *filnam)
{
uint32_t i;
char newst[BMAX_PATH+8];
newst[0] = '|';
for (i=0; i < BMAX_PATH+4 && filnam[i]; i++) newst[i+1] = filnam[i];
newst[i+1] = 0;
return kzopen(newst);
}
#endif
char *kpzbuf = NULL;
int32_t kpzbufsiz;
int32_t kpzbufloadfil(buildvfs_kfd const handle)
{
int32_t const leng = kfilelength(handle);
if (leng > kpzbufsiz)
{
kpzbuf = (char *) Xrealloc(kpzbuf, leng+1);
kpzbufsiz = leng;
if (!kpzbuf)
return 0;
}
kpzbuf[leng] = 0; // FIXME: buf[leng] read in kpegrend(), see BUF_LENG_READ
kread(handle, kpzbuf, leng);
return leng;
}
int32_t kpzbufload(char const * const filnam)
{
buildvfs_kfd const handle = kopen4load(filnam, 0);
if (handle == buildvfs_kfd_invalid)
return 0;
int32_t const leng = kpzbufloadfil(handle);
kclose(handle);
return leng;
}
// 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 = (intptr_t)(pointer to start of BIG buffer)
// cachesize = length of BIG buffer
//
// Step 2: Call allocache(intptr_t *bufptr, int32_t bufsiz, char *lockptr)
// whenever you need to allocate a buffer, where:
//
// *bufptr = pointer to multi-byte pointer to buffer
// Confused? Using this method, cache2d can remove
// previously allocated things from the cache safely by
// setting the multi-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 16384
#if !defined DEBUG_ALLOCACHE_AS_MALLOC
static int32_t cachesize = 0;
static uint8_t zerochar = 0;
static intptr_t cachestart = 0;
static int32_t lockrecip[200];
int32_t cacnum = 0;
cactype cac[MAXCACHEOBJECTS];
#endif
uint8_t toupperlookup[256] =
{
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,
0x60,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x7b,0x7c,0x7d,0x7e,0x7f,
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,
0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,
0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff
};
static void reportandexit(const char *errormessage);
void cacheInitBuffer(intptr_t dacachestart, int32_t dacachesize)
{
#ifndef DEBUG_ALLOCACHE_AS_MALLOC
int32_t i;
for (i=1; i<200; i++)
lockrecip[i] = tabledivide32_noinline(1<<28, 200-i);
// we allocate this block with aligned_alloc, but I'm leaving this here in
// case we run on any platforms that just implement it as regular malloc
cachestart = ((uintptr_t)dacachestart+15)&~(uintptr_t)0xf;
cachesize = (dacachesize-(((uintptr_t)(dacachestart))&0xf))&~(uintptr_t)0xf;
cac[0].leng = cachesize;
cac[0].lock = &zerochar;
cacnum = 1;
initprintf("Initialized %.1fM cache\n", (float)(dacachesize/1024.f/1024.f));
#else
UNREFERENCED_PARAMETER(dacachestart);
UNREFERENCED_PARAMETER(dacachesize);
#endif
}
#ifdef DEBUG_ALLOCACHE_AS_MALLOC
void cacheAllocateBlock(intptr_t *newhandle, int32_t newbytes, uint8_t *newlockptr)
{
UNREFERENCED_PARAMETER(newlockptr);
*newhandle = (intptr_t)Xmalloc(newbytes);
}
#else
static inline void inc_and_check_cacnum(void)
{
if (EDUKE32_PREDICT_FALSE(++cacnum > MAXCACHEOBJECTS))
reportandexit("Too many objects in cache! (cacnum > MAXCACHEOBJECTS)");
}
int32_t cacheFindBlock(int32_t newbytes, int32_t *besto, int32_t *bestz)
{
int32_t bestval = 0x7fffffff;
for (native_t z=cacnum-1, o1=cachesize; z>=0; z--)
{
o1 -= cac[z].leng;
int32_t const o2 = o1 + newbytes;
if (o2 > cachesize)
continue;
int32_t daval = 0;
for (native_t i=o1, zz=z; i<o2; i+=cac[zz++].leng)
{
if (*cac[zz].lock == 0)
continue;
else if (*cac[zz].lock >= 200)
{
daval = 0x7fffffff;
break;
}
// Potential for eviction increases with
// - smaller item size
// - smaller lock byte value (but in [1 .. 199])
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;
}
}
return bestval;
}
void cacheAllocateBlock(intptr_t* newhandle, int32_t newbytes, uint8_t* newlockptr)
{
// Make all requests a multiple of the system page size
int const pageSize = Bgetpagesize();
newbytes = (newbytes + pageSize-1) & ~(pageSize-1);
#ifdef DEBUGGINGAIDS
if (EDUKE32_PREDICT_FALSE(!newlockptr || *newlockptr == 0))
reportandexit("ALLOCACHE CALLED WITH LOCK OF 0!");
#endif
if (EDUKE32_PREDICT_FALSE((unsigned)newbytes > (unsigned)cachesize))
{
initprintf("Cachesize: %d\n",cachesize);
initprintf("*Newhandle: 0x%" PRIxPTR ", Newbytes: %d, *Newlock: %d\n",(intptr_t)newhandle,newbytes,*newlockptr);
reportandexit("BUFFER TOO BIG TO FIT IN CACHE!");
}
int32_t bestz = 0;
int32_t besto = 0;
int cnt = cacnum-1;
// if we can't find a block, try to age the cache until we can
// it's better than the alternative of aborting the entire program
while (cacheFindBlock(newbytes, &besto, &bestz) == 0x7fffffff)
{
cacheAgeEntries();
if (!cnt--) reportandexit("CACHE SPACE ALL LOCKED UP!");
}
//printf("%d %d %d\n",besto,newbytes,*newlockptr);
//Suck things out
int32_t sucklen = -newbytes;
int32_t suckz = bestz;
for (;sucklen<0; sucklen+=cac[suckz++].leng)
if (*cac[suckz].lock)
*cac[suckz].hand = 0;
//Remove all blocks except 1
suckz -= bestz+1;
cacnum -= suckz;
Bmemmove(&cac[bestz], &cac[bestz + suckz], (cacnum - bestz) * sizeof(cactype));
cac[bestz].hand = newhandle;
*newhandle = cachestart + besto;
cac[bestz].leng = newbytes;
cac[bestz].lock = newlockptr;
//Add new empty block if necessary
if (sucklen <= 0)
return;
if (++bestz == cacnum)
{
inc_and_check_cacnum();
cac[bestz].leng = sucklen;
cac[bestz].lock = &zerochar;
return;
}
if (*cac[bestz].lock == 0)
{
cac[bestz].leng += sucklen;
return;
}
inc_and_check_cacnum();
for (native_t z=cacnum-1; z>bestz; z--)
cac[z] = cac[z-1];
cac[bestz].leng = sucklen;
cac[bestz].lock = &zerochar;
}
#endif
void cacheAgeEntries(void)
{
#ifndef DEBUG_ALLOCACHE_AS_MALLOC
static int agecount;
if (agecount >= cacnum)
agecount = cacnum-1;
int cnt = min(MAXCACHEOBJECTS >> 5, cacnum-1);
while(cnt--)
{
// If we have pointer to lock char and it's in [2 .. 199], decrease.
if (cac[agecount].lock)
{
if ((((*cac[agecount].lock)-2)&255) < 198)
(*cac[agecount].lock)--;
else if (*cac[agecount].lock == 255)
cnt++;
}
if (--agecount < 0)
agecount = cacnum-1;
}
#endif
}
static void reportandexit(const char *errormessage)
{
#ifndef DEBUG_ALLOCACHE_AS_MALLOC
//setvmode(0x3);
int32_t j = 0;
for (native_t i = 0; i < cacnum; i++)
{
buildprint(i, "- ");
if (cac[i].hand)
initprintf("ptr: 0x%" PRIxPTR ", ", *cac[i].hand);
else
initprintf("ptr: NULL, ");
initprintf("leng: %d, ", cac[i].leng);
if (cac[i].lock)
initprintf("lock: %d\n", *cac[i].lock);
else
initprintf("lock: NULL\n");
j += cac[i].leng;
}
initprintf("Cachesize = %d\n", cachesize);
initprintf("Cacnum = %d\n", cacnum);
initprintf("Cache length sum = %d\n", j);
#endif
initprintf("ERROR: %s\n", errormessage);
Bexit(1);
}
#include <errno.h>
typedef struct _searchpath
{
struct _searchpath *next;
char *path;
size_t pathlen; // to save repeated calls to strlen()
int32_t user;
} searchpath_t;
static searchpath_t *searchpathhead = NULL;
static size_t maxsearchpathlen = 0;
int32_t pathsearchmode = 0;
#ifndef USE_PHYSFS
char *listsearchpath(int32_t initp)
{
static searchpath_t *sp;
if (initp)
sp = searchpathhead;
else if (sp != NULL)
sp = sp->next;
return sp ? sp->path : NULL;
}
int32_t addsearchpath_user(const char *p, int32_t user)
{
struct Bstat st;
char *s;
searchpath_t *srch;
char *path = Xstrdup(p);
if (path[Bstrlen(path)-1] == '\\')
path[Bstrlen(path)-1] = 0; // hack for stat() returning ENOENT on paths ending in a backslash
if (Bstat(path, &st) < 0)
{
Xfree(path);
if (errno == ENOENT) return -2;
return -1;
}
if (!(st.st_mode & BS_IFDIR))
{
Xfree(path);
return -1;
}
srch = (searchpath_t *)Xmalloc(sizeof(searchpath_t));
srch->next = searchpathhead;
srch->pathlen = Bstrlen(path)+1;
srch->path = (char *)Xmalloc(srch->pathlen + 1);
Bstrcpy(srch->path, path);
for (s=srch->path; *s; s++) { }
s--;
if (s<srch->path || toupperlookup[*s] != '/')
Bstrcat(srch->path, "/");
searchpathhead = srch;
if (srch->pathlen > maxsearchpathlen)
maxsearchpathlen = srch->pathlen;
Bcorrectfilename(srch->path,0);
srch->user = user;
initprintf("Using %s for game data\n", srch->path);
Xfree(path);
return 0;
}
int32_t removesearchpath(const char *p)
{
searchpath_t *srch;
char *s;
char *path = (char *)Xmalloc(Bstrlen(p) + 2);
Bstrcpy(path, p);
if (path[Bstrlen(path)-1] == '\\')
path[Bstrlen(path)-1] = 0;
for (s=path; *s; s++) { }
s--;
if (s<path || toupperlookup[*s] != '/')
Bstrcat(path, "/");
Bcorrectfilename(path,0);
for (srch = searchpathhead; srch; srch = srch->next)
{
if (!Bstrncmp(path, srch->path, srch->pathlen))
{
// initprintf("Removing %s from path stack\n", path);
if (srch == searchpathhead)
searchpathhead = srch->next;
else
{
searchpath_t *sp;
for (sp = searchpathhead; sp; sp = sp->next)
{
if (sp->next == srch)
{
// initprintf("matched %s\n", srch->path);
sp->next = srch->next;
break;
}
}
}
Xfree(srch->path);
Xfree(srch);
break;
}
}
Xfree(path);
return 0;
}
void removesearchpaths_withuser(int32_t usermask)
{
searchpath_t *next;
for (searchpath_t *srch = searchpathhead; srch; srch = next)
{
next = srch->next;
if (srch->user & usermask)
{
if (srch == searchpathhead)
searchpathhead = srch->next;
else
{
searchpath_t *sp;
for (sp = searchpathhead; sp; sp = sp->next)
{
if (sp->next == srch)
{
sp->next = srch->next;
break;
}
}
}
Xfree(srch->path);
Xfree(srch);
}
}
}
int32_t findfrompath(const char *fn, char **where)
{
// pathsearchmode == 0: tests current dir and then the dirs of the path stack
// pathsearchmode == 1: tests fn without modification, then like for pathsearchmode == 0
if (pathsearchmode)
{
// test unmolested filename first
if (buildvfs_exists(fn))
{
*where = Xstrdup(fn);
return 0;
}
#ifndef _WIN32
else
{
char *tfn = Bstrtolower(Xstrdup(fn));
if (buildvfs_exists(tfn))
{
*where = tfn;
return 0;
}
Bstrupr(tfn);
if (buildvfs_exists(tfn))
{
*where = tfn;
return 0;
}
Xfree(tfn);
}
#endif
}
char const *cpfn;
for (cpfn = fn; toupperlookup[*cpfn] == '/'; cpfn++) { }
char *ffn = Xstrdup(cpfn);
Bcorrectfilename(ffn,0); // compress relative paths
int32_t allocsiz = max<int>(maxsearchpathlen, 2); // "./" (aka. curdir)
allocsiz += strlen(ffn);
allocsiz += 1; // a nul
char *pfn = (char *)Xmalloc(allocsiz);
strcpy(pfn, "./");
strcat(pfn, ffn);
if (buildvfs_exists(pfn))
{
*where = pfn;
Xfree(ffn);
return 0;
}
for (searchpath_t *sp = searchpathhead; sp; sp = sp->next)
{
char *tfn = Xstrdup(ffn);
strcpy(pfn, sp->path);
strcat(pfn, ffn);
//initprintf("Trying %s\n", pfn);
if (buildvfs_exists(pfn))
{
*where = pfn;
Xfree(ffn);
Xfree(tfn);
return 0;
}
#ifndef _WIN32
//Check with all lowercase
strcpy(pfn, sp->path);
Bstrtolower(tfn);
strcat(pfn, tfn);
if (buildvfs_exists(pfn))
{
*where = pfn;
Xfree(ffn);
Xfree(tfn);
return 0;
}
//Check again with uppercase
strcpy(pfn, sp->path);
Bstrupr(tfn);
strcat(pfn, tfn);
if (buildvfs_exists(pfn))
{
*where = pfn;
Xfree(ffn);
Xfree(tfn);
return 0;
}
#endif
Xfree(tfn);
}
Xfree(pfn); Xfree(ffn);
return -1;
}
#if defined(_WIN32) && defined(DEBUGGINGAIDS)
# define FILENAME_CASE_CHECK
#endif
static buildvfs_kfd openfrompath_internal(const char *fn, char **where, int32_t flags, int32_t mode)
{
if (findfrompath(fn, where) < 0)
return -1;
return Bopen(*where, flags, mode);
}
buildvfs_kfd openfrompath(const char *fn, int32_t flags, int32_t mode)
{
char *pfn = NULL;
buildvfs_kfd h = openfrompath_internal(fn, &pfn, flags, mode);
Xfree(pfn);
return h;
}
buildvfs_FILE fopenfrompath(const char *fn, const char *mode)
{
int32_t fh;
buildvfs_FILE h;
int32_t bmode = 0, smode = 0;
const char *c;
for (c=mode; c[0];)
{
if (c[0] == 'r' && c[1] == '+') { bmode = BO_RDWR; smode = BS_IREAD|BS_IWRITE; c+=2; }
else if (c[0] == 'r') { bmode = BO_RDONLY; smode = BS_IREAD; c+=1; }
else if (c[0] == 'w' && c[1] == '+') { bmode = BO_RDWR|BO_CREAT|BO_TRUNC; smode = BS_IREAD|BS_IWRITE; c+=2; }
else if (c[0] == 'w') { bmode = BO_WRONLY|BO_CREAT|BO_TRUNC; smode = BS_IREAD|BS_IWRITE; c+=2; }
else if (c[0] == 'a' && c[1] == '+') { bmode = BO_RDWR|BO_CREAT; smode=BS_IREAD|BS_IWRITE; c+=2; }
else if (c[0] == 'a') { bmode = BO_WRONLY|BO_CREAT; smode=BS_IREAD|BS_IWRITE; c+=1; }
else if (c[0] == 'b') { bmode |= BO_BINARY; c+=1; }
else if (c[1] == 't') { bmode |= BO_TEXT; c+=1; }
else c++;
}
fh = openfrompath(fn,bmode,smode);
if (fh < 0) return NULL;
h = fdopen(fh,mode);
if (!h) close(fh);
return h;
}
#define MAXGROUPFILES 8 // Warning: Fix groupfil if this is changed
#define MAXOPENFILES 64 // Warning: Fix filehan if this is changed
enum {
GRP_RESERVED_ID_START = 254,
GRP_ZIP = GRP_RESERVED_ID_START,
GRP_FILESYSTEM = GRP_RESERVED_ID_START + 1,
};
EDUKE32_STATIC_ASSERT(MAXGROUPFILES <= GRP_RESERVED_ID_START);
int32_t numgroupfiles = 0;
static int32_t gnumfiles[MAXGROUPFILES];
static intptr_t groupfil[MAXGROUPFILES] = {-1,-1,-1,-1,-1,-1,-1,-1};
static int32_t groupfilpos[MAXGROUPFILES];
static uint8_t groupfilgrp[MAXGROUPFILES];
static char *gfilelist[MAXGROUPFILES];
static char *groupname[MAXGROUPFILES];
static int32_t *gfileoffs[MAXGROUPFILES];
static uint8_t filegrp[MAXOPENFILES];
static int32_t filepos[MAXOPENFILES];
static intptr_t filehan[MAXOPENFILES] =
{
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
};
#ifdef WITHKPLIB
static char filenamsav[MAXOPENFILES][260];
static int32_t kzcurhand = -1;
int32_t cache1d_file_fromzip(buildvfs_kfd fil)
{
return (filegrp[fil] == GRP_ZIP);
}
#endif
static int32_t kopen_internal(const char *filename, char **lastpfn, char searchfirst, char checkcase, char tryzip, int32_t newhandle, uint8_t *arraygrp, intptr_t *arrayhan, int32_t *arraypos);
static int32_t kread_grp(int32_t handle, void *buffer, int32_t leng);
static int32_t klseek_grp(int32_t handle, int32_t offset, int32_t whence);
static void kclose_grp(int32_t handle);
static bool alreadycalled;
extern FString progdir;
int initgroupfile(const char *filename)
{
char buf[70];
// translate all backslashes (0x5c) to forward slashes (0x2f)
toupperlookup[0x5c] = 0x2f;
if (filename == NULL)
return -1;
// Technically you should be able to load more zips even if your GRPs are maxed out,
// but this system is already enough of a disaster.
if (numgroupfiles >= MAXGROUPFILES)
return -1;
char *zfn = NULL;
if (kopen_internal(filename, &zfn, 0, 0, 0, numgroupfiles, groupfilgrp, groupfil, groupfilpos) < 0)
return -1;
#ifdef WITHKPLIB
// check if ZIP
if (zfn)
{
kread_grp(numgroupfiles, buf, 4);
if (buf[0] == 0x50 && buf[1] == 0x4B && buf[2] == 0x03 && buf[3] == 0x04)
{
kclose_grp(numgroupfiles);
kzaddstack(zfn);
Xfree(zfn);
return MAXGROUPFILES;
}
klseek_grp(numgroupfiles,0,BSEEK_SET);
Xfree(zfn);
}
#else
Xfree(zfn);
#endif
// check if GRP
kread_grp(numgroupfiles,buf,16);
if (!Bmemcmp(buf, "KenSilverman", 12))
{
gnumfiles[numgroupfiles] = B_LITTLE32(*((int32_t *)&buf[12]));
gfilelist[numgroupfiles] = (char *)Xmalloc(gnumfiles[numgroupfiles]<<4);
gfileoffs[numgroupfiles] = (int32_t *)Xmalloc((gnumfiles[numgroupfiles]+1)<<2);
kread_grp(numgroupfiles,gfilelist[numgroupfiles],gnumfiles[numgroupfiles]<<4);
int32_t j = (gnumfiles[numgroupfiles]+1)<<4;
for (bssize_t i=0; i<gnumfiles[numgroupfiles]; i++)
{
int32_t const k = B_LITTLE32(*((int32_t *)&gfilelist[numgroupfiles][(i<<4)+12]));
gfilelist[numgroupfiles][(i<<4)+12] = 0;
gfileoffs[numgroupfiles][i] = j;
j += k;
}
gfileoffs[numgroupfiles][gnumfiles[numgroupfiles]] = j;
groupname[numgroupfiles] = Xstrdup(filename);
return numgroupfiles++;
}
klseek_grp(numgroupfiles, 0, BSEEK_SET);
// check if SSI
// this performs several checks because there is no "SSI" magic
int32_t version;
kread_grp(numgroupfiles, &version, 4);
version = B_LITTLE32(version);
while (version == 1 || version == 2) // if
{
char zerobuf[70];
Bmemset(zerobuf, 0, 70);
int32_t numfiles;
kread_grp(numgroupfiles, &numfiles, 4);
numfiles = B_LITTLE32(numfiles);
uint8_t temp, temp2;
// get the string length
kread_grp(numgroupfiles, &temp, 1);
if (temp > 31) // 32 bytes allocated for the string
break;
// seek to the end of the string
klseek_grp(numgroupfiles, temp, BSEEK_CUR);
// verify everything remaining is a null terminator
temp = 32 - temp;
kread_grp(numgroupfiles, buf, temp);
if (Bmemcmp(buf, zerobuf, temp))
break;
if (version == 2)
{
// get the string length
kread_grp(numgroupfiles, &temp, 1);
if (temp > 11) // 12 bytes allocated for the string
break;
// seek to the end of the string
klseek_grp(numgroupfiles, temp, BSEEK_CUR);
// verify everything remaining is a null terminator
temp = 12 - temp;
kread_grp(numgroupfiles, buf, temp);
if (Bmemcmp(buf, zerobuf, temp))
break;
}
temp2 = 0;
for (int i=0;i<3;i++)
{
// get the string length
kread_grp(numgroupfiles, &temp, 1);
if (temp > 70) // 70 bytes allocated for the string
{
temp2 = 1;
break;
}
// seek to the end of the string
klseek_grp(numgroupfiles, temp, BSEEK_CUR);
// verify everything remaining is a null terminator
temp = 70 - temp;
if (temp == 0)
continue;
kread_grp(numgroupfiles, buf, temp);
temp2 |= !!Bmemcmp(buf, zerobuf, temp);
}
if (temp2)
break;
// Passed all the tests: read data.
gnumfiles[numgroupfiles] = numfiles;
gfilelist[numgroupfiles] = (char *)Xmalloc(gnumfiles[numgroupfiles]<<4);
gfileoffs[numgroupfiles] = (int32_t *)Xmalloc((gnumfiles[numgroupfiles]+1)<<2);
int32_t j = (version == 2 ? 267 : 254) + (numfiles * 121), k;
for (bssize_t i = 0; i < numfiles; i++)
{
// get the string length
kread_grp(numgroupfiles, &temp, 1);
if (temp > 12)
temp = 12;
// read the file name
kread_grp(numgroupfiles, &gfilelist[numgroupfiles][i<<4], temp);
gfilelist[numgroupfiles][(i<<4)+temp] = 0;
// skip to the end of the 12 bytes
klseek_grp(numgroupfiles, 12-temp, BSEEK_CUR);
// get the file size
kread_grp(numgroupfiles, &k, 4);
k = B_LITTLE32(k);
// record the offset of the file in the SSI
gfileoffs[numgroupfiles][i] = j;
j += k;
// skip unknown data
klseek_grp(numgroupfiles, 104, BSEEK_CUR);
}
gfileoffs[numgroupfiles][gnumfiles[numgroupfiles]] = j;
groupname[numgroupfiles] = Xstrdup(filename);
return numgroupfiles++;
}
kclose_grp(numgroupfiles);
return -1;
}
void uninitgroupfile(void)
{
int32_t i;
for (i=numgroupfiles-1; i>=0; i--)
if (groupfil[i] != -1)
{
DO_FREE_AND_NULL(gfilelist[i]);
DO_FREE_AND_NULL(gfileoffs[i]);
DO_FREE_AND_NULL(groupname[i]);
Bclose(groupfil[i]);
groupfil[i] = -1;
}
numgroupfiles = 0;
// JBF 20040111: "close" any files open in groups
for (i=0; i<MAXOPENFILES; i++)
{
if (filegrp[i] < GRP_RESERVED_ID_START) // JBF 20040130: not external or ZIPped
filehan[i] = -1;
}
}
#ifdef FILENAME_CASE_CHECK
// See
// http://stackoverflow.com/questions/74451/getting-actual-file-name-with-proper-casing-on-windows
// for relevant discussion.
static char fnbuf[BMAX_PATH];
int fnofs;
int32_t (*check_filename_casing_fn)(void) = NULL;
// -1: failure, 0: match, 1: mismatch
static int32_t check_filename_mismatch(const char * const filename, int ofs)
{
if (!GetShortPathNameA(filename, fnbuf, BMAX_PATH)) return -1;
if (!GetLongPathNameA(fnbuf, fnbuf, BMAX_PATH)) return -1;
fnofs = ofs;
int len = Bstrlen(fnbuf+ofs);
char const * const fn = filename+ofs;
if (!Bstrncmp(fnbuf+ofs, fn, len))
return 0;
char * const tfn = Bstrtolower(Xstrdup(fn));
if (!Bstrncmp(fnbuf+ofs, tfn, len))
{
Xfree(tfn);
return 0;
}
Bstrupr(tfn);
if (!Bstrncmp(fnbuf+ofs, tfn, len))
{
Xfree(tfn);
return 0;
}
Xfree(tfn);
return 1;
}
#endif
static int32_t kopen_internal(const char *filename, char **lastpfn, char searchfirst, char checkcase, char tryzip, int32_t newhandle, uint8_t *arraygrp, intptr_t *arrayhan, int32_t *arraypos)
{
buildvfs_kfd fil;
if (searchfirst == 0 && (fil = openfrompath_internal(filename, lastpfn, BO_BINARY|BO_RDONLY, BS_IREAD)) >= 0)
{
#ifdef FILENAME_CASE_CHECK
if (checkcase && check_filename_casing_fn && check_filename_casing_fn())
{
int32_t status;
char *cp, *lastslash;
// convert all slashes to backslashes because SHGetFileInfo()
// complains else!
lastslash = *lastpfn;
for (cp=*lastpfn; *cp; cp++)
if (*cp=='/')
{
*cp = '\\';
lastslash = cp;
}
if (lastslash != *lastpfn)
lastslash++;
status = check_filename_mismatch(*lastpfn, lastslash-*lastpfn);
if (status == -1)
{
// initprintf("SHGetFileInfo failed with error code %lu\n", GetLastError());
}
else if (status == 1)
{
initprintf("warning: case mismatch: passed \"%s\", real \"%s\"\n",
lastslash, fnbuf+fnofs);
}
}
#else
UNREFERENCED_PARAMETER(checkcase);
#endif
arraygrp[newhandle] = GRP_FILESYSTEM;
arrayhan[newhandle] = fil;
arraypos[newhandle] = 0;
return newhandle;
}
for (; toupperlookup[*filename] == '/'; filename++) { }
#ifdef WITHKPLIB
if (tryzip)
{
intptr_t i;
if ((kzcurhand != newhandle) && (kztell() >= 0))
{
if (kzcurhand >= 0) arraypos[kzcurhand] = kztell();
kzclose();
kzcurhand = -1;
}
if (searchfirst != 1 && (i = kzipopen(filename)) != 0)
{
kzcurhand = newhandle;
arraygrp[newhandle] = GRP_ZIP;
arrayhan[newhandle] = i;
arraypos[newhandle] = 0;
strcpy(filenamsav[newhandle],filename);
return newhandle;
}
}
#else
UNREFERENCED_PARAMETER(tryzip);
#endif
for (bssize_t k = searchfirst != 1 ? numgroupfiles-1 : 0; k >= 0; --k)
{
if (groupfil[k] < 0)
continue;
for (bssize_t i = gnumfiles[k]-1; i >= 0; --i)
{
char const * const gfileptr = (char *)&gfilelist[k][i<<4];
unsigned int j;
for (j = 0; j < 13; ++j)
{
if (!filename[j]) break;
if (toupperlookup[filename[j]] != toupperlookup[gfileptr[j]])
goto gnumfiles_continue;
}
if (j<13 && gfileptr[j]) continue; // JBF: because e1l1.map might exist before e1l1
if (j==13 && filename[j]) continue; // JBF: long file name
arraygrp[newhandle] = k;
arrayhan[newhandle] = i;
arraypos[newhandle] = 0;
return newhandle;
gnumfiles_continue: ;
}
}
return -1;
}
void krename(int32_t crcval, int32_t filenum, const char *newname)
{
Bstrncpy((char *)&gfilelist[crcval][filenum<<4], newname, 12);
}
char const * kfileparent(int32_t const handle)
{
int32_t const groupnum = filegrp[handle];
if ((unsigned)groupnum >= MAXGROUPFILES || groupfil[groupnum] == -1)
return NULL;
return groupname[groupnum];
}
int32_t kopen4load(const char *filename, char searchfirst)
{
int32_t newhandle = MAXOPENFILES-1;
if (filename==NULL)
return -1;
while (filehan[newhandle] != -1)
{
newhandle--;
if (newhandle < 0)
{
initprintf("TOO MANY FILES OPEN IN FILE GROUPING SYSTEM!");
Bexit(0);
}
}
char *lastpfn = NULL;
int32_t h = kopen_internal(filename, &lastpfn, searchfirst, 1, 1, newhandle, filegrp, filehan, filepos);
Xfree(lastpfn);
return h;
}
char g_modDir[BMAX_PATH] = "/";
buildvfs_kfd kopen4loadfrommod(const char *fileName, char searchfirst)
{
buildvfs_kfd kFile = buildvfs_kfd_invalid;
if (g_modDir[0] != '/' || g_modDir[1] != 0)
{
static char staticFileName[BMAX_PATH];
Bsnprintf(staticFileName, sizeof(staticFileName), "%s/%s", g_modDir, fileName);
kFile = kopen4load(staticFileName, searchfirst);
}
return (kFile == buildvfs_kfd_invalid) ? kopen4load(fileName, searchfirst) : kFile;
}
int32_t kread_internal(int32_t handle, void *buffer, int32_t leng, const uint8_t *arraygrp, const intptr_t *arrayhan, int32_t *arraypos)
{
int32_t filenum = arrayhan[handle];
int32_t groupnum = arraygrp[handle];
if (groupnum == GRP_FILESYSTEM) return Bread(filenum,buffer,leng);
#ifdef WITHKPLIB
else if (groupnum == GRP_ZIP)
{
if (kzcurhand != handle)
{
if (kztell() >= 0) { arraypos[kzcurhand] = kztell(); kzclose(); }
kzcurhand = handle;
kzipopen(filenamsav[handle]);
kzseek(arraypos[handle],SEEK_SET);
}
return kzread(buffer,leng);
}
#endif
if (EDUKE32_PREDICT_FALSE(groupfil[groupnum] == -1))
return 0;
int32_t rootgroupnum = groupnum;
int32_t i = 0;
while (groupfilgrp[rootgroupnum] != GRP_FILESYSTEM)
{
i += gfileoffs[groupfilgrp[rootgroupnum]][groupfil[rootgroupnum]];
rootgroupnum = groupfilgrp[rootgroupnum];
}
if (EDUKE32_PREDICT_TRUE(groupfil[rootgroupnum] != -1))
{
i += gfileoffs[groupnum][filenum]+arraypos[handle];
if (i != groupfilpos[rootgroupnum])
{
Blseek(groupfil[rootgroupnum],i,BSEEK_SET);
groupfilpos[rootgroupnum] = i;
}
leng = min(leng,(gfileoffs[groupnum][filenum+1]-gfileoffs[groupnum][filenum])-arraypos[handle]);
leng = Bread(groupfil[rootgroupnum],buffer,leng);
arraypos[handle] += leng;
groupfilpos[rootgroupnum] += leng;
return leng;
}
return 0;
}
int32_t klseek_internal(int32_t handle, int32_t offset, int32_t whence, const uint8_t *arraygrp, intptr_t *arrayhan, int32_t *arraypos)
{
int32_t const groupnum = arraygrp[handle];
if (groupnum == GRP_FILESYSTEM) return Blseek(arrayhan[handle],offset,whence);
#ifdef WITHKPLIB
else if (groupnum == GRP_ZIP)
{
if (kzcurhand != handle)
{
if (kztell() >= 0) { arraypos[kzcurhand] = kztell(); kzclose(); }
kzcurhand = handle;
kzipopen(filenamsav[handle]);
kzseek(arraypos[handle],SEEK_SET);
}
return kzseek(offset,whence);
}
#endif
if (groupfil[groupnum] != -1)
{
switch (whence)
{
case BSEEK_SET:
arraypos[handle] = offset; break;
case BSEEK_END:
{
int32_t const i = arrayhan[handle];
arraypos[handle] = (gfileoffs[groupnum][i+1]-gfileoffs[groupnum][i])+offset;
break;
}
case BSEEK_CUR:
arraypos[handle] += offset; break;
}
return arraypos[handle];
}
return -1;
}
int32_t kfilelength_internal(int32_t handle, const uint8_t *arraygrp, intptr_t *arrayhan, int32_t *arraypos)
{
int32_t const groupnum = arraygrp[handle];
if (groupnum == GRP_FILESYSTEM)
{
return buildvfs_length(arrayhan[handle]);
}
#ifdef WITHKPLIB
else if (groupnum == GRP_ZIP)
{
if (kzcurhand != handle)
{
if (kztell() >= 0) { arraypos[kzcurhand] = kztell(); kzclose(); }
kzcurhand = handle;
kzipopen(filenamsav[handle]);
kzseek(arraypos[handle],SEEK_SET);
}
return kzfilelength();
}
#endif
int32_t const i = arrayhan[handle];
return gfileoffs[groupnum][i+1]-gfileoffs[groupnum][i];
}
int32_t ktell_internal(int32_t handle, const uint8_t *arraygrp, intptr_t *arrayhan, int32_t *arraypos)
{
int32_t groupnum = arraygrp[handle];
if (groupnum == GRP_FILESYSTEM) return Blseek(arrayhan[handle],0,BSEEK_CUR);
#ifdef WITHKPLIB
else if (groupnum == GRP_ZIP)
{
if (kzcurhand != handle)
{
if (kztell() >= 0) { arraypos[kzcurhand] = kztell(); kzclose(); }
kzcurhand = handle;
kzipopen(filenamsav[handle]);
kzseek(arraypos[handle],SEEK_SET);
}
return kztell();
}
#endif
if (groupfil[groupnum] != -1)
return arraypos[handle];
return -1;
}
void kclose_internal(int32_t handle, const uint8_t *arraygrp, intptr_t *arrayhan)
{
if (handle < 0) return;
if (arraygrp[handle] == GRP_FILESYSTEM) Bclose(arrayhan[handle]);
#ifdef WITHKPLIB
else if (arraygrp[handle] == GRP_ZIP)
{
kzclose();
kzcurhand = -1;
}
#endif
arrayhan[handle] = -1;
}
int32_t kread(int32_t handle, void *buffer, int32_t leng)
{
return kread_internal(handle, buffer, leng, filegrp, filehan, filepos);
}
int32_t klseek(int32_t handle, int32_t offset, int32_t whence)
{
return klseek_internal(handle, offset, whence, filegrp, filehan, filepos);
}
int32_t kfilelength(int32_t handle)
{
return kfilelength_internal(handle, filegrp, filehan, filepos);
}
int32_t ktell(int32_t handle)
{
return ktell_internal(handle, filegrp, filehan, filepos);
}
void kclose(int32_t handle)
{
return kclose_internal(handle, filegrp, filehan);
}
static int32_t kread_grp(int32_t handle, void *buffer, int32_t leng)
{
return kread_internal(handle, buffer, leng, groupfilgrp, groupfil, groupfilpos);
}
static int32_t klseek_grp(int32_t handle, int32_t offset, int32_t whence)
{
return klseek_internal(handle, offset, whence, groupfilgrp, groupfil, groupfilpos);
}
static void kclose_grp(int32_t handle)
{
return kclose_internal(handle, groupfilgrp, groupfil);
}
#endif
int32_t klistaddentry(CACHE1D_FIND_REC **rec, const char *name, int32_t type, int32_t source)
{
CACHE1D_FIND_REC *r = NULL, *attach = NULL;
if (*rec)
{
int32_t insensitive, v;
CACHE1D_FIND_REC *last = NULL;
for (attach = *rec; attach; last = attach, attach = attach->next)
{
if (type == CACHE1D_FIND_DRIVE) continue; // we just want to get to the end for drives
#ifdef _WIN32
insensitive = 1;
#else
if (source == CACHE1D_SOURCE_GRP || attach->source == CACHE1D_SOURCE_GRP)
insensitive = 1;
else if (source == CACHE1D_SOURCE_ZIP || attach->source == CACHE1D_SOURCE_ZIP)
insensitive = 1;
else
{
extern int16_t editstatus; // XXX
insensitive = !editstatus;
}
// ^ in the game, don't show file list case-sensitive
#endif
if (insensitive) v = Bstrcasecmp(name, attach->name);
else v = Bstrcmp(name, attach->name);
// sorted list
if (v > 0) continue; // item to add is bigger than the current one
// so look for something bigger than us
if (v < 0) // item to add is smaller than the current one
{
attach = NULL; // so wedge it between the current item and the one before
break;
}
// matched
if (source >= attach->source) return 1; // item to add is of lower priority
r = attach;
break;
}
// wasn't found in the list, so attach to the end
if (!attach) attach = last;
}
if (r)
{
r->type = type;
r->source = source;
return 0;
}
r = (CACHE1D_FIND_REC *)Xmalloc(sizeof(CACHE1D_FIND_REC)+strlen(name)+1);
r->name = (char *)r + sizeof(CACHE1D_FIND_REC); strcpy(r->name, name);
r->type = type;
r->source = source;
r->usera = r->userb = NULL;
if (!attach) // we are the first item
{
r->prev = NULL;
r->next = *rec;
if (*rec)(*rec)->prev = r;
*rec = r;
}
else
{
r->prev = attach;
r->next = attach->next;
if (attach->next) attach->next->prev = r;
attach->next = r;
}
return 0;
}
void klistfree(CACHE1D_FIND_REC *rec)
{
CACHE1D_FIND_REC *n;
while (rec)
{
n = rec->next;
Xfree(rec);
rec = n;
}
}
CACHE1D_FIND_REC *klistpath(const char *_path, const char *mask, int32_t type)
{
CACHE1D_FIND_REC *rec = NULL;
char *path;
// pathsearchmode == 0: enumerates a path in the virtual filesystem
// pathsearchmode == 1: enumerates the system filesystem path passed in
path = Xstrdup(_path);
// we don't need any leading dots and slashes or trailing slashes either
{
int32_t i,j;
for (i=0; path[i] == '.' || toupperlookup[path[i]] == '/';) i++;
for (j=0; (path[j] = path[i]); j++,i++) ;
while (j>0 && toupperlookup[path[j-1]] == '/') j--;
path[j] = 0;
//initprintf("Cleaned up path = \"%s\"\n",path);
}
if (*path && (type & CACHE1D_FIND_DIR))
{
if (klistaddentry(&rec, "..", CACHE1D_FIND_DIR, CACHE1D_SOURCE_CURDIR) < 0)
{
Xfree(path);
klistfree(rec);
return NULL;
}
}
if (!(type & CACHE1D_OPT_NOSTACK)) // current directory and paths in the search stack
{
int32_t stackdepth = CACHE1D_SOURCE_CURDIR;
#ifdef USE_PHYSFS
char **rc = PHYSFS_enumerateFiles("");
char **i;
for (i = rc; *i != NULL; i++)
{
char * name = *i;
if ((name[0] == '.' && name[1] == 0) ||
(name[0] == '.' && name[1] == '.' && name[2] == 0))
continue;
bool const isdir = buildvfs_isdir(name);
if ((type & CACHE1D_FIND_DIR) && !isdir) continue;
if ((type & CACHE1D_FIND_FILE) && isdir) continue;
if (!Bwildmatch(name, mask)) continue;
switch (klistaddentry(&rec, name,
isdir ? CACHE1D_FIND_DIR : CACHE1D_FIND_FILE,
stackdepth))
{
case -1: goto failure;
//case 1: initprintf("%s:%s dropped for lower priority\n", d,dirent->name); break;
//case 0: initprintf("%s:%s accepted\n", d,dirent->name); break;
default:
break;
}
}
PHYSFS_freeList(rc);
#else
static const char *const CUR_DIR = "./";
// Adjusted for the following "autoload" dir fix - NY00123
searchpath_t *search = NULL;
const char *d = pathsearchmode ? _path : CUR_DIR;
char buf[BMAX_PATH];
BDIR *dir;
struct Bdirent *dirent;
do
{
if (d==CUR_DIR && (type & CACHE1D_FIND_NOCURDIR))
goto next;
strcpy(buf, d);
if (!pathsearchmode)
{
// Fix for "autoload" dir in multi-user environments - NY00123
strcat(buf, path);
if (*path) strcat(buf, "/");
}
dir = Bopendir(buf);
if (dir)
{
while ((dirent = Breaddir(dir)))
{
if ((dirent->name[0] == '.' && dirent->name[1] == 0) ||
(dirent->name[0] == '.' && dirent->name[1] == '.' && dirent->name[2] == 0))
continue;
if ((type & CACHE1D_FIND_DIR) && !(dirent->mode & BS_IFDIR)) continue;
if ((type & CACHE1D_FIND_FILE) && (dirent->mode & BS_IFDIR)) continue;
if (!Bwildmatch(dirent->name, mask)) continue;
switch (klistaddentry(&rec, dirent->name,
(dirent->mode & BS_IFDIR) ? CACHE1D_FIND_DIR : CACHE1D_FIND_FILE,
stackdepth))
{
case -1: goto failure;
//case 1: initprintf("%s:%s dropped for lower priority\n", d,dirent->name); break;
//case 0: initprintf("%s:%s accepted\n", d,dirent->name); break;
default:
break;
}
}
Bclosedir(dir);
}
next:
if (pathsearchmode)
break;
if (!search)
{
search = searchpathhead;
stackdepth = CACHE1D_SOURCE_PATH;
}
else
{
search = search->next;
stackdepth++;
}
if (search)
d = search->path;
}
while (search);
#endif
}
#ifndef USE_PHYSFS
#ifdef WITHKPLIB
if (!(type & CACHE1D_FIND_NOCURDIR)) // TEMP, until we have sorted out fs.listpath() API
if (!pathsearchmode) // next, zip files
{
char buf[BMAX_PATH+4];
int32_t i, j, ftype;
strcpy(buf,path);
if (*path) strcat(buf,"/");
strcat(buf,mask);
for (kzfindfilestart(buf); kzfindfile(buf);)
{
if (buf[0] != '|') continue; // local files we don't need
// scan for the end of the string and shift
// everything left a char in the process
for (i=1; (buf[i-1]=buf[i]); i++)
{
/* do nothing */
}
i-=2;
if (i < 0)
i = 0;
// if there's a slash at the end, this is a directory entry
if (toupperlookup[buf[i]] == '/') { ftype = CACHE1D_FIND_DIR; buf[i] = 0; }
else ftype = CACHE1D_FIND_FILE;
// skip over the common characters at the beginning of the base path and the zip entry
for (j=0; buf[j] && path[j]; j++)
{
if (toupperlookup[ path[j] ] == toupperlookup[ buf[j] ]) continue;
break;
}
// we've now hopefully skipped the common path component at the beginning.
// if that's true, we should be staring at a null byte in path and either any character in buf
// if j==0, or a slash if j>0
if ((!path[0] && buf[j]) || (!path[j] && toupperlookup[ buf[j] ] == '/'))
{
if (j>0) j++;
// yep, so now we shift what follows back to the start of buf and while we do that,
// keep an eye out for any more slashes which would mean this entry has sub-entities
// and is useless to us.
for (i = 0; (buf[i] = buf[j]) && toupperlookup[buf[j]] != '/'; i++,j++) ;
if (toupperlookup[buf[j]] == '/') continue; // damn, try next entry
}
else
{
// if we're here it means we have a situation where:
// path = foo
// buf = foobar...
// or
// path = foobar
// buf = foo...
// which would mean the entry is higher up in the directory tree and is also useless
continue;
}
if ((type & CACHE1D_FIND_DIR) && ftype != CACHE1D_FIND_DIR) continue;
if ((type & CACHE1D_FIND_FILE) && ftype != CACHE1D_FIND_FILE) continue;
// the entry is in the clear
switch (klistaddentry(&rec, buf, ftype, CACHE1D_SOURCE_ZIP))
{
case -1:
goto failure;
//case 1: initprintf("<ZIP>:%s dropped for lower priority\n", buf); break;
//case 0: initprintf("<ZIP>:%s accepted\n", buf); break;
default:
break;
}
}
}
#endif
// then, grp files
if (!(type & CACHE1D_FIND_NOCURDIR)) // TEMP, until we have sorted out fs.listpath() API
if (!pathsearchmode && !*path && (type & CACHE1D_FIND_FILE))
{
char buf[13];
int32_t i,j;
buf[12] = 0;
for (i=0; i<MAXGROUPFILES; i++)
{
if (groupfil[i] == -1) continue;
for (j=gnumfiles[i]-1; j>=0; j--)
{
Bmemcpy(buf,&gfilelist[i][j<<4],12);
if (!Bwildmatch(buf,mask)) continue;
switch (klistaddentry(&rec, buf, CACHE1D_FIND_FILE, CACHE1D_SOURCE_GRP))
{
case -1:
goto failure;
//case 1: initprintf("<GRP>:%s dropped for lower priority\n", workspace); break;
//case 0: initprintf("<GRP>:%s accepted\n", workspace); break;
default:
break;
}
}
}
}
#endif
if (pathsearchmode && (type & CACHE1D_FIND_DRIVE))
{
char *drives, *drp;
drives = Bgetsystemdrives();
if (drives)
{
for (drp=drives; *drp; drp+=strlen(drp)+1)
{
if (klistaddentry(&rec, drp, CACHE1D_FIND_DRIVE, CACHE1D_SOURCE_DRIVE) < 0)
{
Xfree(drives);
goto failure;
}
}
Xfree(drives);
}
}
Xfree(path);
// XXX: may be NULL if no file was listed, and thus indistinguishable from
// an error condition.
return rec;
failure:
Xfree(path);
klistfree(rec);
return NULL;
}
static int32_t kdfread_func(intptr_t fil, void *outbuf, int32_t length)
{
return kread((buildvfs_kfd)fil, outbuf, length);
}
static void dfwrite_func(intptr_t fp, const void *inbuf, int32_t length)
{
buildvfs_fwrite(inbuf, length, 1, (buildvfs_FILE)fp);
}
// LZ4_COMPRESSION_ACCELERATION_VALUE can be tuned for performance/space trade-off
// (lower number = higher compression ratio, higher number = faster compression speed)
#define LZ4_COMPRESSION_ACCELERATION_VALUE 5
static char compressedDataStackBuf[131072];
int32_t lz4CompressionLevel = LZ4_COMPRESSION_ACCELERATION_VALUE;
int32_t kdfread_LZ4(void *buffer, int dasizeof, int count, buildvfs_kfd fil)
{
int32_t leng;
// read compressed data length
if (kread(fil, &leng, sizeof(leng)) != sizeof(leng))
return -1;
leng = B_LITTLE32(leng);
char *pCompressedData = compressedDataStackBuf;
if (leng > ARRAY_SSIZE(compressedDataStackBuf))
pCompressedData = (char *)Xaligned_alloc(16, leng);
if (kread(fil, pCompressedData, leng) != leng)
return -1;
int32_t decompressedLength = LZ4_decompress_safe(pCompressedData, (char*) buffer, leng, dasizeof*count);
if (pCompressedData != compressedDataStackBuf)
Xaligned_free(pCompressedData);
return decompressedLength/dasizeof;
}
void dfwrite_LZ4(const void *buffer, int dasizeof, int count, buildvfs_FILE fil)
{
char * pCompressedData = compressedDataStackBuf;
int const maxCompressedSize = LZ4_compressBound(dasizeof * count);
if (maxCompressedSize > ARRAY_SSIZE(compressedDataStackBuf))
pCompressedData = (char *)Xaligned_alloc(16, maxCompressedSize);
int const leng = LZ4_compress_fast((const char*) buffer, pCompressedData, dasizeof*count, maxCompressedSize, lz4CompressionLevel);
int const swleng = B_LITTLE32(leng);
buildvfs_fwrite(&swleng, sizeof(swleng), 1, fil);
buildvfs_fwrite(pCompressedData, leng, 1, fil);
if (pCompressedData != compressedDataStackBuf)
Xaligned_free(pCompressedData);
}