2006-04-23 06:44:19 +00:00
|
|
|
// "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
|
2012-03-12 04:47:04 +00:00
|
|
|
// by Jonathon Fowler (jf@jonof.id.au)
|
2006-04-23 06:44:19 +00:00
|
|
|
|
2012-11-11 17:57:13 +00:00
|
|
|
|
|
|
|
#ifdef CACHE1D_COMPRESS_ONLY
|
|
|
|
// Standalone libcache1d.so containing only the compression/decompression
|
|
|
|
// functions.
|
|
|
|
# include <stdint.h>
|
|
|
|
# include <stdio.h>
|
|
|
|
# include <string.h>
|
|
|
|
# include <stddef.h>
|
|
|
|
|
|
|
|
# define BFILE FILE
|
|
|
|
# define C1D_STATIC
|
|
|
|
# define B_LITTLE16(x) (x)
|
|
|
|
# define B_LITTLE32(x) (x)
|
|
|
|
# define Bmemset memset
|
|
|
|
# define Bmemcpy memcpy
|
|
|
|
# define bsize_t size_t
|
|
|
|
#else
|
|
|
|
// cache1d.o for EDuke32
|
|
|
|
# define C1D_STATIC static
|
|
|
|
|
2006-04-23 06:44:19 +00:00
|
|
|
#include "compat.h"
|
On Windows, check for case-mismatched file names on successful kopen4load().
When a file from the local file system is opened, its real file name is gotten
with SHGetFileInfo() and compared against the one that was passed. In the case
they're not identical, a warning is issued.
This is one step towards eliminating mismatched file names in DEFs etc., which
cause trouble on systems that look them up case-sensitively. However, it's not
perfect because the issue is trickier than it appears on first sight.
For one thing, this will only check the last (i.e. file) part in the path,
falsely accepting mismatched directory names. However for these, it reports
them ruthlessly, even for those names where the try-other-case hack (try all
uppercase, all lowercase) would find the correctly-cased file.
git-svn-id: https://svn.eduke32.com/eduke32@2692 1a8010ca-5511-0410-912e-c29ae57300e0
2012-05-25 15:23:55 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
// for FILENAME_CASE_CHECK
|
|
|
|
# include <shellapi.h>
|
|
|
|
#endif
|
2006-04-23 06:44:19 +00:00
|
|
|
#include "cache1d.h"
|
|
|
|
#include "pragmas.h"
|
|
|
|
#include "baselayer.h"
|
|
|
|
|
|
|
|
#ifdef WITHKPLIB
|
|
|
|
#include "kplib.h"
|
|
|
|
|
2006-04-24 19:04:22 +00:00
|
|
|
//Insert '|' in front of filename
|
|
|
|
//Doing this tells kzopen to load the file only if inside a .ZIP file
|
2010-10-17 14:49:39 +00:00
|
|
|
static intptr_t kzipopen(const char *filnam)
|
2006-04-23 06:44:19 +00:00
|
|
|
{
|
2009-01-09 09:29:17 +00:00
|
|
|
uint32_t i;
|
2009-07-29 21:56:56 +00:00
|
|
|
char newst[BMAX_PATH+8];
|
2006-04-23 06:44:19 +00:00
|
|
|
|
2006-04-24 19:04:22 +00:00
|
|
|
newst[0] = '|';
|
2009-02-19 16:47:54 +00:00
|
|
|
for (i=0; filnam[i] && (i < sizeof(newst)-2); i++) newst[i+1] = filnam[i];
|
2006-04-24 19:04:22 +00:00
|
|
|
newst[i+1] = 0;
|
|
|
|
return(kzopen(newst));
|
2006-04-23 06:44:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
// 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
|
2009-01-09 09:29:17 +00:00
|
|
|
// Call initcache(int32_t cachestart, int32_t cachesize) where
|
2006-04-23 06:44:19 +00:00
|
|
|
//
|
2008-02-16 22:27:08 +00:00
|
|
|
// cachestart = (intptr_t)(pointer to start of BIG buffer)
|
2006-04-23 06:44:19 +00:00
|
|
|
// cachesize = length of BIG buffer
|
|
|
|
//
|
2009-01-09 09:29:17 +00:00
|
|
|
// Step 2: Call allocache(intptr_t *bufptr, int32_t bufsiz, char *lockptr)
|
2006-04-23 06:44:19 +00:00
|
|
|
// whenever you need to allocate a buffer, where:
|
|
|
|
//
|
2008-02-16 22:27:08 +00:00
|
|
|
// *bufptr = pointer to multi-byte pointer to buffer
|
2006-04-23 06:44:19 +00:00
|
|
|
// Confused? Using this method, cache2d can remove
|
|
|
|
// previously allocated things from the cache safely by
|
2008-02-16 22:27:08 +00:00
|
|
|
// setting the multi-byte pointer to 0.
|
2006-04-23 06:44:19 +00:00
|
|
|
// 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.
|
|
|
|
|
2012-02-18 17:57:07 +00:00
|
|
|
// Uncomment for easier allocache-allocated bound checking (e.g. with Valgrind)
|
|
|
|
//#define DEBUG_ALLOCACHE_AS_MALLOC
|
|
|
|
|
2006-04-23 06:44:19 +00:00
|
|
|
#define MAXCACHEOBJECTS 9216
|
|
|
|
|
2012-03-10 21:21:20 +00:00
|
|
|
#ifndef DEBUG_ALLOCACHE_AS_MALLOC
|
2009-01-09 09:29:17 +00:00
|
|
|
static int32_t cachesize = 0;
|
2012-03-10 21:21:20 +00:00
|
|
|
//static int32_t cachecount = 0;
|
|
|
|
static char zerochar = 0;
|
|
|
|
static intptr_t cachestart = 0;
|
|
|
|
static int32_t agecount = 0;
|
2009-01-09 09:29:17 +00:00
|
|
|
static int32_t lockrecip[200];
|
2012-03-10 21:21:20 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
int32_t cacnum = 0;
|
|
|
|
cactype cac[MAXCACHEOBJECTS];
|
2006-04-23 06:44:19 +00:00
|
|
|
|
2012-11-05 02:49:08 +00:00
|
|
|
static char 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
|
|
|
|
};
|
2006-04-23 06:44:19 +00:00
|
|
|
|
2011-02-21 23:08:21 +00:00
|
|
|
static void reportandexit(const char *errormessage);
|
2006-04-23 06:44:19 +00:00
|
|
|
|
|
|
|
|
2009-01-09 09:29:17 +00:00
|
|
|
void initcache(intptr_t dacachestart, int32_t dacachesize)
|
2006-04-23 06:44:19 +00:00
|
|
|
{
|
2012-03-10 21:21:20 +00:00
|
|
|
#ifndef DEBUG_ALLOCACHE_AS_MALLOC
|
2009-01-09 09:29:17 +00:00
|
|
|
int32_t i;
|
2006-04-23 06:44:19 +00:00
|
|
|
|
2009-02-19 16:47:54 +00:00
|
|
|
for (i=1; i<200; i++) lockrecip[i] = (1<<28)/(200-i);
|
2006-04-23 06:44:19 +00:00
|
|
|
|
2008-02-16 22:27:08 +00:00
|
|
|
// The following code was relocated here from engine.c, since this
|
|
|
|
// function is only ever called once (from there), and it seems to
|
|
|
|
// really belong here:
|
|
|
|
//
|
|
|
|
// initcache((FP_OFF(pic)+15)&0xfffffff0,(cachesize-((-FP_OFF(pic))&15))&0xfffffff0);
|
|
|
|
//
|
|
|
|
// I'm not sure why it's necessary, but the code is making sure the
|
2009-11-14 02:30:47 +00:00
|
|
|
// cache starts on a multiple of 16 bytes? -- SA
|
2008-02-16 22:27:08 +00:00
|
|
|
|
|
|
|
//printf("BEFORE: cachestart = %x, cachesize = %d\n", dacachestart, dacachesize);
|
|
|
|
cachestart = ((uintptr_t)dacachestart+15)&~(uintptr_t)0xf;
|
2009-04-26 05:57:42 +00:00
|
|
|
cachesize = (dacachesize-(((uintptr_t)(dacachestart))&0xf))&~(uintptr_t)0xf;
|
2008-02-16 22:27:08 +00:00
|
|
|
//printf("AFTER : cachestart = %x, cachesize = %d\n", cachestart, cachesize);
|
2006-04-23 06:44:19 +00:00
|
|
|
|
2006-04-24 19:04:22 +00:00
|
|
|
cac[0].leng = cachesize;
|
|
|
|
cac[0].lock = &zerochar;
|
|
|
|
cacnum = 1;
|
2006-04-23 06:44:19 +00:00
|
|
|
|
2008-12-10 11:36:53 +00:00
|
|
|
initprintf("Initialized %.1fM cache\n", (float)(dacachesize/1024.f/1024.f));
|
2012-03-10 21:21:20 +00:00
|
|
|
#else
|
|
|
|
UNREFERENCED_PARAMETER(dacachestart);
|
|
|
|
UNREFERENCED_PARAMETER(dacachesize);
|
|
|
|
#endif
|
2006-04-23 06:44:19 +00:00
|
|
|
}
|
|
|
|
|
2012-02-18 17:57:07 +00:00
|
|
|
#ifdef DEBUG_ALLOCACHE_AS_MALLOC
|
|
|
|
void allocache(intptr_t *newhandle, int32_t newbytes, char *newlockptr)
|
|
|
|
{
|
|
|
|
UNREFERENCED_PARAMETER(newlockptr);
|
|
|
|
|
|
|
|
*newhandle = (intptr_t)Bmalloc(newbytes);
|
|
|
|
if (!*newhandle)
|
|
|
|
reportandexit("OUT OF MEMORY in allocache as malloc wrapper!");
|
|
|
|
}
|
|
|
|
#else
|
2009-01-09 09:29:17 +00:00
|
|
|
void allocache(intptr_t *newhandle, int32_t newbytes, char *newlockptr)
|
2006-04-23 06:44:19 +00:00
|
|
|
{
|
2009-01-09 09:29:17 +00:00
|
|
|
int32_t i, /*j,*/ z, zz, bestz=0, daval, bestval, besto=0, o1, o2, sucklen, suckz;
|
2006-04-24 19:04:22 +00:00
|
|
|
|
2008-02-16 22:27:08 +00:00
|
|
|
//printf(" ==> asking for %d bytes, ", newbytes);
|
|
|
|
// Make all requests a multiple of 16 bytes
|
|
|
|
newbytes = (newbytes+15)&0xfffffff0;
|
|
|
|
//printf("allocated %d bytes\n", newbytes);
|
2008-05-10 01:29:37 +00:00
|
|
|
|
2006-04-24 19:04:22 +00:00
|
|
|
if ((unsigned)newbytes > (unsigned)cachesize)
|
|
|
|
{
|
2007-12-12 17:42:14 +00:00
|
|
|
Bprintf("Cachesize: %d\n",cachesize);
|
2008-06-11 02:33:23 +00:00
|
|
|
Bprintf("*Newhandle: 0x%" PRIxPTR ", Newbytes: %d, *Newlock: %d\n",(intptr_t)newhandle,newbytes,*newlockptr);
|
2006-04-24 19:04:22 +00:00
|
|
|
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;
|
2009-02-19 16:47:54 +00:00
|
|
|
for (z=cacnum-1; z>=0; z--)
|
2006-04-24 19:04:22 +00:00
|
|
|
{
|
|
|
|
o1 -= cac[z].leng;
|
|
|
|
o2 = o1+newbytes; if (o2 > cachesize) continue;
|
|
|
|
|
|
|
|
daval = 0;
|
2009-02-19 16:47:54 +00:00
|
|
|
for (i=o1,zz=z; i<o2; i+=cac[zz++].leng)
|
2006-04-24 19:04:22 +00:00
|
|
|
{
|
|
|
|
if (*cac[zz].lock == 0) continue;
|
2007-12-12 17:42:14 +00:00
|
|
|
if (*cac[zz].lock >= 200) { daval = 0x7fffffff; break; }
|
2006-04-24 19:04:22 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-12-12 17:42:14 +00:00
|
|
|
//printf("%d %d %d\n",besto,newbytes,*newlockptr);
|
2006-04-24 19:04:22 +00:00
|
|
|
|
|
|
|
if (bestval == 0x7fffffff)
|
|
|
|
reportandexit("CACHE SPACE ALL LOCKED UP!");
|
|
|
|
|
|
|
|
//Suck things out
|
2009-02-19 16:47:54 +00:00
|
|
|
for (sucklen=-newbytes,suckz=bestz; sucklen<0; sucklen+=cac[suckz++].leng)
|
2006-04-24 19:04:22 +00:00
|
|
|
if (*cac[suckz].lock) *cac[suckz].hand = 0;
|
|
|
|
|
|
|
|
//Remove all blocks except 1
|
|
|
|
suckz -= (bestz+1); cacnum -= suckz;
|
2012-02-18 22:14:45 +00:00
|
|
|
// copybufbyte(&cac[bestz+suckz],&cac[bestz],(cacnum-bestz)*sizeof(cactype));
|
|
|
|
Bmemmove(&cac[bestz], &cac[bestz+suckz], (cacnum-bestz)*sizeof(cactype));
|
2008-02-16 22:27:08 +00:00
|
|
|
cac[bestz].hand = newhandle; *newhandle = cachestart+(intptr_t)besto;
|
2006-04-24 19:04:22 +00:00
|
|
|
cac[bestz].leng = newbytes;
|
|
|
|
cac[bestz].lock = newlockptr;
|
2012-03-10 21:21:20 +00:00
|
|
|
// cachecount++;
|
2006-04-24 19:04:22 +00:00
|
|
|
|
|
|
|
//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;
|
|
|
|
}
|
|
|
|
|
2007-12-12 17:42:14 +00:00
|
|
|
if (*cac[bestz].lock == 0) { cac[bestz].leng += sucklen; return; }
|
2006-04-24 19:04:22 +00:00
|
|
|
|
|
|
|
cacnum++; if (cacnum > MAXCACHEOBJECTS) reportandexit("Too many objects in cache! (cacnum > MAXCACHEOBJECTS)");
|
2009-02-19 16:47:54 +00:00
|
|
|
for (z=cacnum-1; z>bestz; z--) cac[z] = cac[z-1];
|
2006-04-24 19:04:22 +00:00
|
|
|
cac[bestz].leng = sucklen;
|
|
|
|
cac[bestz].lock = &zerochar;
|
2006-04-23 06:44:19 +00:00
|
|
|
}
|
2012-02-18 17:57:07 +00:00
|
|
|
#endif
|
2006-04-23 06:44:19 +00:00
|
|
|
|
|
|
|
void agecache(void)
|
|
|
|
{
|
2012-02-18 17:57:07 +00:00
|
|
|
#ifndef DEBUG_ALLOCACHE_AS_MALLOC
|
2010-01-21 10:02:04 +00:00
|
|
|
int32_t cnt = (cacnum>>4);
|
2006-04-24 19:04:22 +00:00
|
|
|
|
|
|
|
if (agecount >= cacnum) agecount = cacnum-1;
|
2010-01-21 10:02:04 +00:00
|
|
|
if (agecount < 0 || !cnt) return;
|
2010-06-22 21:50:01 +00:00
|
|
|
|
2010-01-21 10:02:04 +00:00
|
|
|
for (; cnt>=0; cnt--)
|
2006-04-24 19:04:22 +00:00
|
|
|
{
|
2010-06-07 09:03:16 +00:00
|
|
|
if (cac[agecount].lock && (((*cac[agecount].lock)-2)&255) < 198)
|
2010-01-21 10:02:04 +00:00
|
|
|
(*cac[agecount].lock)--;
|
2006-04-24 19:04:22 +00:00
|
|
|
|
2010-01-21 10:02:04 +00:00
|
|
|
agecount--;
|
|
|
|
if (agecount < 0) agecount = cacnum-1;
|
2006-04-24 19:04:22 +00:00
|
|
|
}
|
2012-02-18 17:57:07 +00:00
|
|
|
#endif
|
2006-04-23 06:44:19 +00:00
|
|
|
}
|
|
|
|
|
2011-02-21 23:08:21 +00:00
|
|
|
static void reportandexit(const char *errormessage)
|
2006-04-23 06:44:19 +00:00
|
|
|
{
|
2012-02-18 17:57:07 +00:00
|
|
|
#ifndef DEBUG_ALLOCACHE_AS_MALLOC
|
2009-01-09 09:29:17 +00:00
|
|
|
int32_t i, j;
|
2006-04-24 19:04:22 +00:00
|
|
|
|
|
|
|
//setvmode(0x3);
|
|
|
|
j = 0;
|
2009-02-19 16:47:54 +00:00
|
|
|
for (i=0; i<cacnum; i++)
|
2006-04-24 19:04:22 +00:00
|
|
|
{
|
2007-12-12 17:42:14 +00:00
|
|
|
Bprintf("%d- ",i);
|
2008-06-11 02:33:23 +00:00
|
|
|
if (cac[i].hand) Bprintf("ptr: 0x%" PRIxPTR ", ",*cac[i].hand);
|
2006-04-24 19:04:22 +00:00
|
|
|
else Bprintf("ptr: NULL, ");
|
2007-12-12 17:42:14 +00:00
|
|
|
Bprintf("leng: %d, ",cac[i].leng);
|
2006-04-24 19:04:22 +00:00
|
|
|
if (cac[i].lock) Bprintf("lock: %d\n",*cac[i].lock);
|
|
|
|
else Bprintf("lock: NULL\n");
|
|
|
|
j += cac[i].leng;
|
|
|
|
}
|
2007-12-12 17:42:14 +00:00
|
|
|
Bprintf("Cachesize = %d\n",cachesize);
|
|
|
|
Bprintf("Cacnum = %d\n",cacnum);
|
|
|
|
Bprintf("Cache length sum = %d\n",j);
|
2012-02-18 17:57:07 +00:00
|
|
|
#endif
|
2006-04-24 19:04:22 +00:00
|
|
|
initprintf("ERROR: %s\n",errormessage);
|
2012-02-18 17:57:07 +00:00
|
|
|
exit(1);
|
2006-04-23 06:44:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
2007-12-12 17:42:14 +00:00
|
|
|
typedef struct _searchpath
|
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
struct _searchpath *next;
|
|
|
|
char *path;
|
|
|
|
size_t pathlen; // to save repeated calls to strlen()
|
2006-04-23 06:44:19 +00:00
|
|
|
} searchpath_t;
|
|
|
|
static searchpath_t *searchpathhead = NULL;
|
|
|
|
static size_t maxsearchpathlen = 0;
|
2009-01-09 09:29:17 +00:00
|
|
|
int32_t pathsearchmode = 0;
|
2006-04-23 06:44:19 +00:00
|
|
|
|
2012-10-07 15:26:09 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-01-09 09:29:17 +00:00
|
|
|
int32_t addsearchpath(const char *p)
|
2006-04-23 06:44:19 +00:00
|
|
|
{
|
2012-11-15 14:27:57 +00:00
|
|
|
struct Bstat st;
|
2006-04-24 19:04:22 +00:00
|
|
|
char *s;
|
|
|
|
searchpath_t *srch;
|
2013-03-27 01:39:18 +00:00
|
|
|
char *path = Bstrdup(p);
|
2006-04-24 19:04:22 +00:00
|
|
|
|
2013-03-27 01:39:18 +00:00
|
|
|
if (path[Bstrlen(path)-1] == '\\')
|
2013-03-28 09:05:36 +00:00
|
|
|
path[Bstrlen(path)-1] = 0; // hack for stat() returning ENOENT on paths ending in a backslash
|
2013-03-27 01:39:18 +00:00
|
|
|
|
|
|
|
if (Bstat(path, &st) < 0)
|
2007-12-12 17:42:14 +00:00
|
|
|
{
|
2013-03-27 01:39:18 +00:00
|
|
|
Bfree(path);
|
2006-04-24 19:04:22 +00:00
|
|
|
if (errno == ENOENT) return -2;
|
|
|
|
return -1;
|
|
|
|
}
|
2013-03-27 01:39:18 +00:00
|
|
|
if (!(st.st_mode & BS_IFDIR))
|
|
|
|
{
|
|
|
|
Bfree(path);
|
|
|
|
return -1;
|
|
|
|
}
|
2006-04-24 19:04:22 +00:00
|
|
|
|
2011-01-16 02:50:27 +00:00
|
|
|
srch = (searchpath_t *)Bmalloc(sizeof(searchpath_t));
|
2013-03-27 01:39:18 +00:00
|
|
|
if (!srch)
|
|
|
|
{
|
|
|
|
Bfree(path);
|
|
|
|
return -1;
|
|
|
|
}
|
2006-04-24 19:04:22 +00:00
|
|
|
|
|
|
|
srch->next = searchpathhead;
|
2013-03-27 01:39:18 +00:00
|
|
|
srch->pathlen = Bstrlen(path)+1;
|
2011-01-16 02:50:27 +00:00
|
|
|
srch->path = (char *)Bmalloc(srch->pathlen + 1);
|
2007-12-12 17:42:14 +00:00
|
|
|
if (!srch->path)
|
|
|
|
{
|
2013-03-27 01:39:18 +00:00
|
|
|
Bfree(path);
|
2009-10-07 06:47:35 +00:00
|
|
|
Bfree(srch);
|
2006-04-24 19:04:22 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2012-03-04 20:15:22 +00:00
|
|
|
|
2013-03-27 01:39:18 +00:00
|
|
|
Bstrcpy(srch->path, path);
|
2013-04-05 07:48:20 +00:00
|
|
|
for (s=srch->path; *s; s++);
|
2012-03-04 20:15:22 +00:00
|
|
|
s--;
|
2013-04-05 07:48:20 +00:00
|
|
|
|
2012-03-04 20:15:22 +00:00
|
|
|
if (s<srch->path || toupperlookup[*s] != '/')
|
|
|
|
Bstrcat(srch->path, "/");
|
2006-04-24 19:04:22 +00:00
|
|
|
|
|
|
|
searchpathhead = srch;
|
2012-03-04 20:15:22 +00:00
|
|
|
if (srch->pathlen > maxsearchpathlen)
|
|
|
|
maxsearchpathlen = srch->pathlen;
|
2007-12-12 17:42:14 +00:00
|
|
|
|
2006-12-17 01:08:39 +00:00
|
|
|
Bcorrectfilename(srch->path,0);
|
2007-12-12 17:42:14 +00:00
|
|
|
|
2010-05-19 10:02:35 +00:00
|
|
|
initprintf("Using %s for game data\n", srch->path);
|
2006-04-24 19:04:22 +00:00
|
|
|
|
2013-03-27 01:39:18 +00:00
|
|
|
Bfree(path);
|
2006-04-24 19:04:22 +00:00
|
|
|
return 0;
|
2006-04-23 06:44:19 +00:00
|
|
|
}
|
|
|
|
|
2013-04-05 07:48:20 +00:00
|
|
|
int32_t removesearchpath(const char *p)
|
|
|
|
{
|
|
|
|
searchpath_t *srch;
|
|
|
|
char *s;
|
|
|
|
char *path = (char *)Bmalloc(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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Bfree(srch->path);
|
|
|
|
Bfree(srch);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Bfree(path);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-01-09 09:29:17 +00:00
|
|
|
int32_t findfrompath(const char *fn, char **where)
|
2006-04-23 06:44:19 +00:00
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
searchpath_t *sp;
|
|
|
|
char *pfn, *ffn;
|
2009-01-09 09:29:17 +00:00
|
|
|
int32_t allocsiz;
|
2006-04-24 19:04:22 +00:00
|
|
|
|
|
|
|
// pathsearchmode == 0: tests current dir and then the dirs of the path stack
|
|
|
|
// pathsearchmode == 1: tests fn without modification, then like for pathsearchmode == 0
|
|
|
|
|
2007-12-12 17:42:14 +00:00
|
|
|
if (pathsearchmode)
|
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
// test unmolested filename first
|
2007-12-12 17:42:14 +00:00
|
|
|
if (access(fn, F_OK) >= 0)
|
|
|
|
{
|
2009-10-07 06:47:35 +00:00
|
|
|
*where = Bstrdup(fn);
|
2006-04-24 19:04:22 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2012-05-25 15:23:58 +00:00
|
|
|
#ifndef _WIN32
|
2010-08-14 21:32:28 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
char *tfn = Bstrtolower(Bstrdup(fn));
|
|
|
|
|
|
|
|
if (access(tfn, F_OK) >= 0)
|
|
|
|
{
|
|
|
|
*where = tfn;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Bstrupr(tfn);
|
|
|
|
|
|
|
|
if (access(tfn, F_OK) >= 0)
|
|
|
|
{
|
|
|
|
*where = tfn;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Bfree(tfn);
|
|
|
|
}
|
2012-05-25 15:23:58 +00:00
|
|
|
#endif
|
2006-04-24 19:04:22 +00:00
|
|
|
}
|
|
|
|
|
2011-01-16 02:50:27 +00:00
|
|
|
for (pfn = (char *)fn; toupperlookup[*pfn] == '/'; pfn++);
|
2009-10-07 06:47:35 +00:00
|
|
|
ffn = Bstrdup(pfn);
|
2006-04-24 19:04:22 +00:00
|
|
|
if (!ffn) return -1;
|
|
|
|
Bcorrectfilename(ffn,0); // compress relative paths
|
|
|
|
|
|
|
|
allocsiz = max(maxsearchpathlen, 2); // "./" (aka. curdir)
|
|
|
|
allocsiz += strlen(ffn);
|
|
|
|
allocsiz += 1; // a nul
|
|
|
|
|
2009-10-07 06:47:35 +00:00
|
|
|
pfn = (char *)Bmalloc(allocsiz);
|
|
|
|
if (!pfn) { Bfree(ffn); return -1; }
|
2006-04-24 19:04:22 +00:00
|
|
|
|
|
|
|
strcpy(pfn, "./");
|
|
|
|
strcat(pfn, ffn);
|
2007-12-12 17:42:14 +00:00
|
|
|
if (access(pfn, F_OK) >= 0)
|
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
*where = pfn;
|
2009-10-07 06:47:35 +00:00
|
|
|
Bfree(ffn);
|
2006-04-24 19:04:22 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-12-12 17:42:14 +00:00
|
|
|
for (sp = searchpathhead; sp; sp = sp->next)
|
|
|
|
{
|
2010-05-16 22:53:08 +00:00
|
|
|
char *tfn = Bstrdup(ffn);
|
|
|
|
|
2006-04-24 19:04:22 +00:00
|
|
|
strcpy(pfn, sp->path);
|
|
|
|
strcat(pfn, ffn);
|
|
|
|
//initprintf("Trying %s\n", pfn);
|
2007-12-12 17:42:14 +00:00
|
|
|
if (access(pfn, F_OK) >= 0)
|
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
*where = pfn;
|
2009-10-07 06:47:35 +00:00
|
|
|
Bfree(ffn);
|
2010-05-16 22:53:08 +00:00
|
|
|
Bfree(tfn);
|
2010-08-14 21:32:28 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-05-25 15:23:58 +00:00
|
|
|
#ifndef _WIN32
|
2010-08-14 21:32:28 +00:00
|
|
|
//Check with all lowercase
|
|
|
|
strcpy(pfn, sp->path);
|
|
|
|
Bstrtolower(tfn);
|
|
|
|
strcat(pfn, tfn);
|
|
|
|
if (access(pfn, F_OK) >= 0)
|
|
|
|
{
|
|
|
|
*where = pfn;
|
|
|
|
Bfree(ffn);
|
|
|
|
Bfree(tfn);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//Check again with uppercase
|
|
|
|
strcpy(pfn, sp->path);
|
|
|
|
Bstrupr(tfn);
|
|
|
|
strcat(pfn, tfn);
|
|
|
|
if (access(pfn, F_OK) >= 0)
|
|
|
|
{
|
|
|
|
*where = pfn;
|
|
|
|
Bfree(ffn);
|
|
|
|
Bfree(tfn);
|
2006-04-24 19:04:22 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2012-05-25 15:23:58 +00:00
|
|
|
#endif
|
2010-05-16 22:53:08 +00:00
|
|
|
Bfree(tfn);
|
2006-04-24 19:04:22 +00:00
|
|
|
}
|
2012-05-25 15:23:58 +00:00
|
|
|
|
2009-10-07 06:47:35 +00:00
|
|
|
Bfree(pfn); Bfree(ffn);
|
2006-04-24 19:04:22 +00:00
|
|
|
return -1;
|
2006-04-23 06:44:19 +00:00
|
|
|
}
|
|
|
|
|
On Windows, check for case-mismatched file names on successful kopen4load().
When a file from the local file system is opened, its real file name is gotten
with SHGetFileInfo() and compared against the one that was passed. In the case
they're not identical, a warning is issued.
This is one step towards eliminating mismatched file names in DEFs etc., which
cause trouble on systems that look them up case-sensitively. However, it's not
perfect because the issue is trickier than it appears on first sight.
For one thing, this will only check the last (i.e. file) part in the path,
falsely accepting mismatched directory names. However for these, it reports
them ruthlessly, even for those names where the try-other-case hack (try all
uppercase, all lowercase) would find the correctly-cased file.
git-svn-id: https://svn.eduke32.com/eduke32@2692 1a8010ca-5511-0410-912e-c29ae57300e0
2012-05-25 15:23:55 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
# define FILENAME_CASE_CHECK
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef FILENAME_CASE_CHECK
|
|
|
|
// don't free pfn if !=0 AND we Bopen()'ed the file successfully
|
|
|
|
static int32_t dont_free_pfn;
|
|
|
|
static char *lastpfn;
|
|
|
|
#endif
|
|
|
|
|
2009-01-09 09:29:17 +00:00
|
|
|
int32_t openfrompath(const char *fn, int32_t flags, int32_t mode)
|
2006-04-23 06:44:19 +00:00
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
char *pfn;
|
2009-01-09 09:29:17 +00:00
|
|
|
int32_t h;
|
2006-04-23 06:44:19 +00:00
|
|
|
|
2006-04-24 19:04:22 +00:00
|
|
|
if (findfrompath(fn, &pfn) < 0) return -1;
|
2012-05-28 18:15:19 +00:00
|
|
|
|
2006-04-24 19:04:22 +00:00
|
|
|
h = Bopen(pfn, flags, mode);
|
On Windows, check for case-mismatched file names on successful kopen4load().
When a file from the local file system is opened, its real file name is gotten
with SHGetFileInfo() and compared against the one that was passed. In the case
they're not identical, a warning is issued.
This is one step towards eliminating mismatched file names in DEFs etc., which
cause trouble on systems that look them up case-sensitively. However, it's not
perfect because the issue is trickier than it appears on first sight.
For one thing, this will only check the last (i.e. file) part in the path,
falsely accepting mismatched directory names. However for these, it reports
them ruthlessly, even for those names where the try-other-case hack (try all
uppercase, all lowercase) would find the correctly-cased file.
git-svn-id: https://svn.eduke32.com/eduke32@2692 1a8010ca-5511-0410-912e-c29ae57300e0
2012-05-25 15:23:55 +00:00
|
|
|
#ifdef FILENAME_CASE_CHECK
|
|
|
|
if (h>=0 && dont_free_pfn)
|
|
|
|
lastpfn = pfn;
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
Bfree(pfn);
|
2006-04-23 06:44:19 +00:00
|
|
|
|
2006-04-24 19:04:22 +00:00
|
|
|
return h;
|
2006-04-23 06:44:19 +00:00
|
|
|
}
|
|
|
|
|
2011-01-16 02:50:27 +00:00
|
|
|
BFILE *fopenfrompath(const char *fn, const char *mode)
|
2006-04-23 06:44:19 +00:00
|
|
|
{
|
2009-01-09 09:29:17 +00:00
|
|
|
int32_t fh;
|
2006-04-24 19:04:22 +00:00
|
|
|
BFILE *h;
|
2009-01-09 09:29:17 +00:00
|
|
|
int32_t bmode = 0, smode = 0;
|
2006-04-24 19:04:22 +00:00
|
|
|
const char *c;
|
|
|
|
|
2007-12-12 17:42:14 +00:00
|
|
|
for (c=mode; c[0];)
|
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
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;
|
2006-04-23 06:44:19 +00:00
|
|
|
}
|
|
|
|
|
2009-01-09 09:29:17 +00:00
|
|
|
int32_t numgroupfiles = 0;
|
|
|
|
static int32_t gnumfiles[MAXGROUPFILES];
|
|
|
|
static int32_t groupfil[MAXGROUPFILES] = {-1,-1,-1,-1,-1,-1,-1,-1};
|
|
|
|
static int32_t groupfilpos[MAXGROUPFILES];
|
2006-04-23 06:44:19 +00:00
|
|
|
static char *gfilelist[MAXGROUPFILES];
|
2009-01-09 09:29:17 +00:00
|
|
|
static int32_t *gfileoffs[MAXGROUPFILES];
|
2006-04-23 06:44:19 +00:00
|
|
|
|
2012-11-15 21:09:56 +00:00
|
|
|
static char filegrp[MAXOPENFILES];
|
2009-01-09 09:29:17 +00:00
|
|
|
static int32_t filepos[MAXOPENFILES];
|
2008-05-23 10:00:43 +00:00
|
|
|
static intptr_t filehan[MAXOPENFILES] =
|
2007-12-12 17:42:14 +00:00
|
|
|
{
|
|
|
|
-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
|
|
|
|
};
|
2006-04-23 06:44:19 +00:00
|
|
|
#ifdef WITHKPLIB
|
|
|
|
static char filenamsav[MAXOPENFILES][260];
|
2009-01-09 09:29:17 +00:00
|
|
|
static int32_t kzcurhand = -1;
|
2012-11-15 21:09:56 +00:00
|
|
|
|
|
|
|
int32_t cache1d_file_fromzip(int32_t fil)
|
|
|
|
{
|
|
|
|
return (filegrp[fil] == 254);
|
|
|
|
}
|
2006-04-23 06:44:19 +00:00
|
|
|
#endif
|
|
|
|
|
2010-10-17 14:49:39 +00:00
|
|
|
int32_t initgroupfile(const char *filename)
|
2006-04-23 06:44:19 +00:00
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
char buf[16];
|
2009-01-09 09:29:17 +00:00
|
|
|
int32_t i, j, k;
|
2006-04-23 06:44:19 +00:00
|
|
|
#ifdef WITHKPLIB
|
2006-04-24 19:04:22 +00:00
|
|
|
char *zfn;
|
2006-11-13 23:12:47 +00:00
|
|
|
#endif
|
2006-04-23 06:44:19 +00:00
|
|
|
|
|
|
|
#ifdef _WIN32
|
2006-04-24 19:04:22 +00:00
|
|
|
// on Windows, translate all backslashes (0x5c) to forward slashes (0x2f)
|
|
|
|
toupperlookup[0x5c] = 0x2f;
|
2006-04-23 06:44:19 +00:00
|
|
|
#endif
|
2006-04-24 19:04:22 +00:00
|
|
|
|
2006-04-23 06:44:19 +00:00
|
|
|
#ifdef WITHKPLIB
|
2006-04-24 19:04:22 +00:00
|
|
|
if (findfrompath(filename, &zfn) < 0) return -1;
|
|
|
|
|
|
|
|
// check to see if the file passed is a ZIP and pass it on to kplib if it is
|
|
|
|
i = Bopen(zfn,BO_BINARY|BO_RDONLY,BS_IREAD);
|
2009-10-07 06:47:35 +00:00
|
|
|
if (i < 0) { Bfree(zfn); return -1; }
|
2006-04-24 19:04:22 +00:00
|
|
|
|
|
|
|
Bread(i, buf, 4);
|
2007-12-12 17:42:14 +00:00
|
|
|
if (buf[0] == 0x50 && buf[1] == 0x4B && buf[2] == 0x03 && buf[3] == 0x04)
|
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
Bclose(i);
|
|
|
|
j = kzaddstack(zfn);
|
2009-10-07 06:47:35 +00:00
|
|
|
Bfree(zfn);
|
2006-04-24 19:04:22 +00:00
|
|
|
return j;
|
|
|
|
}
|
2009-10-07 06:47:35 +00:00
|
|
|
Bfree(zfn);
|
2006-04-24 19:04:22 +00:00
|
|
|
|
|
|
|
if (numgroupfiles >= MAXGROUPFILES) return(-1);
|
|
|
|
|
|
|
|
Blseek(i,0,BSEEK_SET);
|
|
|
|
groupfil[numgroupfiles] = i;
|
2006-04-23 06:44:19 +00:00
|
|
|
#else
|
2006-04-24 19:04:22 +00:00
|
|
|
groupfil[numgroupfiles] = openfrompath(filename,BO_BINARY|BO_RDONLY,BS_IREAD);
|
|
|
|
if (groupfil[numgroupfiles] != -1)
|
2006-04-23 06:44:19 +00:00
|
|
|
#endif
|
2006-04-24 19:04:22 +00:00
|
|
|
{
|
|
|
|
groupfilpos[numgroupfiles] = 0;
|
|
|
|
Bread(groupfil[numgroupfiles],buf,16);
|
2012-03-10 21:21:20 +00:00
|
|
|
if (Bmemcmp(buf, "KenSilverman", 12))
|
2006-04-24 19:04:22 +00:00
|
|
|
{
|
|
|
|
Bclose(groupfil[numgroupfiles]);
|
|
|
|
groupfil[numgroupfiles] = -1;
|
|
|
|
return(-1);
|
|
|
|
}
|
2009-01-09 09:29:17 +00:00
|
|
|
gnumfiles[numgroupfiles] = B_LITTLE32(*((int32_t *)&buf[12]));
|
2006-04-24 19:04:22 +00:00
|
|
|
|
2010-01-16 20:17:33 +00:00
|
|
|
if ((gfilelist[numgroupfiles] = (char *)Bmalloc(gnumfiles[numgroupfiles]<<4)) == 0)
|
2012-03-10 21:21:20 +00:00
|
|
|
{
|
|
|
|
Bprintf("Not enough memory for file grouping system\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
2010-01-16 20:17:33 +00:00
|
|
|
if ((gfileoffs[numgroupfiles] = (int32_t *)Bmalloc((gnumfiles[numgroupfiles]+1)<<2)) == 0)
|
2012-03-10 21:21:20 +00:00
|
|
|
{
|
|
|
|
Bprintf("Not enough memory for file grouping system\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
2006-04-24 19:04:22 +00:00
|
|
|
|
|
|
|
Bread(groupfil[numgroupfiles],gfilelist[numgroupfiles],gnumfiles[numgroupfiles]<<4);
|
|
|
|
|
|
|
|
j = 0;
|
2009-02-19 16:47:54 +00:00
|
|
|
for (i=0; i<gnumfiles[numgroupfiles]; i++)
|
2006-04-24 19:04:22 +00:00
|
|
|
{
|
2009-01-09 09:29:17 +00:00
|
|
|
k = B_LITTLE32(*((int32_t *)&gfilelist[numgroupfiles][(i<<4)+12]));
|
2006-04-24 19:04:22 +00:00
|
|
|
gfilelist[numgroupfiles][(i<<4)+12] = 0;
|
|
|
|
gfileoffs[numgroupfiles][i] = j;
|
|
|
|
j += k;
|
|
|
|
}
|
|
|
|
gfileoffs[numgroupfiles][gnumfiles[numgroupfiles]] = j;
|
|
|
|
}
|
|
|
|
numgroupfiles++;
|
|
|
|
return(groupfil[numgroupfiles-1]);
|
2006-04-23 06:44:19 +00:00
|
|
|
}
|
|
|
|
|
2009-01-09 09:29:17 +00:00
|
|
|
void uninitsinglegroupfile(int32_t grphandle)
|
2006-04-23 06:44:19 +00:00
|
|
|
{
|
2009-01-09 09:29:17 +00:00
|
|
|
int32_t i, grpnum = -1;
|
2006-04-24 19:04:22 +00:00
|
|
|
|
2009-02-19 16:47:54 +00:00
|
|
|
for (i=numgroupfiles-1; i>=0; i--)
|
2006-04-24 19:04:22 +00:00
|
|
|
if (groupfil[i] != -1 && groupfil[i] == grphandle)
|
|
|
|
{
|
2010-01-16 20:17:33 +00:00
|
|
|
Bfree(gfilelist[i]);
|
|
|
|
Bfree(gfileoffs[i]);
|
2006-04-24 19:04:22 +00:00
|
|
|
Bclose(groupfil[i]);
|
|
|
|
groupfil[i] = -1;
|
|
|
|
grpnum = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (grpnum == -1) return;
|
|
|
|
|
|
|
|
// JBF 20040111
|
|
|
|
numgroupfiles--;
|
|
|
|
|
|
|
|
// move any group files following this one back
|
|
|
|
for (i=grpnum+1; i<MAXGROUPFILES; i++)
|
2007-12-12 17:42:14 +00:00
|
|
|
if (groupfil[i] != -1)
|
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
groupfil[i-1] = groupfil[i];
|
|
|
|
gnumfiles[i-1] = gnumfiles[i];
|
|
|
|
groupfilpos[i-1] = groupfilpos[i];
|
|
|
|
gfilelist[i-1] = gfilelist[i];
|
|
|
|
gfileoffs[i-1] = gfileoffs[i];
|
|
|
|
groupfil[i] = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// fix up the open files that need attention
|
2009-02-19 16:47:54 +00:00
|
|
|
for (i=0; i<MAXOPENFILES; i++)
|
2007-12-12 17:42:14 +00:00
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
if (filegrp[i] >= 254) // external file (255) or ZIPped file (254)
|
|
|
|
continue;
|
|
|
|
else if (filegrp[i] == grpnum) // close file in group we closed
|
|
|
|
filehan[i] = -1;
|
|
|
|
else if (filegrp[i] > grpnum) // move back a file in a group after the one we closed
|
|
|
|
filegrp[i]--;
|
|
|
|
}
|
2006-04-23 06:44:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void uninitgroupfile(void)
|
|
|
|
{
|
2009-01-09 09:29:17 +00:00
|
|
|
int32_t i;
|
2006-04-24 19:04:22 +00:00
|
|
|
|
2009-02-19 16:47:54 +00:00
|
|
|
for (i=numgroupfiles-1; i>=0; i--)
|
2006-04-24 19:04:22 +00:00
|
|
|
if (groupfil[i] != -1)
|
|
|
|
{
|
2010-01-16 20:17:33 +00:00
|
|
|
Bfree(gfilelist[i]);
|
|
|
|
Bfree(gfileoffs[i]);
|
2006-04-24 19:04:22 +00:00
|
|
|
Bclose(groupfil[i]);
|
|
|
|
groupfil[i] = -1;
|
|
|
|
}
|
|
|
|
numgroupfiles = 0;
|
|
|
|
|
|
|
|
// JBF 20040111: "close" any files open in groups
|
2009-02-19 16:47:54 +00:00
|
|
|
for (i=0; i<MAXOPENFILES; i++)
|
2007-12-12 17:42:14 +00:00
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
if (filegrp[i] < 254) // JBF 20040130: not external or ZIPped
|
|
|
|
filehan[i] = -1;
|
|
|
|
}
|
2006-04-23 06:44:19 +00:00
|
|
|
}
|
|
|
|
|
On Windows, check for case-mismatched file names on successful kopen4load().
When a file from the local file system is opened, its real file name is gotten
with SHGetFileInfo() and compared against the one that was passed. In the case
they're not identical, a warning is issued.
This is one step towards eliminating mismatched file names in DEFs etc., which
cause trouble on systems that look them up case-sensitively. However, it's not
perfect because the issue is trickier than it appears on first sight.
For one thing, this will only check the last (i.e. file) part in the path,
falsely accepting mismatched directory names. However for these, it reports
them ruthlessly, even for those names where the try-other-case hack (try all
uppercase, all lowercase) would find the correctly-cased file.
git-svn-id: https://svn.eduke32.com/eduke32@2692 1a8010ca-5511-0410-912e-c29ae57300e0
2012-05-25 15:23:55 +00:00
|
|
|
#ifdef FILENAME_CASE_CHECK
|
|
|
|
// See
|
|
|
|
// http://stackoverflow.com/questions/74451/getting-actual-file-name-with-proper-casing-on-windows
|
|
|
|
// for relevant discussion.
|
|
|
|
|
|
|
|
static SHFILEINFO shinf;
|
|
|
|
|
2012-05-28 18:15:19 +00:00
|
|
|
int32_t (*check_filename_casing_fn)(void) = NULL;
|
|
|
|
|
On Windows, check for case-mismatched file names on successful kopen4load().
When a file from the local file system is opened, its real file name is gotten
with SHGetFileInfo() and compared against the one that was passed. In the case
they're not identical, a warning is issued.
This is one step towards eliminating mismatched file names in DEFs etc., which
cause trouble on systems that look them up case-sensitively. However, it's not
perfect because the issue is trickier than it appears on first sight.
For one thing, this will only check the last (i.e. file) part in the path,
falsely accepting mismatched directory names. However for these, it reports
them ruthlessly, even for those names where the try-other-case hack (try all
uppercase, all lowercase) would find the correctly-cased file.
git-svn-id: https://svn.eduke32.com/eduke32@2692 1a8010ca-5511-0410-912e-c29ae57300e0
2012-05-25 15:23:55 +00:00
|
|
|
// -1: failure, 0: match, 1: mismatch
|
|
|
|
static int32_t check_filename_mismatch(const char *filename, int32_t ofs)
|
|
|
|
{
|
2012-05-28 18:15:19 +00:00
|
|
|
const char *fn = filename+ofs;
|
2012-10-13 14:53:26 +00:00
|
|
|
int32_t len;
|
2012-05-28 18:15:19 +00:00
|
|
|
|
On Windows, check for case-mismatched file names on successful kopen4load().
When a file from the local file system is opened, its real file name is gotten
with SHGetFileInfo() and compared against the one that was passed. In the case
they're not identical, a warning is issued.
This is one step towards eliminating mismatched file names in DEFs etc., which
cause trouble on systems that look them up case-sensitively. However, it's not
perfect because the issue is trickier than it appears on first sight.
For one thing, this will only check the last (i.e. file) part in the path,
falsely accepting mismatched directory names. However for these, it reports
them ruthlessly, even for those names where the try-other-case hack (try all
uppercase, all lowercase) would find the correctly-cased file.
git-svn-id: https://svn.eduke32.com/eduke32@2692 1a8010ca-5511-0410-912e-c29ae57300e0
2012-05-25 15:23:55 +00:00
|
|
|
// we assume that UNICODE is not #defined, winlayer.c errors out else
|
|
|
|
if (!SHGetFileInfo(filename, -1, &shinf, sizeof(shinf), SHGFI_DISPLAYNAME))
|
|
|
|
return -1;
|
|
|
|
|
2012-10-13 14:53:26 +00:00
|
|
|
len = Bstrlen(shinf.szDisplayName);
|
|
|
|
|
|
|
|
if (!Bstrncmp(shinf.szDisplayName, fn, len))
|
2012-05-28 18:15:19 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
{
|
|
|
|
char *tfn = Bstrtolower(Bstrdup(fn));
|
|
|
|
|
2012-10-13 14:53:26 +00:00
|
|
|
if (!Bstrncmp(shinf.szDisplayName, tfn, len))
|
2012-05-28 18:15:19 +00:00
|
|
|
{
|
|
|
|
Bfree(tfn);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Bstrupr(tfn);
|
|
|
|
|
2012-10-13 14:53:26 +00:00
|
|
|
if (!Bstrncmp(shinf.szDisplayName, tfn, len))
|
2012-05-28 18:15:19 +00:00
|
|
|
{
|
|
|
|
Bfree(tfn);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Bfree(tfn);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
On Windows, check for case-mismatched file names on successful kopen4load().
When a file from the local file system is opened, its real file name is gotten
with SHGetFileInfo() and compared against the one that was passed. In the case
they're not identical, a warning is issued.
This is one step towards eliminating mismatched file names in DEFs etc., which
cause trouble on systems that look them up case-sensitively. However, it's not
perfect because the issue is trickier than it appears on first sight.
For one thing, this will only check the last (i.e. file) part in the path,
falsely accepting mismatched directory names. However for these, it reports
them ruthlessly, even for those names where the try-other-case hack (try all
uppercase, all lowercase) would find the correctly-cased file.
git-svn-id: https://svn.eduke32.com/eduke32@2692 1a8010ca-5511-0410-912e-c29ae57300e0
2012-05-25 15:23:55 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2010-10-17 14:49:39 +00:00
|
|
|
int32_t kopen4load(const char *filename, char searchfirst)
|
2006-04-23 06:44:19 +00:00
|
|
|
{
|
2010-01-21 10:02:04 +00:00
|
|
|
int32_t j, k, fil, newhandle = MAXOPENFILES-1;
|
2006-04-24 19:04:22 +00:00
|
|
|
char bad, *gfileptr;
|
2008-05-23 10:00:43 +00:00
|
|
|
intptr_t i;
|
2006-04-24 19:04:22 +00:00
|
|
|
|
2012-05-28 18:15:19 +00:00
|
|
|
#ifdef FILENAME_CASE_CHECK
|
|
|
|
const int32_t do_case_check = check_filename_casing_fn && check_filename_casing_fn();
|
|
|
|
#endif
|
|
|
|
|
2011-01-16 00:23:39 +00:00
|
|
|
if (filename==NULL)
|
|
|
|
return -1;
|
|
|
|
|
2006-04-24 19:04:22 +00:00
|
|
|
while (filehan[newhandle] != -1)
|
|
|
|
{
|
|
|
|
newhandle--;
|
|
|
|
if (newhandle < 0)
|
|
|
|
{
|
|
|
|
Bprintf("TOO MANY FILES OPEN IN FILE GROUPING SYSTEM!");
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
On Windows, check for case-mismatched file names on successful kopen4load().
When a file from the local file system is opened, its real file name is gotten
with SHGetFileInfo() and compared against the one that was passed. In the case
they're not identical, a warning is issued.
This is one step towards eliminating mismatched file names in DEFs etc., which
cause trouble on systems that look them up case-sensitively. However, it's not
perfect because the issue is trickier than it appears on first sight.
For one thing, this will only check the last (i.e. file) part in the path,
falsely accepting mismatched directory names. However for these, it reports
them ruthlessly, even for those names where the try-other-case hack (try all
uppercase, all lowercase) would find the correctly-cased file.
git-svn-id: https://svn.eduke32.com/eduke32@2692 1a8010ca-5511-0410-912e-c29ae57300e0
2012-05-25 15:23:55 +00:00
|
|
|
#ifdef FILENAME_CASE_CHECK
|
2012-05-28 18:15:19 +00:00
|
|
|
dont_free_pfn = do_case_check;
|
On Windows, check for case-mismatched file names on successful kopen4load().
When a file from the local file system is opened, its real file name is gotten
with SHGetFileInfo() and compared against the one that was passed. In the case
they're not identical, a warning is issued.
This is one step towards eliminating mismatched file names in DEFs etc., which
cause trouble on systems that look them up case-sensitively. However, it's not
perfect because the issue is trickier than it appears on first sight.
For one thing, this will only check the last (i.e. file) part in the path,
falsely accepting mismatched directory names. However for these, it reports
them ruthlessly, even for those names where the try-other-case hack (try all
uppercase, all lowercase) would find the correctly-cased file.
git-svn-id: https://svn.eduke32.com/eduke32@2692 1a8010ca-5511-0410-912e-c29ae57300e0
2012-05-25 15:23:55 +00:00
|
|
|
#endif
|
|
|
|
|
2012-06-21 19:48:54 +00:00
|
|
|
if (searchfirst == 0 && (fil = openfrompath(filename,BO_BINARY|BO_RDONLY,BS_IREAD)) >= 0)
|
2010-01-21 10:02:04 +00:00
|
|
|
{
|
On Windows, check for case-mismatched file names on successful kopen4load().
When a file from the local file system is opened, its real file name is gotten
with SHGetFileInfo() and compared against the one that was passed. In the case
they're not identical, a warning is issued.
This is one step towards eliminating mismatched file names in DEFs etc., which
cause trouble on systems that look them up case-sensitively. However, it's not
perfect because the issue is trickier than it appears on first sight.
For one thing, this will only check the last (i.e. file) part in the path,
falsely accepting mismatched directory names. However for these, it reports
them ruthlessly, even for those names where the try-other-case hack (try all
uppercase, all lowercase) would find the correctly-cased file.
git-svn-id: https://svn.eduke32.com/eduke32@2692 1a8010ca-5511-0410-912e-c29ae57300e0
2012-05-25 15:23:55 +00:00
|
|
|
#ifdef FILENAME_CASE_CHECK
|
2012-05-28 18:15:19 +00:00
|
|
|
if (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);
|
|
|
|
|
|
|
|
dont_free_pfn = 0;
|
|
|
|
|
|
|
|
if (status == -1)
|
On Windows, check for case-mismatched file names on successful kopen4load().
When a file from the local file system is opened, its real file name is gotten
with SHGetFileInfo() and compared against the one that was passed. In the case
they're not identical, a warning is issued.
This is one step towards eliminating mismatched file names in DEFs etc., which
cause trouble on systems that look them up case-sensitively. However, it's not
perfect because the issue is trickier than it appears on first sight.
For one thing, this will only check the last (i.e. file) part in the path,
falsely accepting mismatched directory names. However for these, it reports
them ruthlessly, even for those names where the try-other-case hack (try all
uppercase, all lowercase) would find the correctly-cased file.
git-svn-id: https://svn.eduke32.com/eduke32@2692 1a8010ca-5511-0410-912e-c29ae57300e0
2012-05-25 15:23:55 +00:00
|
|
|
{
|
2013-01-08 23:12:53 +00:00
|
|
|
// initprintf("SHGetFileInfo failed with error code %lu\n", GetLastError());
|
2012-05-28 18:15:19 +00:00
|
|
|
}
|
|
|
|
else if (status == 1)
|
|
|
|
{
|
|
|
|
initprintf("warning: case mismatch: passed \"%s\", real \"%s\"\n",
|
|
|
|
lastslash, shinf.szDisplayName);
|
On Windows, check for case-mismatched file names on successful kopen4load().
When a file from the local file system is opened, its real file name is gotten
with SHGetFileInfo() and compared against the one that was passed. In the case
they're not identical, a warning is issued.
This is one step towards eliminating mismatched file names in DEFs etc., which
cause trouble on systems that look them up case-sensitively. However, it's not
perfect because the issue is trickier than it appears on first sight.
For one thing, this will only check the last (i.e. file) part in the path,
falsely accepting mismatched directory names. However for these, it reports
them ruthlessly, even for those names where the try-other-case hack (try all
uppercase, all lowercase) would find the correctly-cased file.
git-svn-id: https://svn.eduke32.com/eduke32@2692 1a8010ca-5511-0410-912e-c29ae57300e0
2012-05-25 15:23:55 +00:00
|
|
|
}
|
|
|
|
|
2012-05-28 18:15:19 +00:00
|
|
|
Bfree(lastpfn);
|
|
|
|
lastpfn=NULL;
|
On Windows, check for case-mismatched file names on successful kopen4load().
When a file from the local file system is opened, its real file name is gotten
with SHGetFileInfo() and compared against the one that was passed. In the case
they're not identical, a warning is issued.
This is one step towards eliminating mismatched file names in DEFs etc., which
cause trouble on systems that look them up case-sensitively. However, it's not
perfect because the issue is trickier than it appears on first sight.
For one thing, this will only check the last (i.e. file) part in the path,
falsely accepting mismatched directory names. However for these, it reports
them ruthlessly, even for those names where the try-other-case hack (try all
uppercase, all lowercase) would find the correctly-cased file.
git-svn-id: https://svn.eduke32.com/eduke32@2692 1a8010ca-5511-0410-912e-c29ae57300e0
2012-05-25 15:23:55 +00:00
|
|
|
}
|
|
|
|
#endif
|
2010-01-21 10:02:04 +00:00
|
|
|
filegrp[newhandle] = 255;
|
|
|
|
filehan[newhandle] = fil;
|
|
|
|
filepos[newhandle] = 0;
|
|
|
|
return(newhandle);
|
|
|
|
}
|
2006-04-24 19:04:22 +00:00
|
|
|
|
On Windows, check for case-mismatched file names on successful kopen4load().
When a file from the local file system is opened, its real file name is gotten
with SHGetFileInfo() and compared against the one that was passed. In the case
they're not identical, a warning is issued.
This is one step towards eliminating mismatched file names in DEFs etc., which
cause trouble on systems that look them up case-sensitively. However, it's not
perfect because the issue is trickier than it appears on first sight.
For one thing, this will only check the last (i.e. file) part in the path,
falsely accepting mismatched directory names. However for these, it reports
them ruthlessly, even for those names where the try-other-case hack (try all
uppercase, all lowercase) would find the correctly-cased file.
git-svn-id: https://svn.eduke32.com/eduke32@2692 1a8010ca-5511-0410-912e-c29ae57300e0
2012-05-25 15:23:55 +00:00
|
|
|
#ifdef FILENAME_CASE_CHECK
|
|
|
|
dont_free_pfn = 0;
|
|
|
|
#endif
|
|
|
|
|
2006-04-24 19:04:22 +00:00
|
|
|
for (; toupperlookup[*filename] == '/'; filename++);
|
|
|
|
|
2006-04-23 06:44:19 +00:00
|
|
|
#ifdef WITHKPLIB
|
2006-04-24 19:04:22 +00:00
|
|
|
if ((kzcurhand != newhandle) && (kztell() >= 0))
|
|
|
|
{
|
|
|
|
if (kzcurhand >= 0) filepos[kzcurhand] = kztell();
|
|
|
|
kzclose();
|
|
|
|
}
|
2007-12-12 17:42:14 +00:00
|
|
|
if (searchfirst != 1 && (i = kzipopen(filename)) != 0)
|
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
kzcurhand = newhandle;
|
|
|
|
filegrp[newhandle] = 254;
|
|
|
|
filehan[newhandle] = i;
|
|
|
|
filepos[newhandle] = 0;
|
|
|
|
strcpy(filenamsav[newhandle],filename);
|
|
|
|
return newhandle;
|
|
|
|
}
|
2006-04-23 06:44:19 +00:00
|
|
|
#endif
|
|
|
|
|
2009-02-19 16:47:54 +00:00
|
|
|
for (k=numgroupfiles-1; k>=0; k--)
|
2006-04-24 19:04:22 +00:00
|
|
|
{
|
|
|
|
if (searchfirst == 1) k = 0;
|
|
|
|
if (groupfil[k] >= 0)
|
|
|
|
{
|
2009-02-19 16:47:54 +00:00
|
|
|
for (i=gnumfiles[k]-1; i>=0; i--)
|
2006-04-24 19:04:22 +00:00
|
|
|
{
|
|
|
|
gfileptr = (char *)&gfilelist[k][i<<4];
|
|
|
|
|
|
|
|
bad = 0;
|
2009-02-19 16:47:54 +00:00
|
|
|
for (j=0; j<13; j++)
|
2006-04-24 19:04:22 +00:00
|
|
|
{
|
|
|
|
if (!filename[j]) break;
|
|
|
|
if (toupperlookup[filename[j]] != toupperlookup[gfileptr[j]])
|
2012-03-10 21:21:20 +00:00
|
|
|
{
|
|
|
|
bad = 1;
|
|
|
|
break;
|
|
|
|
}
|
2006-04-24 19:04:22 +00:00
|
|
|
}
|
|
|
|
if (bad) continue;
|
|
|
|
if (j<13 && gfileptr[j]) continue; // JBF: because e1l1.map might exist before e1l1
|
2010-01-21 10:02:04 +00:00
|
|
|
if (j==13 && filename[j]) continue; // JBF: long file name
|
2006-04-24 19:04:22 +00:00
|
|
|
|
|
|
|
filegrp[newhandle] = k;
|
|
|
|
filehan[newhandle] = i;
|
|
|
|
filepos[newhandle] = 0;
|
|
|
|
return(newhandle);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(-1);
|
2006-04-23 06:44:19 +00:00
|
|
|
}
|
|
|
|
|
2009-01-09 09:29:17 +00:00
|
|
|
int32_t kread(int32_t handle, void *buffer, int32_t leng)
|
2006-04-23 06:44:19 +00:00
|
|
|
{
|
2010-01-21 10:02:04 +00:00
|
|
|
int32_t i;
|
|
|
|
int32_t filenum = filehan[handle];
|
|
|
|
int32_t groupnum = filegrp[handle];
|
2006-04-23 06:44:19 +00:00
|
|
|
|
2006-04-24 19:04:22 +00:00
|
|
|
if (groupnum == 255) return(Bread(filenum,buffer,leng));
|
2006-04-23 06:44:19 +00:00
|
|
|
#ifdef WITHKPLIB
|
2006-04-24 19:04:22 +00:00
|
|
|
else if (groupnum == 254)
|
|
|
|
{
|
|
|
|
if (kzcurhand != handle)
|
|
|
|
{
|
|
|
|
if (kztell() >= 0) { filepos[kzcurhand] = kztell(); kzclose(); }
|
|
|
|
kzcurhand = handle;
|
|
|
|
kzipopen(filenamsav[handle]);
|
|
|
|
kzseek(filepos[handle],SEEK_SET);
|
|
|
|
}
|
|
|
|
return(kzread(buffer,leng));
|
|
|
|
}
|
2006-04-23 06:44:19 +00:00
|
|
|
#endif
|
|
|
|
|
2006-04-24 19:04:22 +00:00
|
|
|
if (groupfil[groupnum] != -1)
|
|
|
|
{
|
|
|
|
i = gfileoffs[groupnum][filenum]+filepos[handle];
|
|
|
|
if (i != groupfilpos[groupnum])
|
|
|
|
{
|
|
|
|
Blseek(groupfil[groupnum],i+((gnumfiles[groupnum]+1)<<4),BSEEK_SET);
|
|
|
|
groupfilpos[groupnum] = i;
|
|
|
|
}
|
|
|
|
leng = min(leng,(gfileoffs[groupnum][filenum+1]-gfileoffs[groupnum][filenum])-filepos[handle]);
|
|
|
|
leng = Bread(groupfil[groupnum],buffer,leng);
|
|
|
|
filepos[handle] += leng;
|
|
|
|
groupfilpos[groupnum] += leng;
|
|
|
|
return(leng);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(0);
|
2006-04-23 06:44:19 +00:00
|
|
|
}
|
|
|
|
|
2009-01-09 09:29:17 +00:00
|
|
|
int32_t klseek(int32_t handle, int32_t offset, int32_t whence)
|
2006-04-23 06:44:19 +00:00
|
|
|
{
|
2009-01-09 09:29:17 +00:00
|
|
|
int32_t i, groupnum;
|
2006-04-23 06:44:19 +00:00
|
|
|
|
2006-04-24 19:04:22 +00:00
|
|
|
groupnum = filegrp[handle];
|
2006-04-23 06:44:19 +00:00
|
|
|
|
2006-04-24 19:04:22 +00:00
|
|
|
if (groupnum == 255) return(Blseek(filehan[handle],offset,whence));
|
2006-04-23 06:44:19 +00:00
|
|
|
#ifdef WITHKPLIB
|
2006-04-24 19:04:22 +00:00
|
|
|
else if (groupnum == 254)
|
|
|
|
{
|
|
|
|
if (kzcurhand != handle)
|
|
|
|
{
|
|
|
|
if (kztell() >= 0) { filepos[kzcurhand] = kztell(); kzclose(); }
|
|
|
|
kzcurhand = handle;
|
|
|
|
kzipopen(filenamsav[handle]);
|
|
|
|
kzseek(filepos[handle],SEEK_SET);
|
|
|
|
}
|
|
|
|
return(kzseek(offset,whence));
|
|
|
|
}
|
2006-04-23 06:44:19 +00:00
|
|
|
#endif
|
|
|
|
|
2006-04-24 19:04:22 +00:00
|
|
|
if (groupfil[groupnum] != -1)
|
|
|
|
{
|
2006-11-13 23:12:47 +00:00
|
|
|
switch (whence)
|
2006-04-24 19:04:22 +00:00
|
|
|
{
|
2006-11-13 23:12:47 +00:00
|
|
|
case BSEEK_SET:
|
|
|
|
filepos[handle] = offset; break;
|
|
|
|
case BSEEK_END:
|
|
|
|
i = filehan[handle];
|
2006-04-24 19:04:22 +00:00
|
|
|
filepos[handle] = (gfileoffs[groupnum][i+1]-gfileoffs[groupnum][i])+offset;
|
|
|
|
break;
|
2006-11-13 23:12:47 +00:00
|
|
|
case BSEEK_CUR:
|
|
|
|
filepos[handle] += offset; break;
|
2006-04-24 19:04:22 +00:00
|
|
|
}
|
|
|
|
return(filepos[handle]);
|
|
|
|
}
|
|
|
|
return(-1);
|
2006-04-23 06:44:19 +00:00
|
|
|
}
|
|
|
|
|
2009-01-09 09:29:17 +00:00
|
|
|
int32_t kfilelength(int32_t handle)
|
2006-04-23 06:44:19 +00:00
|
|
|
{
|
2009-01-09 09:29:17 +00:00
|
|
|
int32_t i, groupnum;
|
2006-04-23 06:44:19 +00:00
|
|
|
|
2006-04-24 19:04:22 +00:00
|
|
|
groupnum = filegrp[handle];
|
2007-12-12 17:42:14 +00:00
|
|
|
if (groupnum == 255)
|
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
// return(filelength(filehan[handle]))
|
|
|
|
return Bfilelength(filehan[handle]);
|
|
|
|
}
|
2006-04-23 06:44:19 +00:00
|
|
|
#ifdef WITHKPLIB
|
2006-04-24 19:04:22 +00:00
|
|
|
else if (groupnum == 254)
|
|
|
|
{
|
|
|
|
if (kzcurhand != handle)
|
|
|
|
{
|
|
|
|
if (kztell() >= 0) { filepos[kzcurhand] = kztell(); kzclose(); }
|
|
|
|
kzcurhand = handle;
|
|
|
|
kzipopen(filenamsav[handle]);
|
|
|
|
kzseek(filepos[handle],SEEK_SET);
|
|
|
|
}
|
|
|
|
return kzfilelength();
|
|
|
|
}
|
2006-04-23 06:44:19 +00:00
|
|
|
#endif
|
2006-04-24 19:04:22 +00:00
|
|
|
i = filehan[handle];
|
|
|
|
return(gfileoffs[groupnum][i+1]-gfileoffs[groupnum][i]);
|
2006-04-23 06:44:19 +00:00
|
|
|
}
|
|
|
|
|
2009-01-09 09:29:17 +00:00
|
|
|
int32_t ktell(int32_t handle)
|
2006-04-23 06:44:19 +00:00
|
|
|
{
|
2009-01-09 09:29:17 +00:00
|
|
|
int32_t groupnum = filegrp[handle];
|
2006-04-23 06:44:19 +00:00
|
|
|
|
2006-04-24 19:04:22 +00:00
|
|
|
if (groupnum == 255) return(Blseek(filehan[handle],0,BSEEK_CUR));
|
2006-04-23 06:44:19 +00:00
|
|
|
#ifdef WITHKPLIB
|
2006-04-24 19:04:22 +00:00
|
|
|
else if (groupnum == 254)
|
|
|
|
{
|
|
|
|
if (kzcurhand != handle)
|
|
|
|
{
|
|
|
|
if (kztell() >= 0) { filepos[kzcurhand] = kztell(); kzclose(); }
|
|
|
|
kzcurhand = handle;
|
|
|
|
kzipopen(filenamsav[handle]);
|
|
|
|
kzseek(filepos[handle],SEEK_SET);
|
|
|
|
}
|
|
|
|
return kztell();
|
|
|
|
}
|
2006-04-23 06:44:19 +00:00
|
|
|
#endif
|
2006-04-24 19:04:22 +00:00
|
|
|
if (groupfil[groupnum] != -1)
|
|
|
|
return filepos[handle];
|
|
|
|
return(-1);
|
2006-04-23 06:44:19 +00:00
|
|
|
}
|
|
|
|
|
2009-01-09 09:29:17 +00:00
|
|
|
void kclose(int32_t handle)
|
2006-04-23 06:44:19 +00:00
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
if (handle < 0) return;
|
|
|
|
if (filegrp[handle] == 255) Bclose(filehan[handle]);
|
2006-04-23 06:44:19 +00:00
|
|
|
#ifdef WITHKPLIB
|
2006-04-24 19:04:22 +00:00
|
|
|
else if (filegrp[handle] == 254)
|
|
|
|
{
|
|
|
|
kzclose();
|
|
|
|
kzcurhand = -1;
|
|
|
|
}
|
2006-04-23 06:44:19 +00:00
|
|
|
#endif
|
2006-04-24 19:04:22 +00:00
|
|
|
filehan[handle] = -1;
|
2006-04-23 06:44:19 +00:00
|
|
|
}
|
|
|
|
|
2011-02-21 23:08:21 +00:00
|
|
|
static int32_t klistaddentry(CACHE1D_FIND_REC **rec, const char *name, int32_t type, int32_t source)
|
2006-04-23 06:44:19 +00:00
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
CACHE1D_FIND_REC *r = NULL, *attach = NULL;
|
|
|
|
|
2007-12-12 17:42:14 +00:00
|
|
|
if (*rec)
|
|
|
|
{
|
2009-01-09 09:29:17 +00:00
|
|
|
int32_t insensitive, v;
|
2006-04-24 19:04:22 +00:00
|
|
|
CACHE1D_FIND_REC *last = NULL;
|
|
|
|
|
2007-12-12 17:42:14 +00:00
|
|
|
for (attach = *rec; attach; last = attach, attach = attach->next)
|
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
if (type == CACHE1D_FIND_DRIVE) continue; // we just want to get to the end for drives
|
2006-04-23 06:44:19 +00:00
|
|
|
#ifdef _WIN32
|
2006-04-24 19:04:22 +00:00
|
|
|
insensitive = 1;
|
2006-04-23 06:44:19 +00:00
|
|
|
#else
|
2006-04-24 19:04:22 +00:00
|
|
|
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
|
2012-02-18 17:56:13 +00:00
|
|
|
{
|
|
|
|
extern int16_t editstatus; // XXX
|
|
|
|
insensitive = !editstatus;
|
|
|
|
}
|
|
|
|
// ^ in the game, don't show file list case-sensitive
|
2006-04-23 06:44:19 +00:00
|
|
|
#endif
|
2006-04-24 19:04:22 +00:00
|
|
|
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
|
2007-12-12 17:42:14 +00:00
|
|
|
if (v < 0) // item to add is smaller than the current one
|
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2007-12-12 17:42:14 +00:00
|
|
|
if (r)
|
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
r->type = type;
|
|
|
|
r->source = source;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-10-07 06:47:35 +00:00
|
|
|
r = (CACHE1D_FIND_REC *)Bmalloc(sizeof(CACHE1D_FIND_REC)+strlen(name)+1);
|
2006-04-24 19:04:22 +00:00
|
|
|
if (!r) return -1;
|
2011-01-16 02:50:27 +00:00
|
|
|
r->name = (char *)r + sizeof(CACHE1D_FIND_REC); strcpy(r->name, name);
|
2006-04-24 19:04:22 +00:00
|
|
|
r->type = type;
|
|
|
|
r->source = source;
|
|
|
|
r->usera = r->userb = NULL;
|
|
|
|
|
2007-12-12 17:42:14 +00:00
|
|
|
if (!attach) // we are the first item
|
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
r->prev = NULL;
|
|
|
|
r->next = *rec;
|
2007-12-12 17:42:14 +00:00
|
|
|
if (*rec)(*rec)->prev = r;
|
2006-04-24 19:04:22 +00:00
|
|
|
*rec = r;
|
2007-12-12 17:42:14 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
r->prev = attach;
|
|
|
|
r->next = attach->next;
|
|
|
|
if (attach->next) attach->next->prev = r;
|
|
|
|
attach->next = r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2006-04-23 06:44:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void klistfree(CACHE1D_FIND_REC *rec)
|
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
CACHE1D_FIND_REC *n;
|
|
|
|
|
2007-12-12 17:42:14 +00:00
|
|
|
while (rec)
|
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
n = rec->next;
|
2009-10-07 06:47:35 +00:00
|
|
|
Bfree(rec);
|
2006-04-24 19:04:22 +00:00
|
|
|
rec = n;
|
|
|
|
}
|
2006-04-23 06:44:19 +00:00
|
|
|
}
|
|
|
|
|
2009-01-09 09:29:17 +00:00
|
|
|
CACHE1D_FIND_REC *klistpath(const char *_path, const char *mask, int32_t type)
|
2006-04-23 06:44:19 +00:00
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
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
|
|
|
|
|
2009-10-07 06:47:35 +00:00
|
|
|
path = Bstrdup(_path);
|
2006-04-24 19:04:22 +00:00
|
|
|
if (!path) return NULL;
|
|
|
|
|
|
|
|
// we don't need any leading dots and slashes or trailing slashes either
|
|
|
|
{
|
2009-01-09 09:29:17 +00:00
|
|
|
int32_t i,j;
|
2007-12-12 17:42:14 +00:00
|
|
|
for (i=0; path[i] == '.' || toupperlookup[path[i]] == '/';) i++;
|
2006-04-24 19:04:22 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2007-12-12 17:42:14 +00:00
|
|
|
if (*path && (type & CACHE1D_FIND_DIR))
|
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
if (klistaddentry(&rec, "..", CACHE1D_FIND_DIR, CACHE1D_SOURCE_CURDIR) < 0) goto failure;
|
|
|
|
}
|
|
|
|
|
2007-12-12 17:42:14 +00:00
|
|
|
if (!(type & CACHE1D_OPT_NOSTACK)) // current directory and paths in the search stack
|
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
searchpath_t *search = NULL;
|
|
|
|
BDIR *dir;
|
|
|
|
struct Bdirent *dirent;
|
2008-08-04 03:48:46 +00:00
|
|
|
// Adjusted for the following "autoload" dir fix - NY00123
|
|
|
|
const char *d = "./";
|
2009-01-09 09:29:17 +00:00
|
|
|
int32_t stackdepth = CACHE1D_SOURCE_CURDIR;
|
2006-04-24 19:04:22 +00:00
|
|
|
char buf[BMAX_PATH];
|
|
|
|
|
|
|
|
if (pathsearchmode) d = _path;
|
|
|
|
|
2007-12-12 17:42:14 +00:00
|
|
|
do
|
|
|
|
{
|
2011-09-06 17:46:34 +00:00
|
|
|
strcpy(buf, d);
|
2007-12-12 17:42:14 +00:00
|
|
|
if (!pathsearchmode)
|
|
|
|
{
|
2008-08-04 03:48:46 +00:00
|
|
|
// Fix for "autoload" dir in multi-user environments - NY00123
|
|
|
|
strcat(buf, path);
|
2006-04-24 19:04:22 +00:00
|
|
|
if (*path) strcat(buf, "/");
|
2007-12-12 17:42:14 +00:00
|
|
|
}
|
2011-09-06 17:46:34 +00:00
|
|
|
|
2006-04-24 19:04:22 +00:00
|
|
|
dir = Bopendir(buf);
|
2007-12-12 17:42:14 +00:00
|
|
|
if (dir)
|
|
|
|
{
|
|
|
|
while ((dirent = Breaddir(dir)))
|
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
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,
|
2007-12-12 17:42:14 +00:00
|
|
|
stackdepth))
|
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
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;
|
2006-11-13 23:12:47 +00:00
|
|
|
default:
|
|
|
|
break;
|
2006-04-24 19:04:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Bclosedir(dir);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pathsearchmode) break;
|
|
|
|
|
2007-12-12 17:42:14 +00:00
|
|
|
if (!search)
|
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
search = searchpathhead;
|
|
|
|
stackdepth = CACHE1D_SOURCE_PATH;
|
2007-12-12 17:42:14 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
search = search->next;
|
|
|
|
stackdepth++;
|
|
|
|
}
|
|
|
|
if (search) d = search->path;
|
2007-12-12 17:42:14 +00:00
|
|
|
}
|
|
|
|
while (search);
|
2006-04-24 19:04:22 +00:00
|
|
|
}
|
|
|
|
|
2013-02-14 16:02:16 +00:00
|
|
|
#ifdef WITHKPLIB
|
2007-12-12 17:42:14 +00:00
|
|
|
if (!pathsearchmode) // next, zip files
|
|
|
|
{
|
2009-07-29 21:56:56 +00:00
|
|
|
char buf[BMAX_PATH+4];
|
2009-01-09 09:29:17 +00:00
|
|
|
int32_t i, j, ftype;
|
2006-04-24 19:04:22 +00:00
|
|
|
strcpy(buf,path);
|
|
|
|
if (*path) strcat(buf,"/");
|
|
|
|
strcat(buf,mask);
|
2007-12-12 17:42:14 +00:00
|
|
|
for (kzfindfilestart(buf); kzfindfile(buf);)
|
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
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
|
2012-03-04 20:15:22 +00:00
|
|
|
for (i=1; (buf[i-1]=buf[i]); i++)
|
|
|
|
{
|
|
|
|
/* do nothing */
|
|
|
|
}
|
|
|
|
i-=2;
|
2006-04-24 19:04:22 +00:00
|
|
|
|
|
|
|
// if there's a slash at the end, this is a directory entry
|
2007-12-12 17:42:14 +00:00
|
|
|
if (toupperlookup[buf[i]] == '/') { ftype = CACHE1D_FIND_DIR; buf[i] = 0; }
|
2006-04-24 19:04:22 +00:00
|
|
|
else ftype = CACHE1D_FIND_FILE;
|
|
|
|
|
|
|
|
// skip over the common characters at the beginning of the base path and the zip entry
|
2007-12-12 17:42:14 +00:00
|
|
|
for (j=0; buf[j] && path[j]; j++)
|
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
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
|
2007-12-12 17:42:14 +00:00
|
|
|
if ((!path[0] && buf[j]) || (!path[j] && toupperlookup[ buf[j] ] == '/'))
|
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
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
|
2007-12-12 17:42:14 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
// 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
|
2007-12-12 17:42:14 +00:00
|
|
|
switch (klistaddentry(&rec, buf, ftype, CACHE1D_SOURCE_ZIP))
|
|
|
|
{
|
2006-11-13 23:12:47 +00:00
|
|
|
case -1:
|
|
|
|
goto failure;
|
2006-04-24 19:04:22 +00:00
|
|
|
//case 1: initprintf("<ZIP>:%s dropped for lower priority\n", buf); break;
|
|
|
|
//case 0: initprintf("<ZIP>:%s accepted\n", buf); break;
|
2006-11-13 23:12:47 +00:00
|
|
|
default:
|
|
|
|
break;
|
2006-04-24 19:04:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-02-14 16:02:16 +00:00
|
|
|
#endif
|
2006-04-24 19:04:22 +00:00
|
|
|
// then, grp files
|
2007-12-12 17:42:14 +00:00
|
|
|
if (!pathsearchmode && !*path && (type & CACHE1D_FIND_FILE))
|
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
char buf[13];
|
2009-01-09 09:29:17 +00:00
|
|
|
int32_t i,j;
|
2006-04-24 19:04:22 +00:00
|
|
|
buf[12] = 0;
|
2009-02-19 16:47:54 +00:00
|
|
|
for (i=0; i<MAXGROUPFILES; i++)
|
2007-12-12 17:42:14 +00:00
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
if (groupfil[i] == -1) continue;
|
2009-02-19 16:47:54 +00:00
|
|
|
for (j=gnumfiles[i]-1; j>=0; j--)
|
2006-04-24 19:04:22 +00:00
|
|
|
{
|
|
|
|
Bmemcpy(buf,&gfilelist[i][j<<4],12);
|
|
|
|
if (!Bwildmatch(buf,mask)) continue;
|
2007-12-12 17:42:14 +00:00
|
|
|
switch (klistaddentry(&rec, buf, CACHE1D_FIND_FILE, CACHE1D_SOURCE_GRP))
|
|
|
|
{
|
2006-11-13 23:12:47 +00:00
|
|
|
case -1:
|
|
|
|
goto failure;
|
2006-04-24 19:04:22 +00:00
|
|
|
//case 1: initprintf("<GRP>:%s dropped for lower priority\n", workspace); break;
|
|
|
|
//case 0: initprintf("<GRP>:%s accepted\n", workspace); break;
|
2006-11-13 23:12:47 +00:00
|
|
|
default:
|
|
|
|
break;
|
2006-04-24 19:04:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-12-12 17:42:14 +00:00
|
|
|
if (pathsearchmode && (type & CACHE1D_FIND_DRIVE))
|
|
|
|
{
|
2006-04-24 19:04:22 +00:00
|
|
|
char *drives, *drp;
|
|
|
|
drives = Bgetsystemdrives();
|
2007-12-12 17:42:14 +00:00
|
|
|
if (drives)
|
|
|
|
{
|
|
|
|
for (drp=drives; *drp; drp+=strlen(drp)+1)
|
|
|
|
{
|
|
|
|
if (klistaddentry(&rec, drp, CACHE1D_FIND_DRIVE, CACHE1D_SOURCE_DRIVE) < 0)
|
|
|
|
{
|
2009-10-07 06:47:35 +00:00
|
|
|
Bfree(drives);
|
2006-04-24 19:04:22 +00:00
|
|
|
goto failure;
|
|
|
|
}
|
|
|
|
}
|
2009-10-07 06:47:35 +00:00
|
|
|
Bfree(drives);
|
2006-04-24 19:04:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-07 06:47:35 +00:00
|
|
|
Bfree(path);
|
2006-04-24 19:04:22 +00:00
|
|
|
return rec;
|
2006-04-23 06:44:19 +00:00
|
|
|
failure:
|
2009-10-07 06:47:35 +00:00
|
|
|
Bfree(path);
|
2006-04-24 19:04:22 +00:00
|
|
|
klistfree(rec);
|
|
|
|
return NULL;
|
2006-04-23 06:44:19 +00:00
|
|
|
}
|
|
|
|
|
2012-11-11 17:57:13 +00:00
|
|
|
|
|
|
|
#endif // #ifdef CACHE1D_COMPRESS_ONLY / else
|
|
|
|
|
|
|
|
|
2006-04-24 19:04:22 +00:00
|
|
|
//Internal LZW variables
|
2006-04-23 06:44:19 +00:00
|
|
|
#define LZWSIZE 16384 //Watch out for shorts!
|
2012-02-20 19:52:04 +00:00
|
|
|
#define LZWSIZEPAD (LZWSIZE+(LZWSIZE>>4))
|
|
|
|
|
2012-02-20 19:52:53 +00:00
|
|
|
// lzwrawbuf LZWSIZE+1: see (*) below
|
|
|
|
static char lzwtmpbuf[LZWSIZEPAD], lzwrawbuf[LZWSIZE+1], lzwcompbuf[LZWSIZEPAD];
|
2012-02-20 19:52:04 +00:00
|
|
|
static int16_t lzwbuf2[LZWSIZEPAD], lzwbuf3[LZWSIZEPAD];
|
2006-04-23 06:44:19 +00:00
|
|
|
|
2012-02-18 22:14:45 +00:00
|
|
|
static int32_t lzwcompress(const char *lzwinbuf, int32_t uncompleng, char *lzwoutbuf);
|
|
|
|
static int32_t lzwuncompress(const char *lzwinbuf, int32_t compleng, char *lzwoutbuf);
|
2006-04-23 06:44:19 +00:00
|
|
|
|
2012-11-11 17:57:13 +00:00
|
|
|
#ifndef CACHE1D_COMPRESS_ONLY
|
|
|
|
static int32_t kdfread_func(intptr_t fil, void *outbuf, int32_t length)
|
|
|
|
{
|
|
|
|
return kread((int32_t)fil, outbuf, length);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dfwrite_func(intptr_t fp, const void *inbuf, int32_t length)
|
|
|
|
{
|
|
|
|
Bfwrite(inbuf, length, 1, (BFILE *)fp);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
# define kdfread_func NULL
|
|
|
|
# define dfwrite_func NULL
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// These two follow the argument order of the C functions "read" and "write":
|
|
|
|
// handle, buffer, length.
|
|
|
|
C1D_STATIC int32_t (*c1d_readfunc)(intptr_t, void *, int32_t) = kdfread_func;
|
|
|
|
C1D_STATIC void (*c1d_writefunc)(intptr_t, const void *, int32_t) = dfwrite_func;
|
|
|
|
|
|
|
|
|
|
|
|
////////// COMPRESSED READ //////////
|
|
|
|
|
|
|
|
static uint32_t decompress_part(int16_t *lengptr, intptr_t f, uint32_t *kgoalptr)
|
|
|
|
{
|
|
|
|
int32_t leng;
|
|
|
|
if (c1d_readfunc(f, lengptr, 2) != 2)
|
|
|
|
return 1;
|
|
|
|
leng = B_LITTLE16(*lengptr);
|
|
|
|
if (c1d_readfunc(f,lzwcompbuf, leng) != leng)
|
|
|
|
return 1;
|
|
|
|
*kgoalptr = lzwuncompress(lzwcompbuf, *lengptr, lzwrawbuf);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read from 'f' into 'buffer'.
|
|
|
|
C1D_STATIC int32_t c1d_read_compressed(void *buffer, bsize_t dasizeof, bsize_t count, intptr_t f)
|
2006-04-23 06:44:19 +00:00
|
|
|
{
|
2009-01-09 09:29:17 +00:00
|
|
|
uint32_t i, j, k, kgoal;
|
|
|
|
int16_t leng;
|
2006-04-24 19:04:22 +00:00
|
|
|
char *ptr;
|
|
|
|
|
2012-02-20 19:52:53 +00:00
|
|
|
if (dasizeof > LZWSIZE)
|
|
|
|
{
|
|
|
|
count *= dasizeof;
|
|
|
|
dasizeof = 1;
|
|
|
|
}
|
|
|
|
|
2006-04-24 19:04:22 +00:00
|
|
|
ptr = (char *)buffer;
|
|
|
|
|
2012-11-11 17:57:13 +00:00
|
|
|
k = decompress_part(&leng, f, &kgoal);
|
|
|
|
if (k) return -1;
|
2006-04-24 19:04:22 +00:00
|
|
|
|
2012-11-11 17:57:13 +00:00
|
|
|
Bmemcpy(ptr, lzwrawbuf, (int32_t)dasizeof);
|
2009-01-09 09:29:17 +00:00
|
|
|
k += (int32_t)dasizeof;
|
2006-04-24 19:04:22 +00:00
|
|
|
|
2009-02-19 16:47:54 +00:00
|
|
|
for (i=1; i<count; i++)
|
2006-04-24 19:04:22 +00:00
|
|
|
{
|
|
|
|
if (k >= kgoal)
|
|
|
|
{
|
2012-11-11 17:57:13 +00:00
|
|
|
k = decompress_part(&leng, f, &kgoal);
|
|
|
|
if (k) return -1;
|
2006-04-24 19:04:22 +00:00
|
|
|
}
|
2012-02-20 19:52:38 +00:00
|
|
|
|
|
|
|
for (j=0; j<dasizeof; j++)
|
|
|
|
ptr[j+dasizeof] = ((ptr[j]+lzwrawbuf[j+k])&255);
|
|
|
|
|
2006-04-24 19:04:22 +00:00
|
|
|
k += dasizeof;
|
|
|
|
ptr += dasizeof;
|
|
|
|
}
|
2012-02-20 19:52:04 +00:00
|
|
|
|
2006-04-24 19:04:22 +00:00
|
|
|
return count;
|
2006-04-23 06:44:19 +00:00
|
|
|
}
|
|
|
|
|
2012-11-11 17:57:13 +00:00
|
|
|
int32_t kdfread(void *buffer, bsize_t dasizeof, bsize_t count, int32_t fil)
|
2012-02-20 19:52:53 +00:00
|
|
|
{
|
2012-11-11 17:57:13 +00:00
|
|
|
return c1d_read_compressed(buffer, dasizeof, count, (intptr_t)fil);
|
|
|
|
}
|
|
|
|
|
2012-02-20 19:52:53 +00:00
|
|
|
|
2012-11-11 17:57:13 +00:00
|
|
|
////////// COMPRESSED WRITE //////////
|
2012-02-20 19:52:53 +00:00
|
|
|
|
2012-11-11 17:57:13 +00:00
|
|
|
static uint32_t compress_part(uint32_t k, intptr_t f)
|
|
|
|
{
|
|
|
|
int16_t leng, swleng;
|
|
|
|
leng = (int16_t)lzwcompress(lzwrawbuf,k,lzwcompbuf);
|
|
|
|
swleng = B_LITTLE16(leng);
|
|
|
|
c1d_writefunc(f, &swleng, 2);
|
|
|
|
c1d_writefunc(f, lzwcompbuf, leng);
|
2012-02-20 19:52:53 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-11-11 17:57:13 +00:00
|
|
|
// Write from 'buffer' to 'f'.
|
|
|
|
C1D_STATIC void c1d_write_compressed(const void *buffer, bsize_t dasizeof, bsize_t count, intptr_t f)
|
2006-04-23 06:44:19 +00:00
|
|
|
{
|
2009-01-09 09:29:17 +00:00
|
|
|
uint32_t i, j, k;
|
2012-02-18 22:14:45 +00:00
|
|
|
const char *ptr;
|
2006-04-24 19:04:22 +00:00
|
|
|
|
2012-02-20 19:52:53 +00:00
|
|
|
if (dasizeof > LZWSIZE)
|
|
|
|
{
|
|
|
|
count *= dasizeof;
|
|
|
|
dasizeof = 1;
|
|
|
|
}
|
|
|
|
|
2012-11-06 07:49:44 +00:00
|
|
|
ptr = (char*)buffer;
|
2006-04-24 19:04:22 +00:00
|
|
|
|
2012-02-20 19:52:53 +00:00
|
|
|
Bmemcpy(lzwrawbuf, ptr, (int32_t)dasizeof);
|
2006-04-24 19:04:22 +00:00
|
|
|
|
2012-02-20 19:52:53 +00:00
|
|
|
k = dasizeof;
|
2006-04-24 19:04:22 +00:00
|
|
|
if (k > LZWSIZE-dasizeof)
|
2012-11-11 17:57:13 +00:00
|
|
|
k = compress_part(k, f);
|
2006-04-24 19:04:22 +00:00
|
|
|
|
2009-02-19 16:47:54 +00:00
|
|
|
for (i=1; i<count; i++)
|
2006-04-24 19:04:22 +00:00
|
|
|
{
|
2012-02-20 19:52:38 +00:00
|
|
|
for (j=0; j<dasizeof; j++)
|
|
|
|
lzwrawbuf[j+k] = ((ptr[j+dasizeof]-ptr[j])&255);
|
2012-02-20 19:52:53 +00:00
|
|
|
|
2006-04-24 19:04:22 +00:00
|
|
|
k += dasizeof;
|
|
|
|
if (k > LZWSIZE-dasizeof)
|
2012-11-11 17:57:13 +00:00
|
|
|
k = compress_part(k, f);
|
2012-02-20 19:52:53 +00:00
|
|
|
|
2006-04-24 19:04:22 +00:00
|
|
|
ptr += dasizeof;
|
|
|
|
}
|
2012-02-20 19:52:53 +00:00
|
|
|
|
2006-04-24 19:04:22 +00:00
|
|
|
if (k > 0)
|
2012-11-11 17:57:13 +00:00
|
|
|
compress_part(k, f);
|
2006-04-23 06:44:19 +00:00
|
|
|
}
|
|
|
|
|
2012-11-11 17:57:13 +00:00
|
|
|
void dfwrite(const void *buffer, bsize_t dasizeof, bsize_t count, BFILE *fil)
|
|
|
|
{
|
|
|
|
c1d_write_compressed(buffer, dasizeof, count, (intptr_t)fil);
|
|
|
|
}
|
|
|
|
|
|
|
|
////////// CORE COMPRESSION FUNCTIONS //////////
|
|
|
|
|
2012-02-18 22:14:45 +00:00
|
|
|
static int32_t lzwcompress(const char *lzwinbuf, int32_t uncompleng, char *lzwoutbuf)
|
2006-04-23 06:44:19 +00:00
|
|
|
{
|
2012-02-20 19:52:53 +00:00
|
|
|
int32_t i, addr, addrcnt, *intptr;
|
2009-01-09 09:29:17 +00:00
|
|
|
int32_t bytecnt1, bitcnt, numbits, oneupnumbits;
|
|
|
|
int16_t *shortptr;
|
2006-04-24 19:04:22 +00:00
|
|
|
|
2012-02-20 19:52:53 +00:00
|
|
|
int16_t *const lzwcodehead = lzwbuf2;
|
|
|
|
int16_t *const lzwcodenext = lzwbuf3;
|
|
|
|
|
2012-02-20 19:52:38 +00:00
|
|
|
for (i=255; i>=0; i--)
|
|
|
|
{
|
|
|
|
lzwtmpbuf[i] = i;
|
2012-02-20 19:52:53 +00:00
|
|
|
lzwcodenext[i] = (i+1)&255;
|
|
|
|
lzwcodehead[i] = -1;
|
2012-02-20 19:52:38 +00:00
|
|
|
}
|
|
|
|
|
2012-02-20 19:52:53 +00:00
|
|
|
Bmemset(lzwoutbuf, 0, 4+uncompleng+1);
|
|
|
|
// clearbuf(lzwoutbuf,((uncompleng+15)+3)>>2,0L);
|
2006-04-24 19:04:22 +00:00
|
|
|
|
|
|
|
addrcnt = 256; bytecnt1 = 0; bitcnt = (4<<3);
|
|
|
|
numbits = 8; oneupnumbits = (1<<8);
|
|
|
|
do
|
|
|
|
{
|
|
|
|
addr = lzwinbuf[bytecnt1];
|
|
|
|
do
|
|
|
|
{
|
2012-02-20 19:52:53 +00:00
|
|
|
int32_t newaddr;
|
|
|
|
|
2006-04-24 19:04:22 +00:00
|
|
|
bytecnt1++;
|
2012-02-20 19:52:53 +00:00
|
|
|
if (bytecnt1 == uncompleng)
|
|
|
|
break; // (*) see XXX below
|
2012-02-20 19:52:38 +00:00
|
|
|
|
2012-02-20 19:52:53 +00:00
|
|
|
if (lzwcodehead[addr] < 0)
|
|
|
|
{
|
|
|
|
lzwcodehead[addr] = addrcnt;
|
|
|
|
break;
|
|
|
|
}
|
2012-02-20 19:52:38 +00:00
|
|
|
|
2012-02-20 19:52:53 +00:00
|
|
|
newaddr = lzwcodehead[addr];
|
2012-02-20 19:52:22 +00:00
|
|
|
while (lzwtmpbuf[newaddr] != lzwinbuf[bytecnt1])
|
2006-04-24 19:04:22 +00:00
|
|
|
{
|
2012-02-20 19:52:53 +00:00
|
|
|
if (lzwcodenext[newaddr] < 0)
|
|
|
|
{
|
|
|
|
lzwcodenext[newaddr] = addrcnt;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
newaddr = lzwcodenext[newaddr];
|
2006-04-24 19:04:22 +00:00
|
|
|
}
|
2012-02-20 19:52:38 +00:00
|
|
|
|
2012-02-20 19:52:53 +00:00
|
|
|
if (lzwcodenext[newaddr] == addrcnt)
|
|
|
|
break;
|
2006-04-24 19:04:22 +00:00
|
|
|
addr = newaddr;
|
2007-12-12 17:42:14 +00:00
|
|
|
}
|
|
|
|
while (addr >= 0);
|
2012-02-20 19:52:38 +00:00
|
|
|
|
2012-02-20 19:52:53 +00:00
|
|
|
lzwtmpbuf[addrcnt] = lzwinbuf[bytecnt1]; // XXX: potential oob access of lzwinbuf via (*) above
|
|
|
|
lzwcodehead[addrcnt] = -1;
|
|
|
|
lzwcodenext[addrcnt] = -1;
|
2006-04-24 19:04:22 +00:00
|
|
|
|
2009-01-09 09:29:17 +00:00
|
|
|
intptr = (int32_t *)&lzwoutbuf[bitcnt>>3];
|
2006-04-24 19:04:22 +00:00
|
|
|
intptr[0] |= B_LITTLE32(addr<<(bitcnt&7));
|
|
|
|
bitcnt += numbits;
|
|
|
|
if ((addr&((oneupnumbits>>1)-1)) > ((addrcnt-1)&((oneupnumbits>>1)-1)))
|
|
|
|
bitcnt--;
|
|
|
|
|
|
|
|
addrcnt++;
|
2012-02-20 19:52:38 +00:00
|
|
|
if (addrcnt > oneupnumbits)
|
|
|
|
{ numbits++; oneupnumbits <<= 1; }
|
2007-12-12 17:42:14 +00:00
|
|
|
}
|
|
|
|
while ((bytecnt1 < uncompleng) && (bitcnt < (uncompleng<<3)));
|
2006-04-24 19:04:22 +00:00
|
|
|
|
2009-01-09 09:29:17 +00:00
|
|
|
intptr = (int32_t *)&lzwoutbuf[bitcnt>>3];
|
2006-04-24 19:04:22 +00:00
|
|
|
intptr[0] |= B_LITTLE32(addr<<(bitcnt&7));
|
|
|
|
bitcnt += numbits;
|
|
|
|
if ((addr&((oneupnumbits>>1)-1)) > ((addrcnt-1)&((oneupnumbits>>1)-1)))
|
|
|
|
bitcnt--;
|
|
|
|
|
2009-01-09 09:29:17 +00:00
|
|
|
shortptr = (int16_t *)lzwoutbuf;
|
|
|
|
shortptr[0] = B_LITTLE16((int16_t)uncompleng);
|
2006-04-24 19:04:22 +00:00
|
|
|
if (((bitcnt+7)>>3) < uncompleng)
|
|
|
|
{
|
2009-01-09 09:29:17 +00:00
|
|
|
shortptr[1] = B_LITTLE16((int16_t)addrcnt);
|
2006-04-24 19:04:22 +00:00
|
|
|
return((bitcnt+7)>>3);
|
|
|
|
}
|
2012-02-20 19:52:38 +00:00
|
|
|
|
|
|
|
shortptr[1] = 0;
|
|
|
|
for (i=0; i<uncompleng; i++)
|
|
|
|
lzwoutbuf[i+4] = lzwinbuf[i];
|
|
|
|
|
2006-04-24 19:04:22 +00:00
|
|
|
return(uncompleng+4);
|
2006-04-23 06:44:19 +00:00
|
|
|
}
|
|
|
|
|
2012-02-18 22:14:45 +00:00
|
|
|
static int32_t lzwuncompress(const char *lzwinbuf, int32_t compleng, char *lzwoutbuf)
|
2006-04-23 06:44:19 +00:00
|
|
|
{
|
2012-02-20 19:52:38 +00:00
|
|
|
int32_t currstr, numbits, oneupnumbits;
|
2012-02-18 22:14:45 +00:00
|
|
|
int32_t i, dat, leng, bitcnt, outbytecnt;
|
2006-04-24 19:04:22 +00:00
|
|
|
|
2012-02-20 19:52:38 +00:00
|
|
|
const int16_t *const shortptr = (const int16_t *)lzwinbuf;
|
|
|
|
const int32_t strtot = B_LITTLE16(shortptr[1]);
|
|
|
|
const int32_t uncompleng = B_LITTLE16(shortptr[0]);
|
|
|
|
|
2006-04-24 19:04:22 +00:00
|
|
|
if (strtot == 0)
|
|
|
|
{
|
2012-11-11 17:57:13 +00:00
|
|
|
Bmemcpy(lzwoutbuf, lzwinbuf+4, (compleng-4)+3);
|
2012-02-20 19:52:38 +00:00
|
|
|
return uncompleng;
|
2006-04-24 19:04:22 +00:00
|
|
|
}
|
2012-02-20 19:52:38 +00:00
|
|
|
|
|
|
|
for (i=255; i>=0; i--)
|
|
|
|
{
|
|
|
|
lzwbuf2[i] = i;
|
|
|
|
lzwbuf3[i] = i;
|
|
|
|
}
|
|
|
|
|
2006-04-24 19:04:22 +00:00
|
|
|
currstr = 256; bitcnt = (4<<3); outbytecnt = 0;
|
|
|
|
numbits = 8; oneupnumbits = (1<<8);
|
|
|
|
do
|
|
|
|
{
|
2012-02-20 19:52:38 +00:00
|
|
|
const int32_t *const intptr = (const int32_t *)&lzwinbuf[bitcnt>>3];
|
|
|
|
|
2006-04-24 19:04:22 +00:00
|
|
|
dat = ((B_LITTLE32(intptr[0])>>(bitcnt&7)) & (oneupnumbits-1));
|
|
|
|
bitcnt += numbits;
|
|
|
|
if ((dat&((oneupnumbits>>1)-1)) > ((currstr-1)&((oneupnumbits>>1)-1)))
|
2007-12-12 17:42:14 +00:00
|
|
|
{ dat &= ((oneupnumbits>>1)-1); bitcnt--; }
|
2006-04-24 19:04:22 +00:00
|
|
|
|
|
|
|
lzwbuf3[currstr] = dat;
|
|
|
|
|
2009-02-19 16:47:54 +00:00
|
|
|
for (leng=0; dat>=256; leng++,dat=lzwbuf3[dat])
|
2012-02-20 19:52:22 +00:00
|
|
|
lzwtmpbuf[leng] = lzwbuf2[dat];
|
2006-04-24 19:04:22 +00:00
|
|
|
|
|
|
|
lzwoutbuf[outbytecnt++] = dat;
|
2012-02-20 19:52:38 +00:00
|
|
|
for (i=leng-1; i>=0; i--)
|
|
|
|
lzwoutbuf[outbytecnt++] = lzwtmpbuf[i];
|
2006-04-24 19:04:22 +00:00
|
|
|
|
|
|
|
lzwbuf2[currstr-1] = dat; lzwbuf2[currstr] = dat;
|
|
|
|
currstr++;
|
2012-02-20 19:52:38 +00:00
|
|
|
if (currstr > oneupnumbits)
|
|
|
|
{ numbits++; oneupnumbits <<= 1; }
|
2007-12-12 17:42:14 +00:00
|
|
|
}
|
|
|
|
while (currstr < strtot);
|
2012-02-20 19:52:38 +00:00
|
|
|
|
|
|
|
return uncompleng;
|
2006-04-23 06:44:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* vim:ts=4:sw=4:
|
|
|
|
*/
|