Subversion Repositories eduke32

Rev

Rev 5064 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
99 terminx 1
// "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman
2
// Ken Silverman's official web site: "http://www.advsys.net/ken"
3
// See the included license file "BUILDLIC.TXT" for license info.
4
//
5
// This file has been modified from Ken Silverman's original release
2456 hendricks2 6
// by Jonathon Fowler (jf@jonof.id.au)
99 terminx 7
 
3153 helixhorne 8
 
9
#ifdef CACHE1D_COMPRESS_ONLY
10
// Standalone libcache1d.so containing only the compression/decompression
11
// functions.
12
# include <stdint.h>
13
# include <stdio.h>
14
# include <string.h>
15
# include <stddef.h>
4187 helixhorne 16
# include <assert.h>
3153 helixhorne 17
 
18
# define BFILE FILE
19
# define C1D_STATIC
20
# define B_LITTLE16(x) (x)
21
# define B_LITTLE32(x) (x)
22
# define Bmemset memset
23
# define Bmemcpy memcpy
4187 helixhorne 24
# define Bassert assert
3153 helixhorne 25
# define bsize_t size_t
26
#else
27
// cache1d.o for EDuke32
28
# define C1D_STATIC static
29
 
99 terminx 30
#include "compat.h"
2692 helixhorne 31
#ifdef _WIN32
32
// for FILENAME_CASE_CHECK
33
# include <shellapi.h>
34
#endif
99 terminx 35
#include "cache1d.h"
36
#include "pragmas.h"
37
#include "baselayer.h"
5064 hendricks2 38
#include "crc32.h"
99 terminx 39
 
40
#ifdef WITHKPLIB
41
#include "kplib.h"
42
 
4637 terminx 43
char *kpzbuf = NULL;
44
int32_t kpzbufsiz = 0;
45
 
109 terminx 46
//Insert '|' in front of filename
47
//Doing this tells kzopen to load the file only if inside a .ZIP file
1712 helixhorne 48
static intptr_t kzipopen(const char *filnam)
99 terminx 49
{
1205 terminx 50
    uint32_t i;
1479 terminx 51
    char newst[BMAX_PATH+8];
99 terminx 52
 
109 terminx 53
    newst[0] = '|';
1229 terminx 54
    for (i=0; filnam[i] && (i < sizeof(newst)-2); i++) newst[i+1] = filnam[i];
109 terminx 55
    newst[i+1] = 0;
56
    return(kzopen(newst));
99 terminx 57
}
58
 
59
#endif
60
 
61
 
62
//   This module keeps track of a standard linear cacheing system.
63
//   To use this module, here's all you need to do:
64
//
65
//   Step 1: Allocate a nice BIG buffer, like from 1MB-4MB and
1205 terminx 66
//           Call initcache(int32_t cachestart, int32_t cachesize) where
99 terminx 67
//
618 terminx 68
//              cachestart = (intptr_t)(pointer to start of BIG buffer)
99 terminx 69
//              cachesize = length of BIG buffer
70
//
1205 terminx 71
//   Step 2: Call allocache(intptr_t *bufptr, int32_t bufsiz, char *lockptr)
99 terminx 72
//              whenever you need to allocate a buffer, where:
73
//
618 terminx 74
//              *bufptr = pointer to multi-byte pointer to buffer
99 terminx 75
//                 Confused?  Using this method, cache2d can remove
76
//                 previously allocated things from the cache safely by
618 terminx 77
//                 setting the multi-byte pointer to 0.
99 terminx 78
//              bufsiz = number of bytes to allocate
79
//              *lockptr = pointer to locking char which tells whether
80
//                 the region can be removed or not.  If *lockptr = 0 then
81
//                 the region is not locked else its locked.
82
//
83
//   Step 3: If you need to remove everything from the cache, or every
84
//           unlocked item from the cache, you can call uninitcache();
85
//              Call uninitcache(0) to remove all unlocked items, or
86
//              Call uninitcache(1) to remove everything.
87
//           After calling uninitcache, it is still ok to call allocache
88
//           without first calling initcache.
89
 
90
#define MAXCACHEOBJECTS 9216
91
 
4255 helixhorne 92
#if !defined DEBUG_ALLOCACHE_AS_MALLOC
1205 terminx 93
static int32_t cachesize = 0;
2438 helixhorne 94
static char zerochar = 0;
95
static intptr_t cachestart = 0;
96
static int32_t agecount = 0;
1205 terminx 97
static int32_t lockrecip[200];
99 terminx 98
 
2438 helixhorne 99
int32_t cacnum = 0;
100
cactype cac[MAXCACHEOBJECTS];
4255 helixhorne 101
#endif
2438 helixhorne 102
 
4637 terminx 103
char toupperlookup[256] =
3116 hendricks2 104
{
105
    0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
106
    0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
107
    0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
108
    0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
109
    0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
110
    0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,
111
    0x60,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
112
    0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x7b,0x7c,0x7d,0x7e,0x7f,
113
    0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
114
    0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
115
    0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
116
    0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
117
    0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
118
    0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,
119
    0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,
120
    0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff
121
};
99 terminx 122
 
1799 helixhorne 123
static void reportandexit(const char *errormessage);
99 terminx 124
 
125
 
1205 terminx 126
void initcache(intptr_t dacachestart, int32_t dacachesize)
99 terminx 127
{
2438 helixhorne 128
#ifndef DEBUG_ALLOCACHE_AS_MALLOC
1205 terminx 129
    int32_t i;
99 terminx 130
 
4255 helixhorne 131
    for (i=1; i<200; i++)
4658 terminx 132
        lockrecip[i] = tabledivide32_noinline(1<<28, 200-i);
99 terminx 133
 
618 terminx 134
    // The following code was relocated here from engine.c, since this
135
    // function is only ever called once (from there), and it seems to
136
    // really belong here:
137
    //
138
    //   initcache((FP_OFF(pic)+15)&0xfffffff0,(cachesize-((-FP_OFF(pic))&15))&0xfffffff0);
139
    //
140
    // I'm not sure why it's necessary, but the code is making sure the
1540 terminx 141
    // cache starts on a multiple of 16 bytes?  -- SA
99 terminx 142
 
618 terminx 143
//printf("BEFORE: cachestart = %x, cachesize = %d\n", dacachestart, dacachesize);
144
    cachestart = ((uintptr_t)dacachestart+15)&~(uintptr_t)0xf;
1346 terminx 145
    cachesize = (dacachesize-(((uintptr_t)(dacachestart))&0xf))&~(uintptr_t)0xf;
618 terminx 146
//printf("AFTER : cachestart = %x, cachesize = %d\n", cachestart, cachesize);
147
 
109 terminx 148
    cac[0].leng = cachesize;
149
    cac[0].lock = &zerochar;
150
    cacnum = 1;
99 terminx 151
 
1178 terminx 152
    initprintf("Initialized %.1fM cache\n", (float)(dacachesize/1024.f/1024.f));
2438 helixhorne 153
#else
154
    UNREFERENCED_PARAMETER(dacachestart);
155
    UNREFERENCED_PARAMETER(dacachesize);
156
#endif
99 terminx 157
}
158
 
2360 helixhorne 159
#ifdef DEBUG_ALLOCACHE_AS_MALLOC
1205 terminx 160
void allocache(intptr_t *newhandle, int32_t newbytes, char *newlockptr)
99 terminx 161
{
2360 helixhorne 162
    UNREFERENCED_PARAMETER(newlockptr);
163
 
4491 helixhorne 164
    *newhandle = (intptr_t)Xmalloc(newbytes);
2360 helixhorne 165
}
166
#else
4661 terminx 167
static inline void inc_and_check_cacnum(void)
4255 helixhorne 168
{
4661 terminx 169
    if (++cacnum > MAXCACHEOBJECTS)
4255 helixhorne 170
        reportandexit("Too many objects in cache! (cacnum > MAXCACHEOBJECTS)");
171
}
172
 
2360 helixhorne 173
void allocache(intptr_t *newhandle, int32_t newbytes, char *newlockptr)
174
{
4255 helixhorne 175
    int32_t i, z, bestz=0, bestval, besto=0, o1, sucklen, suckz;
99 terminx 176
 
618 terminx 177
//printf("  ==> asking for %d bytes, ", newbytes);
178
    // Make all requests a multiple of 16 bytes
179
    newbytes = (newbytes+15)&0xfffffff0;
180
//printf("allocated %d bytes\n", newbytes);
723 terminx 181
 
109 terminx 182
    if ((unsigned)newbytes > (unsigned)cachesize)
183
    {
584 terminx 184
        Bprintf("Cachesize: %d\n",cachesize);
795 terminx 185
        Bprintf("*Newhandle: 0x%" PRIxPTR ", Newbytes: %d, *Newlock: %d\n",(intptr_t)newhandle,newbytes,*newlockptr);
109 terminx 186
        reportandexit("BUFFER TOO BIG TO FIT IN CACHE!");
187
    }
99 terminx 188
 
109 terminx 189
    if (*newlockptr == 0)
190
    {
191
        reportandexit("ALLOCACHE CALLED WITH LOCK OF 0!");
192
    }
99 terminx 193
 
109 terminx 194
    //Find best place
195
    bestval = 0x7fffffff; o1 = cachesize;
1229 terminx 196
    for (z=cacnum-1; z>=0; z--)
109 terminx 197
    {
4255 helixhorne 198
        int32_t zz, o2, daval;
199
 
109 terminx 200
        o1 -= cac[z].leng;
4255 helixhorne 201
        o2 = o1+newbytes;
99 terminx 202
 
4255 helixhorne 203
        if (o2 > cachesize)
204
            continue;
205
 
109 terminx 206
        daval = 0;
1229 terminx 207
        for (i=o1,zz=z; i<o2; i+=cac[zz++].leng)
109 terminx 208
        {
4255 helixhorne 209
            if (*cac[zz].lock == 0)
210
                continue;
211
 
212
            if (*cac[zz].lock >= 200)
213
            {
214
                daval = 0x7fffffff;
215
                break;
216
            }
217
 
218
            // Potential for eviction increases with
219
            //  - smaller item size
220
            //  - smaller lock byte value (but in [1 .. 199])
221
            daval += mulscale32(cac[zz].leng+65536, lockrecip[*cac[zz].lock]);
222
            if (daval >= bestval)
223
                break;
109 terminx 224
        }
4255 helixhorne 225
 
109 terminx 226
        if (daval < bestval)
227
        {
228
            bestval = daval; besto = o1; bestz = z;
229
            if (bestval == 0) break;
230
        }
231
    }
99 terminx 232
 
584 terminx 233
    //printf("%d %d %d\n",besto,newbytes,*newlockptr);
99 terminx 234
 
109 terminx 235
    if (bestval == 0x7fffffff)
236
        reportandexit("CACHE SPACE ALL LOCKED UP!");
99 terminx 237
 
109 terminx 238
    //Suck things out
1229 terminx 239
    for (sucklen=-newbytes,suckz=bestz; sucklen<0; sucklen+=cac[suckz++].leng)
4255 helixhorne 240
        if (*cac[suckz].lock)
241
            *cac[suckz].hand = 0;
99 terminx 242
 
109 terminx 243
    //Remove all blocks except 1
4255 helixhorne 244
    suckz -= bestz+1;
245
    cacnum -= suckz;
246
 
2361 helixhorne 247
    Bmemmove(&cac[bestz], &cac[bestz+suckz], (cacnum-bestz)*sizeof(cactype));
4255 helixhorne 248
    cac[bestz].hand = newhandle;
249
    *newhandle = cachestart + besto;
109 terminx 250
    cac[bestz].leng = newbytes;
251
    cac[bestz].lock = newlockptr;
99 terminx 252
 
109 terminx 253
    //Add new empty block if necessary
4255 helixhorne 254
    if (sucklen <= 0)
255
        return;
99 terminx 256
 
109 terminx 257
    bestz++;
258
    if (bestz == cacnum)
259
    {
4255 helixhorne 260
        inc_and_check_cacnum();
261
 
109 terminx 262
        cac[bestz].leng = sucklen;
263
        cac[bestz].lock = &zerochar;
264
        return;
265
    }
99 terminx 266
 
4255 helixhorne 267
    if (*cac[bestz].lock == 0)
268
    {
269
        cac[bestz].leng += sucklen;
270
        return;
271
    }
99 terminx 272
 
4255 helixhorne 273
    inc_and_check_cacnum();
274
 
275
    for (z=cacnum-1; z>bestz; z--)
276
        cac[z] = cac[z-1];
109 terminx 277
    cac[bestz].leng = sucklen;
278
    cac[bestz].lock = &zerochar;
99 terminx 279
}
2360 helixhorne 280
#endif
99 terminx 281
 
282
void agecache(void)
283
{
2360 helixhorne 284
#ifndef DEBUG_ALLOCACHE_AS_MALLOC
1592 terminx 285
    int32_t cnt = (cacnum>>4);
99 terminx 286
 
4255 helixhorne 287
    if (agecount >= cacnum)
288
        agecount = cacnum-1;
1658 terminx 289
 
4255 helixhorne 290
    if (agecount < 0 || !cnt)
291
        return;
292
 
1592 terminx 293
    for (; cnt>=0; cnt--)
109 terminx 294
    {
4255 helixhorne 295
        // If we have pointer to lock char and it's in [2 .. 199], decrease.
1657 terminx 296
        if (cac[agecount].lock && (((*cac[agecount].lock)-2)&255) < 198)
1592 terminx 297
            (*cac[agecount].lock)--;
99 terminx 298
 
1592 terminx 299
        agecount--;
4255 helixhorne 300
        if (agecount < 0)
301
            agecount = cacnum-1;
109 terminx 302
    }
2360 helixhorne 303
#endif
99 terminx 304
}
305
 
1799 helixhorne 306
static void reportandexit(const char *errormessage)
99 terminx 307
{
2360 helixhorne 308
#ifndef DEBUG_ALLOCACHE_AS_MALLOC
1205 terminx 309
    int32_t i, j;
99 terminx 310
 
109 terminx 311
    //setvmode(0x3);
312
    j = 0;
1229 terminx 313
    for (i=0; i<cacnum; i++)
109 terminx 314
    {
584 terminx 315
        Bprintf("%d- ",i);
795 terminx 316
        if (cac[i].hand) Bprintf("ptr: 0x%" PRIxPTR ", ",*cac[i].hand);
109 terminx 317
        else Bprintf("ptr: NULL, ");
584 terminx 318
        Bprintf("leng: %d, ",cac[i].leng);
109 terminx 319
        if (cac[i].lock) Bprintf("lock: %d\n",*cac[i].lock);
320
        else Bprintf("lock: NULL\n");
321
        j += cac[i].leng;
322
    }
584 terminx 323
    Bprintf("Cachesize = %d\n",cachesize);
324
    Bprintf("Cacnum = %d\n",cacnum);
325
    Bprintf("Cache length sum = %d\n",j);
2360 helixhorne 326
#endif
109 terminx 327
    initprintf("ERROR: %s\n",errormessage);
4502 hendricks2 328
    Bexit(1);
99 terminx 329
}
330
 
331
#include <errno.h>
332
 
584 terminx 333
typedef struct _searchpath
334
{
109 terminx 335
    struct _searchpath *next;
336
    char *path;
337
    size_t pathlen;             // to save repeated calls to strlen()
4886 hendricks2 338
    int32_t user;
99 terminx 339
} searchpath_t;
340
static searchpath_t *searchpathhead = NULL;
341
static size_t maxsearchpathlen = 0;
1205 terminx 342
int32_t pathsearchmode = 0;
99 terminx 343
 
3059 helixhorne 344
char *listsearchpath(int32_t initp)
345
{
346
    static searchpath_t *sp;
347
 
348
    if (initp)
349
        sp = searchpathhead;
350
    else if (sp != NULL)
351
        sp = sp->next;
352
 
353
    return sp ? sp->path : NULL;
354
}
355
 
4886 hendricks2 356
int32_t addsearchpath_user(const char *p, int32_t user)
99 terminx 357
{
3168 helixhorne 358
    struct Bstat st;
109 terminx 359
    char *s;
360
    searchpath_t *srch;
4491 helixhorne 361
    char *path = Xstrdup(p);
99 terminx 362
 
3615 terminx 363
    if (path[Bstrlen(path)-1] == '\\')
3619 hendricks2 364
        path[Bstrlen(path)-1] = 0; // hack for stat() returning ENOENT on paths ending in a backslash
3615 terminx 365
 
366
    if (Bstat(path, &st) < 0)
584 terminx 367
    {
3615 terminx 368
        Bfree(path);
109 terminx 369
        if (errno == ENOENT) return -2;
370
        return -1;
371
    }
3615 terminx 372
    if (!(st.st_mode & BS_IFDIR))
373
    {
374
        Bfree(path);
375
        return -1;
376
    }
99 terminx 377
 
4491 helixhorne 378
    srch = (searchpath_t *)Xmalloc(sizeof(searchpath_t));
99 terminx 379
 
109 terminx 380
    srch->next    = searchpathhead;
3615 terminx 381
    srch->pathlen = Bstrlen(path)+1;
4491 helixhorne 382
    srch->path    = (char *)Xmalloc(srch->pathlen + 1);
99 terminx 383
 
3615 terminx 384
    Bstrcpy(srch->path, path);
3637 terminx 385
    for (s=srch->path; *s; s++);
2416 helixhorne 386
    s--;
3637 terminx 387
 
2416 helixhorne 388
    if (s<srch->path || toupperlookup[*s] != '/')
389
        Bstrcat(srch->path, "/");
390
 
109 terminx 391
    searchpathhead = srch;
2416 helixhorne 392
    if (srch->pathlen > maxsearchpathlen)
393
        maxsearchpathlen = srch->pathlen;
584 terminx 394
 
421 terminx 395
    Bcorrectfilename(srch->path,0);
584 terminx 396
 
4886 hendricks2 397
    srch->user = user;
398
 
1645 terminx 399
    initprintf("Using %s for game data\n", srch->path);
99 terminx 400
 
3615 terminx 401
    Bfree(path);
109 terminx 402
    return 0;
99 terminx 403
}
404
 
3637 terminx 405
int32_t removesearchpath(const char *p)
406
{
407
    searchpath_t *srch;
408
    char *s;
4491 helixhorne 409
    char *path = (char *)Xmalloc(Bstrlen(p) + 2);
3637 terminx 410
 
411
    Bstrcpy(path, p);
412
 
413
    if (path[Bstrlen(path)-1] == '\\')
414
        path[Bstrlen(path)-1] = 0;
415
 
416
    for (s=path; *s; s++);
417
    s--;
418
 
419
    if (s<path || toupperlookup[*s] != '/')
420
        Bstrcat(path, "/");
421
 
422
    Bcorrectfilename(path,0);
423
 
424
    for (srch = searchpathhead; srch; srch = srch->next)
425
    {
426
        if (!Bstrncmp(path, srch->path, srch->pathlen))
427
        {
428
//            initprintf("Removing %s from path stack\n", path);
429
 
430
            if (srch == searchpathhead)
431
                searchpathhead = srch->next;
432
            else
433
            {
434
                searchpath_t *sp;
435
 
436
                for (sp = searchpathhead; sp; sp = sp->next)
437
                {
438
                    if (sp->next == srch)
439
                    {
440
//                        initprintf("matched %s\n", srch->path);
441
                        sp->next = srch->next;
442
                        break;
443
                    }
444
                }
445
            }
446
 
447
            Bfree(srch->path);
448
            Bfree(srch);
449
            break;
450
        }
451
    }
452
 
453
    Bfree(path);
454
    return 0;
455
}
456
 
4886 hendricks2 457
void removesearchpaths_withuser(int32_t usermask)
458
{
4905 terminx 459
    searchpath_t *next;
460
 
461
    for (searchpath_t *srch = searchpathhead; srch; srch = next)
4886 hendricks2 462
    {
4905 terminx 463
        next = srch->next;
464
 
4886 hendricks2 465
        if (srch->user & usermask)
466
        {
4905 terminx 467
 
4886 hendricks2 468
            if (srch == searchpathhead)
469
                searchpathhead = srch->next;
470
            else
471
            {
472
                searchpath_t *sp;
473
 
474
                for (sp = searchpathhead; sp; sp = sp->next)
475
                {
476
                    if (sp->next == srch)
477
                    {
478
                        sp->next = srch->next;
479
                        break;
480
                    }
481
                }
482
            }
483
 
484
            Bfree(srch->path);
485
            Bfree(srch);
486
        }
487
    }
488
}
489
 
1205 terminx 490
int32_t findfrompath(const char *fn, char **where)
99 terminx 491
{
109 terminx 492
    searchpath_t *sp;
493
    char *pfn, *ffn;
1205 terminx 494
    int32_t allocsiz;
99 terminx 495
 
109 terminx 496
    // pathsearchmode == 0: tests current dir and then the dirs of the path stack
497
    // pathsearchmode == 1: tests fn without modification, then like for pathsearchmode == 0
99 terminx 498
 
584 terminx 499
    if (pathsearchmode)
500
    {
109 terminx 501
        // test unmolested filename first
584 terminx 502
        if (access(fn, F_OK) >= 0)
503
        {
4491 helixhorne 504
            *where = Xstrdup(fn);
109 terminx 505
            return 0;
506
        }
2693 helixhorne 507
#ifndef _WIN32
1695 helixhorne 508
        else
509
        {
4491 helixhorne 510
            char *tfn = Bstrtolower(Xstrdup(fn));
1695 helixhorne 511
 
512
            if (access(tfn, F_OK) >= 0)
513
            {
514
                *where = tfn;
515
                return 0;
516
            }
517
 
518
            Bstrupr(tfn);
519
 
520
            if (access(tfn, F_OK) >= 0)
521
            {
522
                *where = tfn;
523
                return 0;
524
            }
525
 
526
            Bfree(tfn);
527
        }
2693 helixhorne 528
#endif
109 terminx 529
    }
99 terminx 530
 
1762 terminx 531
    for (pfn = (char *)fn; toupperlookup[*pfn] == '/'; pfn++);
4491 helixhorne 532
    ffn = Xstrdup(pfn);
533
 
109 terminx 534
    Bcorrectfilename(ffn,0);    // compress relative paths
535
 
536
    allocsiz = max(maxsearchpathlen, 2);        // "./" (aka. curdir)
537
    allocsiz += strlen(ffn);
538
    allocsiz += 1;      // a nul
539
 
4491 helixhorne 540
    pfn = (char *)Xmalloc(allocsiz);
109 terminx 541
 
542
    strcpy(pfn, "./");
543
    strcat(pfn, ffn);
584 terminx 544
    if (access(pfn, F_OK) >= 0)
545
    {
109 terminx 546
        *where = pfn;
1527 terminx 547
        Bfree(ffn);
109 terminx 548
        return 0;
549
    }
550
 
584 terminx 551
    for (sp = searchpathhead; sp; sp = sp->next)
552
    {
4491 helixhorne 553
        char *tfn = Xstrdup(ffn);
1642 terminx 554
 
109 terminx 555
        strcpy(pfn, sp->path);
556
        strcat(pfn, ffn);
557
        //initprintf("Trying %s\n", pfn);
584 terminx 558
        if (access(pfn, F_OK) >= 0)
559
        {
109 terminx 560
            *where = pfn;
1527 terminx 561
            Bfree(ffn);
1642 terminx 562
            Bfree(tfn);
109 terminx 563
            return 0;
564
        }
1642 terminx 565
 
2693 helixhorne 566
#ifndef _WIN32
1695 helixhorne 567
        //Check with all lowercase
568
        strcpy(pfn, sp->path);
569
        Bstrtolower(tfn);
570
        strcat(pfn, tfn);
571
        if (access(pfn, F_OK) >= 0)
572
        {
573
            *where = pfn;
574
            Bfree(ffn);
575
            Bfree(tfn);
576
            return 0;
577
        }
578
 
579
        //Check again with uppercase
580
        strcpy(pfn, sp->path);
581
        Bstrupr(tfn);
582
        strcat(pfn, tfn);
583
        if (access(pfn, F_OK) >= 0)
584
        {
585
            *where = pfn;
586
            Bfree(ffn);
587
            Bfree(tfn);
588
            return 0;
589
        }
2693 helixhorne 590
#endif
1642 terminx 591
        Bfree(tfn);
109 terminx 592
    }
2693 helixhorne 593
 
1527 terminx 594
    Bfree(pfn); Bfree(ffn);
109 terminx 595
    return -1;
99 terminx 596
}
597
 
4680 terminx 598
#if defined(_WIN32) && defined(DEBUGGINGAIDS)
2692 helixhorne 599
# define FILENAME_CASE_CHECK
600
#endif
601
 
5059 hendricks2 602
static int32_t openfrompath_internal(const char *fn, char **where, int32_t flags, int32_t mode)
603
{
604
    if (findfrompath(fn, where) < 0)
605
        return -1;
2692 helixhorne 606
 
5059 hendricks2 607
    return Bopen(*where, flags, mode);
608
}
609
 
1205 terminx 610
int32_t openfrompath(const char *fn, int32_t flags, int32_t mode)
99 terminx 611
{
5059 hendricks2 612
    char *pfn = NULL;
99 terminx 613
 
5059 hendricks2 614
    int32_t h = openfrompath_internal(fn, &pfn, flags, mode);
2702 helixhorne 615
 
5059 hendricks2 616
    if (pfn)
2692 helixhorne 617
        Bfree(pfn);
99 terminx 618
 
109 terminx 619
    return h;
99 terminx 620
}
621
 
1762 terminx 622
BFILE *fopenfrompath(const char *fn, const char *mode)
99 terminx 623
{
1205 terminx 624
    int32_t fh;
109 terminx 625
    BFILE *h;
1205 terminx 626
    int32_t bmode = 0, smode = 0;
109 terminx 627
    const char *c;
99 terminx 628
 
584 terminx 629
    for (c=mode; c[0];)
630
    {
109 terminx 631
        if (c[0] == 'r' && c[1] == '+') { bmode = BO_RDWR; smode = BS_IREAD|BS_IWRITE; c+=2; }
632
        else if (c[0] == 'r')                { bmode = BO_RDONLY; smode = BS_IREAD; c+=1; }
633
        else if (c[0] == 'w' && c[1] == '+') { bmode = BO_RDWR|BO_CREAT|BO_TRUNC; smode = BS_IREAD|BS_IWRITE; c+=2; }
634
        else if (c[0] == 'w')                { bmode = BO_WRONLY|BO_CREAT|BO_TRUNC; smode = BS_IREAD|BS_IWRITE; c+=2; }
635
        else if (c[0] == 'a' && c[1] == '+') { bmode = BO_RDWR|BO_CREAT; smode=BS_IREAD|BS_IWRITE; c+=2; }
636
        else if (c[0] == 'a')                { bmode = BO_WRONLY|BO_CREAT; smode=BS_IREAD|BS_IWRITE; c+=1; }
637
        else if (c[0] == 'b')                { bmode |= BO_BINARY; c+=1; }
638
        else if (c[1] == 't')                { bmode |= BO_TEXT; c+=1; }
639
        else c++;
640
    }
641
    fh = openfrompath(fn,bmode,smode);
642
    if (fh < 0) return NULL;
99 terminx 643
 
109 terminx 644
    h = fdopen(fh,mode);
645
    if (!h) close(fh);
646
 
647
    return h;
99 terminx 648
}
649
 
5058 hendricks2 650
#define MAXGROUPFILES 8     // Warning: Fix groupfil if this is changed
651
#define MAXOPENFILES 64     // Warning: Fix filehan if this is changed
652
 
653
enum {
654
    GRP_RESERVED_ID_START = 254,
655
 
656
    GRP_ZIP = GRP_RESERVED_ID_START,
657
    GRP_FILESYSTEM = GRP_RESERVED_ID_START + 1,
658
};
659
 
660
EDUKE32_STATIC_ASSERT(MAXGROUPFILES <= GRP_RESERVED_ID_START);
661
 
1205 terminx 662
int32_t numgroupfiles = 0;
663
static int32_t gnumfiles[MAXGROUPFILES];
5059 hendricks2 664
static intptr_t groupfil[MAXGROUPFILES] = {-1,-1,-1,-1,-1,-1,-1,-1};
1205 terminx 665
static int32_t groupfilpos[MAXGROUPFILES];
5059 hendricks2 666
static uint8_t groupfilgrp[MAXGROUPFILES];
99 terminx 667
static char *gfilelist[MAXGROUPFILES];
1205 terminx 668
static int32_t *gfileoffs[MAXGROUPFILES];
5064 hendricks2 669
static int32_t groupcrc[MAXGROUPFILES];
99 terminx 670
 
5058 hendricks2 671
static uint8_t filegrp[MAXOPENFILES];
1205 terminx 672
static int32_t filepos[MAXOPENFILES];
737 qbix79 673
static intptr_t filehan[MAXOPENFILES] =
584 terminx 674
{
675
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
676
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
677
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
678
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
679
};
5058 hendricks2 680
 
99 terminx 681
#ifdef WITHKPLIB
682
static char filenamsav[MAXOPENFILES][260];
1205 terminx 683
static int32_t kzcurhand = -1;
3187 helixhorne 684
 
685
int32_t cache1d_file_fromzip(int32_t fil)
686
{
5058 hendricks2 687
    return (filegrp[fil] == GRP_ZIP);
3187 helixhorne 688
}
99 terminx 689
#endif
690
 
5059 hendricks2 691
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);
692
static int32_t kread_grp(int32_t handle, void *buffer, int32_t leng);
693
static int32_t klseek_grp(int32_t handle, int32_t offset, int32_t whence);
694
static void kclose_grp(int32_t handle);
695
 
5064 hendricks2 696
static void initgroupfile_crc32(int32_t handle)
697
{
698
    int32_t b, crcval = 0;
699
#define BUFFER_SIZE (1024 * 1024 * 8)
700
    uint8_t *buf = (uint8_t *)Xmalloc(BUFFER_SIZE);
701
    klseek_grp(handle, 0, BSEEK_SET);
702
 
703
    do
704
    {
705
        b = kread_grp(handle, buf, BUFFER_SIZE);
706
        if (b > 0) crcval = Bcrc32((uint8_t *)buf, b, crcval);
707
    }
708
    while (b == BUFFER_SIZE);
709
 
710
    groupcrc[handle] = crcval;
711
    klseek_grp(handle, 0, BSEEK_SET);
712
    Bfree(buf);
713
}
714
 
1712 helixhorne 715
int32_t initgroupfile(const char *filename)
99 terminx 716
{
5060 hendricks2 717
    char buf[70];
99 terminx 718
 
4637 terminx 719
    // translate all backslashes (0x5c) to forward slashes (0x2f)
109 terminx 720
    toupperlookup[0x5c] = 0x2f;
721
 
5059 hendricks2 722
    if (filename == NULL)
723
        return -1;
724
 
725
    // Technically you should be able to load more zips even if your GRPs are maxed out,
726
    // but this system is already enough of a disaster.
727
    if (numgroupfiles >= MAXGROUPFILES)
728
        return -1;
729
 
730
    char *zfn = NULL;
731
 
732
    if (kopen_internal(filename, &zfn, 0, 0, 0, numgroupfiles, groupfilgrp, groupfil, groupfilpos) < 0)
733
        return -1;
734
 
99 terminx 735
#ifdef WITHKPLIB
5059 hendricks2 736
    // check if ZIP
737
    if (zfn)
738
    {
739
        kread_grp(numgroupfiles, buf, 4);
740
        if (buf[0] == 0x50 && buf[1] == 0x4B && buf[2] == 0x03 && buf[3] == 0x04)
741
        {
742
            kclose_grp(numgroupfiles);
99 terminx 743
 
5059 hendricks2 744
            kzaddstack(zfn);
745
            Bfree(zfn);
746
            return 0;
747
        }
748
        klseek_grp(numgroupfiles,0,BSEEK_SET);
99 terminx 749
 
1527 terminx 750
        Bfree(zfn);
109 terminx 751
    }
99 terminx 752
#else
5059 hendricks2 753
    if (zfn)
754
        Bfree(zfn);
99 terminx 755
#endif
5059 hendricks2 756
 
757
    // check if GRP
758
    kread_grp(numgroupfiles,buf,16);
759
    if (!Bmemcmp(buf, "KenSilverman", 12))
109 terminx 760
    {
1205 terminx 761
        gnumfiles[numgroupfiles] = B_LITTLE32(*((int32_t *)&buf[12]));
99 terminx 762
 
4491 helixhorne 763
        gfilelist[numgroupfiles] = (char *)Xmalloc(gnumfiles[numgroupfiles]<<4);
764
        gfileoffs[numgroupfiles] = (int32_t *)Xmalloc((gnumfiles[numgroupfiles]+1)<<2);
99 terminx 765
 
5059 hendricks2 766
        kread_grp(numgroupfiles,gfilelist[numgroupfiles],gnumfiles[numgroupfiles]<<4);
99 terminx 767
 
5059 hendricks2 768
        int32_t j = (gnumfiles[numgroupfiles]+1)<<4, k;
769
        for (int32_t i=0; i<gnumfiles[numgroupfiles]; i++)
109 terminx 770
        {
1205 terminx 771
            k = B_LITTLE32(*((int32_t *)&gfilelist[numgroupfiles][(i<<4)+12]));
109 terminx 772
            gfilelist[numgroupfiles][(i<<4)+12] = 0;
773
            gfileoffs[numgroupfiles][i] = j;
774
            j += k;
775
        }
776
        gfileoffs[numgroupfiles][gnumfiles[numgroupfiles]] = j;
5064 hendricks2 777
        initgroupfile_crc32(numgroupfiles);
5059 hendricks2 778
        numgroupfiles++;
779
        return 0;
109 terminx 780
    }
5060 hendricks2 781
    klseek_grp(numgroupfiles, 0, BSEEK_SET);
99 terminx 782
 
5060 hendricks2 783
    // check if SSI
784
    // this performs several checks because there is no "SSI" magic
785
    int32_t version;
786
    kread_grp(numgroupfiles, &version, 4);
787
    version = B_LITTLE32(version);
788
    while (version == 1 || version == 2) // if
789
    {
790
        char zerobuf[70];
791
        Bmemset(zerobuf, 0, 70);
792
 
793
        int32_t numfiles;
794
        kread_grp(numgroupfiles, &numfiles, 4);
795
        numfiles = B_LITTLE32(numfiles);
796
 
797
        uint8_t temp, temp2;
798
 
799
        // get the string length
800
        kread_grp(numgroupfiles, &temp, 1);
801
        if (temp > 31) // 32 bytes allocated for the string
802
            break;
803
        // seek to the end of the string
804
        klseek_grp(numgroupfiles, temp, BSEEK_CUR);
805
        // verify everything remaining is a null terminator
806
        temp = 32 - temp;
807
        kread_grp(numgroupfiles, buf, temp);
808
        if (Bmemcmp(buf, zerobuf, temp))
809
            break;
810
 
811
        if (version == 2)
812
        {
813
            // get the string length
814
            kread_grp(numgroupfiles, &temp, 1);
815
            if (temp > 11) // 12 bytes allocated for the string
816
                break;
817
            // seek to the end of the string
818
            klseek_grp(numgroupfiles, temp, BSEEK_CUR);
819
            // verify everything remaining is a null terminator
820
            temp = 12 - temp;
821
            kread_grp(numgroupfiles, buf, temp);
822
            if (Bmemcmp(buf, zerobuf, temp))
823
                break;
824
        }
825
 
826
        temp2 = 0;
827
        for (uint8_t i=0;i<3;i++)
828
        {
829
            // get the string length
830
            kread_grp(numgroupfiles, &temp, 1);
831
            if (temp > 70) // 70 bytes allocated for the string
832
            {
833
                temp2 = 1;
834
                break;
835
            }
836
            // seek to the end of the string
837
            klseek_grp(numgroupfiles, temp, BSEEK_CUR);
838
            // verify everything remaining is a null terminator
839
            temp = 70 - temp;
840
            if (temp == 0)
841
                continue;
842
            kread_grp(numgroupfiles, buf, temp);
843
            temp2 |= Bmemcmp(buf, zerobuf, temp);
844
        }
845
        if (temp2)
846
            break;
847
 
848
        // Passed all the tests: read data.
849
 
850
        gnumfiles[numgroupfiles] = numfiles;
851
 
852
        gfilelist[numgroupfiles] = (char *)Xmalloc(gnumfiles[numgroupfiles]<<4);
853
        gfileoffs[numgroupfiles] = (int32_t *)Xmalloc((gnumfiles[numgroupfiles]+1)<<2);
854
 
855
        int32_t j = (version == 2 ? 267 : 254) + (numfiles * 121), k;
856
        for (int32_t i = 0; i < numfiles; i++)
857
        {
858
            // get the string length
859
            kread_grp(numgroupfiles, &temp, 1);
860
            if (temp > 12)
861
                temp = 12;
862
            // read the file name
863
            kread_grp(numgroupfiles, &gfilelist[numgroupfiles][i<<4], temp);
864
            gfilelist[numgroupfiles][(i<<4)+temp] = 0;
865
 
866
            // skip to the end of the 12 bytes
867
            klseek_grp(numgroupfiles, 12-temp, BSEEK_CUR);
868
 
869
            // get the file size
870
            kread_grp(numgroupfiles, &k, 4);
871
            k = B_LITTLE32(k);
872
 
873
            // record the offset of the file in the SSI
874
            gfileoffs[numgroupfiles][i] = j;
875
            j += k;
876
 
877
            // skip unknown data
878
            klseek_grp(numgroupfiles, 104, BSEEK_CUR);
879
        }
880
        gfileoffs[numgroupfiles][gnumfiles[numgroupfiles]] = j;
5064 hendricks2 881
        initgroupfile_crc32(numgroupfiles);
5060 hendricks2 882
        numgroupfiles++;
883
        return 0;
884
    }
885
 
5059 hendricks2 886
    kclose_grp(numgroupfiles);
887
    return -1;
99 terminx 888
}
889
 
890
void uninitgroupfile(void)
891
{
1205 terminx 892
    int32_t i;
99 terminx 893
 
1229 terminx 894
    for (i=numgroupfiles-1; i>=0; i--)
109 terminx 895
        if (groupfil[i] != -1)
896
        {
1586 terminx 897
            Bfree(gfilelist[i]);
898
            Bfree(gfileoffs[i]);
109 terminx 899
            Bclose(groupfil[i]);
900
            groupfil[i] = -1;
901
        }
902
    numgroupfiles = 0;
99 terminx 903
 
109 terminx 904
    // JBF 20040111: "close" any files open in groups
1229 terminx 905
    for (i=0; i<MAXOPENFILES; i++)
584 terminx 906
    {
5058 hendricks2 907
        if (filegrp[i] < GRP_RESERVED_ID_START)   // JBF 20040130: not external or ZIPped
109 terminx 908
            filehan[i] = -1;
909
    }
99 terminx 910
}
911
 
2692 helixhorne 912
#ifdef FILENAME_CASE_CHECK
913
// See
914
// http://stackoverflow.com/questions/74451/getting-actual-file-name-with-proper-casing-on-windows
915
// for relevant discussion.
916
 
5076 terminx 917
static char fnbuf[BMAX_PATH];
918
int fnofs;
2692 helixhorne 919
 
2702 helixhorne 920
int32_t (*check_filename_casing_fn)(void) = NULL;
921
 
2692 helixhorne 922
// -1: failure, 0: match, 1: mismatch
5076 terminx 923
static int32_t check_filename_mismatch(const char * const filename, int ofs)
2692 helixhorne 924
{
5076 terminx 925
    if (!GetShortPathNameA(filename, fnbuf, BMAX_PATH)) return -1;
926
    if (!GetLongPathNameA(fnbuf, fnbuf, BMAX_PATH)) return -1;
2702 helixhorne 927
 
5076 terminx 928
    fnofs = ofs;
2692 helixhorne 929
 
5076 terminx 930
    int len = Bstrlen(fnbuf+ofs);
3068 terminx 931
 
5076 terminx 932
    char const * const fn = filename+ofs;
933
 
934
    if (!Bstrncmp(fnbuf+ofs, fn, len))
2702 helixhorne 935
        return 0;
936
 
5076 terminx 937
    char * const tfn = Bstrtolower(Xstrdup(fn));
938
 
939
    if (!Bstrncmp(fnbuf+ofs, tfn, len))
2702 helixhorne 940
    {
5076 terminx 941
        Bfree(tfn);
942
        return 0;
943
    }
2702 helixhorne 944
 
5076 terminx 945
    Bstrupr(tfn);
2702 helixhorne 946
 
5076 terminx 947
    if (!Bstrncmp(fnbuf+ofs, tfn, len))
948
    {
2702 helixhorne 949
        Bfree(tfn);
5076 terminx 950
        return 0;
2702 helixhorne 951
    }
952
 
5076 terminx 953
    Bfree(tfn);
954
 
2702 helixhorne 955
    return 1;
2692 helixhorne 956
}
957
#endif
958
 
5059 hendricks2 959
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)
99 terminx 960
{
5059 hendricks2 961
    int32_t j, k, fil;
109 terminx 962
    char bad, *gfileptr;
737 qbix79 963
    intptr_t i;
99 terminx 964
 
5059 hendricks2 965
    if (searchfirst == 0 && (fil = openfrompath_internal(filename, lastpfn, BO_BINARY|BO_RDONLY, BS_IREAD)) >= 0)
109 terminx 966
    {
2692 helixhorne 967
#ifdef FILENAME_CASE_CHECK
5059 hendricks2 968
        if (checkcase && check_filename_casing_fn && check_filename_casing_fn())
2702 helixhorne 969
        {
970
            int32_t status;
971
            char *cp, *lastslash;
2692 helixhorne 972
 
2702 helixhorne 973
            // convert all slashes to backslashes because SHGetFileInfo()
974
            // complains else!
5059 hendricks2 975
            lastslash = *lastpfn;
976
            for (cp=*lastpfn; *cp; cp++)
2702 helixhorne 977
                if (*cp=='/')
978
                {
979
                    *cp = '\\';
980
                    lastslash = cp;
981
                }
5059 hendricks2 982
            if (lastslash != *lastpfn)
2702 helixhorne 983
                lastslash++;
984
 
5059 hendricks2 985
            status = check_filename_mismatch(*lastpfn, lastslash-*lastpfn);
2702 helixhorne 986
 
987
            if (status == -1)
2692 helixhorne 988
            {
3385 helixhorne 989
//                initprintf("SHGetFileInfo failed with error code %lu\n", GetLastError());
2692 helixhorne 990
            }
2702 helixhorne 991
            else if (status == 1)
992
            {
993
                initprintf("warning: case mismatch: passed \"%s\", real \"%s\"\n",
5076 terminx 994
                           lastslash, fnbuf+fnofs);
2702 helixhorne 995
            }
2692 helixhorne 996
        }
5059 hendricks2 997
#else
998
        UNREFERENCED_PARAMETER(checkcase);
2692 helixhorne 999
#endif
5059 hendricks2 1000
        arraygrp[newhandle] = GRP_FILESYSTEM;
1001
        arrayhan[newhandle] = fil;
1002
        arraypos[newhandle] = 0;
1003
        return newhandle;
1592 terminx 1004
    }
99 terminx 1005
 
109 terminx 1006
    for (; toupperlookup[*filename] == '/'; filename++);
1007
 
99 terminx 1008
#ifdef WITHKPLIB
5059 hendricks2 1009
    if (tryzip)
109 terminx 1010
    {
5059 hendricks2 1011
        if ((kzcurhand != newhandle) && (kztell() >= 0))
1012
        {
1013
            if (kzcurhand >= 0) arraypos[kzcurhand] = kztell();
1014
            kzclose();
1015
        }
1016
        if (searchfirst != 1 && (i = kzipopen(filename)) != 0)
1017
        {
1018
            kzcurhand = newhandle;
1019
            arraygrp[newhandle] = GRP_ZIP;
1020
            arrayhan[newhandle] = i;
1021
            arraypos[newhandle] = 0;
1022
            strcpy(filenamsav[newhandle],filename);
1023
            return newhandle;
1024
        }
109 terminx 1025
    }
5059 hendricks2 1026
#else
1027
    UNREFERENCED_PARAMETER(tryzip);
99 terminx 1028
#endif
1029
 
1229 terminx 1030
    for (k=numgroupfiles-1; k>=0; k--)
109 terminx 1031
    {
1032
        if (searchfirst == 1) k = 0;
1033
        if (groupfil[k] >= 0)
1034
        {
1229 terminx 1035
            for (i=gnumfiles[k]-1; i>=0; i--)
109 terminx 1036
            {
1037
                gfileptr = (char *)&gfilelist[k][i<<4];
99 terminx 1038
 
109 terminx 1039
                bad = 0;
1229 terminx 1040
                for (j=0; j<13; j++)
109 terminx 1041
                {
1042
                    if (!filename[j]) break;
1043
                    if (toupperlookup[filename[j]] != toupperlookup[gfileptr[j]])
2438 helixhorne 1044
                    {
1045
                        bad = 1;
1046
                        break;
1047
                    }
109 terminx 1048
                }
1049
                if (bad) continue;
1050
                if (j<13 && gfileptr[j]) continue;   // JBF: because e1l1.map might exist before e1l1
1592 terminx 1051
                if (j==13 && filename[j]) continue;   // JBF: long file name
99 terminx 1052
 
5059 hendricks2 1053
                arraygrp[newhandle] = k;
1054
                arrayhan[newhandle] = i;
1055
                arraypos[newhandle] = 0;
1056
                return newhandle;
109 terminx 1057
            }
1058
        }
1059
    }
5059 hendricks2 1060
 
1061
    return -1;
99 terminx 1062
}
1063
 
5064 hendricks2 1064
void krename(int32_t crcval, int32_t filenum, const char *newname)
5061 hendricks2 1065
{
5064 hendricks2 1066
    for (int32_t k=numgroupfiles-1; k>=0; k--)
5061 hendricks2 1067
    {
5064 hendricks2 1068
        if (groupfil[k] >= 0 && groupcrc[k] == crcval)
5061 hendricks2 1069
        {
5064 hendricks2 1070
            Bstrncpy((char *)&gfilelist[k][filenum<<4], newname, 12);
1071
            return;
5061 hendricks2 1072
        }
1073
    }
1074
}
1075
 
5059 hendricks2 1076
int32_t kopen4load(const char *filename, char searchfirst)
99 terminx 1077
{
5059 hendricks2 1078
    int32_t newhandle = MAXOPENFILES-1;
99 terminx 1079
 
5059 hendricks2 1080
    if (filename==NULL)
1081
        return -1;
1082
 
1083
    while (filehan[newhandle] != -1)
1084
    {
1085
        newhandle--;
1086
        if (newhandle < 0)
1087
        {
1088
            Bprintf("TOO MANY FILES OPEN IN FILE GROUPING SYSTEM!");
1089
            Bexit(0);
1090
        }
1091
    }
1092
 
1093
    char *lastpfn = NULL;
1094
 
1095
    int32_t h = kopen_internal(filename, &lastpfn, searchfirst, 1, 1, newhandle, filegrp, filehan, filepos);
1096
 
1097
    if (lastpfn)
1098
        Bfree(lastpfn);
1099
 
1100
    return h;
1101
}
1102
 
1103
int32_t kread_internal(int32_t handle, void *buffer, int32_t leng, uint8_t *arraygrp, intptr_t *arrayhan, int32_t *arraypos)
1104
{
1105
    int32_t filenum = arrayhan[handle];
1106
    int32_t groupnum = arraygrp[handle];
1107
 
5058 hendricks2 1108
    if (groupnum == GRP_FILESYSTEM) return(Bread(filenum,buffer,leng));
99 terminx 1109
#ifdef WITHKPLIB
5058 hendricks2 1110
    else if (groupnum == GRP_ZIP)
109 terminx 1111
    {
1112
        if (kzcurhand != handle)
1113
        {
5059 hendricks2 1114
            if (kztell() >= 0) { arraypos[kzcurhand] = kztell(); kzclose(); }
109 terminx 1115
            kzcurhand = handle;
1116
            kzipopen(filenamsav[handle]);
5059 hendricks2 1117
            kzseek(arraypos[handle],SEEK_SET);
109 terminx 1118
        }
1119
        return(kzread(buffer,leng));
1120
    }
99 terminx 1121
#endif
1122
 
5059 hendricks2 1123
    if (EDUKE32_PREDICT_FALSE(groupfil[groupnum] == -1))
1124
        return 0;
1125
 
1126
    int32_t rootgroupnum = groupnum;
1127
    int32_t i = 0;
1128
    while (groupfilgrp[rootgroupnum] != GRP_FILESYSTEM)
109 terminx 1129
    {
5059 hendricks2 1130
        i += gfileoffs[groupfilgrp[rootgroupnum]][groupfil[rootgroupnum]];
1131
        rootgroupnum = groupfilgrp[rootgroupnum];
1132
    }
1133
    if (EDUKE32_PREDICT_TRUE(groupfil[rootgroupnum] != -1))
1134
    {
1135
        i += gfileoffs[groupnum][filenum]+arraypos[handle];
1136
        if (i != groupfilpos[rootgroupnum])
109 terminx 1137
        {
5059 hendricks2 1138
            Blseek(groupfil[rootgroupnum],i,BSEEK_SET);
1139
            groupfilpos[rootgroupnum] = i;
109 terminx 1140
        }
5059 hendricks2 1141
        leng = min(leng,(gfileoffs[groupnum][filenum+1]-gfileoffs[groupnum][filenum])-arraypos[handle]);
1142
        leng = Bread(groupfil[rootgroupnum],buffer,leng);
1143
        arraypos[handle] += leng;
1144
        groupfilpos[rootgroupnum] += leng;
109 terminx 1145
        return(leng);
1146
    }
99 terminx 1147
 
109 terminx 1148
    return(0);
99 terminx 1149
}
1150
 
5059 hendricks2 1151
int32_t klseek_internal(int32_t handle, int32_t offset, int32_t whence, uint8_t *arraygrp, intptr_t *arrayhan, int32_t *arraypos)
99 terminx 1152
{
1205 terminx 1153
    int32_t i, groupnum;
99 terminx 1154
 
5059 hendricks2 1155
    groupnum = arraygrp[handle];
99 terminx 1156
 
5059 hendricks2 1157
    if (groupnum == GRP_FILESYSTEM) return(Blseek(arrayhan[handle],offset,whence));
99 terminx 1158
#ifdef WITHKPLIB
5058 hendricks2 1159
    else if (groupnum == GRP_ZIP)
109 terminx 1160
    {
1161
        if (kzcurhand != handle)
1162
        {
5059 hendricks2 1163
            if (kztell() >= 0) { arraypos[kzcurhand] = kztell(); kzclose(); }
109 terminx 1164
            kzcurhand = handle;
1165
            kzipopen(filenamsav[handle]);
5059 hendricks2 1166
            kzseek(arraypos[handle],SEEK_SET);
109 terminx 1167
        }
1168
        return(kzseek(offset,whence));
1169
    }
99 terminx 1170
#endif
1171
 
109 terminx 1172
    if (groupfil[groupnum] != -1)
1173
    {
331 terminx 1174
        switch (whence)
109 terminx 1175
        {
331 terminx 1176
        case BSEEK_SET:
5059 hendricks2 1177
            arraypos[handle] = offset; break;
331 terminx 1178
        case BSEEK_END:
5059 hendricks2 1179
            i = arrayhan[handle];
1180
            arraypos[handle] = (gfileoffs[groupnum][i+1]-gfileoffs[groupnum][i])+offset;
109 terminx 1181
            break;
331 terminx 1182
        case BSEEK_CUR:
5059 hendricks2 1183
            arraypos[handle] += offset; break;
109 terminx 1184
        }
5059 hendricks2 1185
        return(arraypos[handle]);
109 terminx 1186
    }
1187
    return(-1);
99 terminx 1188
}
1189
 
5059 hendricks2 1190
int32_t kfilelength_internal(int32_t handle, uint8_t *arraygrp, intptr_t *arrayhan, int32_t *arraypos)
99 terminx 1191
{
1205 terminx 1192
    int32_t i, groupnum;
99 terminx 1193
 
5059 hendricks2 1194
    groupnum = arraygrp[handle];
5058 hendricks2 1195
    if (groupnum == GRP_FILESYSTEM)
584 terminx 1196
    {
5059 hendricks2 1197
        // return(filelength(arrayhan[handle]))
1198
        return Bfilelength(arrayhan[handle]);
109 terminx 1199
    }
99 terminx 1200
#ifdef WITHKPLIB
5058 hendricks2 1201
    else if (groupnum == GRP_ZIP)
109 terminx 1202
    {
1203
        if (kzcurhand != handle)
1204
        {
5059 hendricks2 1205
            if (kztell() >= 0) { arraypos[kzcurhand] = kztell(); kzclose(); }
109 terminx 1206
            kzcurhand = handle;
1207
            kzipopen(filenamsav[handle]);
5059 hendricks2 1208
            kzseek(arraypos[handle],SEEK_SET);
109 terminx 1209
        }
1210
        return kzfilelength();
1211
    }
99 terminx 1212
#endif
5059 hendricks2 1213
    i = arrayhan[handle];
109 terminx 1214
    return(gfileoffs[groupnum][i+1]-gfileoffs[groupnum][i]);
99 terminx 1215
}
1216
 
5059 hendricks2 1217
int32_t ktell_internal(int32_t handle, uint8_t *arraygrp, intptr_t *arrayhan, int32_t *arraypos)
99 terminx 1218
{
5059 hendricks2 1219
    int32_t groupnum = arraygrp[handle];
99 terminx 1220
 
5059 hendricks2 1221
    if (groupnum == GRP_FILESYSTEM) return(Blseek(arrayhan[handle],0,BSEEK_CUR));
99 terminx 1222
#ifdef WITHKPLIB
5058 hendricks2 1223
    else if (groupnum == GRP_ZIP)
109 terminx 1224
    {
1225
        if (kzcurhand != handle)
1226
        {
5059 hendricks2 1227
            if (kztell() >= 0) { arraypos[kzcurhand] = kztell(); kzclose(); }
109 terminx 1228
            kzcurhand = handle;
1229
            kzipopen(filenamsav[handle]);
5059 hendricks2 1230
            kzseek(arraypos[handle],SEEK_SET);
109 terminx 1231
        }
1232
        return kztell();
1233
    }
99 terminx 1234
#endif
109 terminx 1235
    if (groupfil[groupnum] != -1)
5059 hendricks2 1236
        return arraypos[handle];
109 terminx 1237
    return(-1);
99 terminx 1238
}
1239
 
5059 hendricks2 1240
void kclose_internal(int32_t handle, uint8_t *arraygrp, intptr_t *arrayhan)
99 terminx 1241
{
109 terminx 1242
    if (handle < 0) return;
5059 hendricks2 1243
    if (arraygrp[handle] == GRP_FILESYSTEM) Bclose(arrayhan[handle]);
99 terminx 1244
#ifdef WITHKPLIB
5059 hendricks2 1245
    else if (arraygrp[handle] == GRP_ZIP)
109 terminx 1246
    {
1247
        kzclose();
1248
        kzcurhand = -1;
1249
    }
99 terminx 1250
#endif
5059 hendricks2 1251
    arrayhan[handle] = -1;
99 terminx 1252
}
1253
 
5059 hendricks2 1254
int32_t kread(int32_t handle, void *buffer, int32_t leng)
1255
{
1256
    return kread_internal(handle, buffer, leng, filegrp, filehan, filepos);
1257
}
1258
int32_t klseek(int32_t handle, int32_t offset, int32_t whence)
1259
{
1260
    return klseek_internal(handle, offset, whence, filegrp, filehan, filepos);
1261
}
1262
int32_t kfilelength(int32_t handle)
1263
{
1264
    return kfilelength_internal(handle, filegrp, filehan, filepos);
1265
}
1266
int32_t ktell(int32_t handle)
1267
{
1268
    return ktell_internal(handle, filegrp, filehan, filepos);
1269
}
1270
void kclose(int32_t handle)
1271
{
1272
    return kclose_internal(handle, filegrp, filehan);
1273
}
1274
 
1275
static int32_t kread_grp(int32_t handle, void *buffer, int32_t leng)
1276
{
1277
    return kread_internal(handle, buffer, leng, groupfilgrp, groupfil, groupfilpos);
1278
}
1279
static int32_t klseek_grp(int32_t handle, int32_t offset, int32_t whence)
1280
{
1281
    return klseek_internal(handle, offset, whence, groupfilgrp, groupfil, groupfilpos);
1282
}
1283
static void kclose_grp(int32_t handle)
1284
{
1285
    return kclose_internal(handle, groupfilgrp, groupfil);
1286
}
1287
 
1799 helixhorne 1288
static int32_t klistaddentry(CACHE1D_FIND_REC **rec, const char *name, int32_t type, int32_t source)
99 terminx 1289
{
109 terminx 1290
    CACHE1D_FIND_REC *r = NULL, *attach = NULL;
99 terminx 1291
 
584 terminx 1292
    if (*rec)
1293
    {
1205 terminx 1294
        int32_t insensitive, v;
109 terminx 1295
        CACHE1D_FIND_REC *last = NULL;
1296
 
584 terminx 1297
        for (attach = *rec; attach; last = attach, attach = attach->next)
1298
        {
109 terminx 1299
            if (type == CACHE1D_FIND_DRIVE) continue;   // we just want to get to the end for drives
99 terminx 1300
#ifdef _WIN32
109 terminx 1301
            insensitive = 1;
99 terminx 1302
#else
109 terminx 1303
            if (source == CACHE1D_SOURCE_GRP || attach->source == CACHE1D_SOURCE_GRP)
1304
                insensitive = 1;
1305
            else if (source == CACHE1D_SOURCE_ZIP || attach->source == CACHE1D_SOURCE_ZIP)
1306
                insensitive = 1;
1307
            else
2356 helixhorne 1308
            {
1309
                extern int16_t editstatus;  // XXX
1310
                insensitive = !editstatus;
1311
            }
1312
            // ^ in the game, don't show file list case-sensitive
99 terminx 1313
#endif
109 terminx 1314
            if (insensitive) v = Bstrcasecmp(name, attach->name);
1315
            else v = Bstrcmp(name, attach->name);
99 terminx 1316
 
109 terminx 1317
            // sorted list
1318
            if (v > 0) continue;        // item to add is bigger than the current one
1319
            // so look for something bigger than us
584 terminx 1320
            if (v < 0)                          // item to add is smaller than the current one
1321
            {
109 terminx 1322
                attach = NULL;          // so wedge it between the current item and the one before
1323
                break;
1324
            }
99 terminx 1325
 
109 terminx 1326
            // matched
1327
            if (source >= attach->source) return 1;     // item to add is of lower priority
1328
            r = attach;
1329
            break;
1330
        }
99 terminx 1331
 
109 terminx 1332
        // wasn't found in the list, so attach to the end
1333
        if (!attach) attach = last;
1334
    }
99 terminx 1335
 
584 terminx 1336
    if (r)
1337
    {
109 terminx 1338
        r->type = type;
1339
        r->source = source;
1340
        return 0;
1341
    }
99 terminx 1342
 
4491 helixhorne 1343
    r = (CACHE1D_FIND_REC *)Xmalloc(sizeof(CACHE1D_FIND_REC)+strlen(name)+1);
1344
 
1762 terminx 1345
    r->name = (char *)r + sizeof(CACHE1D_FIND_REC); strcpy(r->name, name);
109 terminx 1346
    r->type = type;
1347
    r->source = source;
1348
    r->usera = r->userb = NULL;
1349
 
584 terminx 1350
    if (!attach)        // we are the first item
1351
    {
109 terminx 1352
        r->prev = NULL;
1353
        r->next = *rec;
584 terminx 1354
        if (*rec)(*rec)->prev = r;
109 terminx 1355
        *rec = r;
584 terminx 1356
    }
1357
    else
1358
    {
109 terminx 1359
        r->prev = attach;
1360
        r->next = attach->next;
1361
        if (attach->next) attach->next->prev = r;
1362
        attach->next = r;
1363
    }
1364
 
1365
    return 0;
99 terminx 1366
}
1367
 
1368
void klistfree(CACHE1D_FIND_REC *rec)
1369
{
109 terminx 1370
    CACHE1D_FIND_REC *n;
1371
 
584 terminx 1372
    while (rec)
1373
    {
109 terminx 1374
        n = rec->next;
1527 terminx 1375
        Bfree(rec);
109 terminx 1376
        rec = n;
1377
    }
99 terminx 1378
}
1379
 
1205 terminx 1380
CACHE1D_FIND_REC *klistpath(const char *_path, const char *mask, int32_t type)
99 terminx 1381
{
109 terminx 1382
    CACHE1D_FIND_REC *rec = NULL;
1383
    char *path;
99 terminx 1384
 
109 terminx 1385
    // pathsearchmode == 0: enumerates a path in the virtual filesystem
1386
    // pathsearchmode == 1: enumerates the system filesystem path passed in
99 terminx 1387
 
4491 helixhorne 1388
    path = Xstrdup(_path);
99 terminx 1389
 
109 terminx 1390
    // we don't need any leading dots and slashes or trailing slashes either
1391
    {
1205 terminx 1392
        int32_t i,j;
584 terminx 1393
        for (i=0; path[i] == '.' || toupperlookup[path[i]] == '/';) i++;
109 terminx 1394
        for (j=0; (path[j] = path[i]); j++,i++) ;
1395
        while (j>0 && toupperlookup[path[j-1]] == '/') j--;
1396
        path[j] = 0;
1397
        //initprintf("Cleaned up path = \"%s\"\n",path);
1398
    }
99 terminx 1399
 
584 terminx 1400
    if (*path && (type & CACHE1D_FIND_DIR))
1401
    {
109 terminx 1402
        if (klistaddentry(&rec, "..", CACHE1D_FIND_DIR, CACHE1D_SOURCE_CURDIR) < 0) goto failure;
1403
    }
99 terminx 1404
 
584 terminx 1405
    if (!(type & CACHE1D_OPT_NOSTACK))          // current directory and paths in the search stack
1406
    {
109 terminx 1407
        searchpath_t *search = NULL;
1408
        BDIR *dir;
1409
        struct Bdirent *dirent;
4188 helixhorne 1410
 
1411
        static const char *const CUR_DIR = "./";
932 terminx 1412
        // Adjusted for the following "autoload" dir fix - NY00123
4188 helixhorne 1413
        const char *d = pathsearchmode ? _path : CUR_DIR;
1205 terminx 1414
        int32_t stackdepth = CACHE1D_SOURCE_CURDIR;
109 terminx 1415
        char buf[BMAX_PATH];
99 terminx 1416
 
584 terminx 1417
        do
1418
        {
4188 helixhorne 1419
            if (d==CUR_DIR && (type & CACHE1D_FIND_NOCURDIR))
1420
                goto next;
1421
 
2010 helixhorne 1422
            strcpy(buf, d);
584 terminx 1423
            if (!pathsearchmode)
1424
            {
932 terminx 1425
                // Fix for "autoload" dir in multi-user environments - NY00123
1426
                strcat(buf, path);
109 terminx 1427
                if (*path) strcat(buf, "/");
584 terminx 1428
            }
2010 helixhorne 1429
 
109 terminx 1430
            dir = Bopendir(buf);
584 terminx 1431
            if (dir)
1432
            {
1433
                while ((dirent = Breaddir(dir)))
1434
                {
109 terminx 1435
                    if ((dirent->name[0] == '.' && dirent->name[1] == 0) ||
1436
                            (dirent->name[0] == '.' && dirent->name[1] == '.' && dirent->name[2] == 0))
1437
                        continue;
1438
                    if ((type & CACHE1D_FIND_DIR) && !(dirent->mode & BS_IFDIR)) continue;
1439
                    if ((type & CACHE1D_FIND_FILE) && (dirent->mode & BS_IFDIR)) continue;
1440
                    if (!Bwildmatch(dirent->name, mask)) continue;
1441
                    switch (klistaddentry(&rec, dirent->name,
1442
                                          (dirent->mode & BS_IFDIR) ? CACHE1D_FIND_DIR : CACHE1D_FIND_FILE,
584 terminx 1443
                                          stackdepth))
1444
                    {
109 terminx 1445
                    case -1: goto failure;
1446
                        //case 1: initprintf("%s:%s dropped for lower priority\n", d,dirent->name); break;
1447
                        //case 0: initprintf("%s:%s accepted\n", d,dirent->name); break;
331 terminx 1448
                    default:
1449
                        break;
109 terminx 1450
                    }
1451
                }
1452
                Bclosedir(dir);
1453
            }
4188 helixhorne 1454
next:
1455
            if (pathsearchmode)
1456
                break;
99 terminx 1457
 
584 terminx 1458
            if (!search)
1459
            {
109 terminx 1460
                search = searchpathhead;
1461
                stackdepth = CACHE1D_SOURCE_PATH;
584 terminx 1462
            }
1463
            else
1464
            {
109 terminx 1465
                search = search->next;
1466
                stackdepth++;
1467
            }
4188 helixhorne 1468
 
1469
            if (search)
1470
                d = search->path;
584 terminx 1471
        }
1472
        while (search);
109 terminx 1473
    }
1474
 
3484 helixhorne 1475
#ifdef WITHKPLIB
4188 helixhorne 1476
    if (!(type & CACHE1D_FIND_NOCURDIR))  // TEMP, until we have sorted out fs.listpath() API
584 terminx 1477
    if (!pathsearchmode)        // next, zip files
1478
    {
1479 terminx 1479
        char buf[BMAX_PATH+4];
1205 terminx 1480
        int32_t i, j, ftype;
109 terminx 1481
        strcpy(buf,path);
1482
        if (*path) strcat(buf,"/");
1483
        strcat(buf,mask);
584 terminx 1484
        for (kzfindfilestart(buf); kzfindfile(buf);)
1485
        {
109 terminx 1486
            if (buf[0] != '|') continue;        // local files we don't need
1487
 
1488
            // scan for the end of the string and shift
1489
            // everything left a char in the process
2416 helixhorne 1490
            for (i=1; (buf[i-1]=buf[i]); i++)
1491
            {
1492
                /* do nothing */
1493
            }
1494
            i-=2;
4543 hendricks2 1495
            if (i < 0)
1496
                i = 0;
109 terminx 1497
 
1498
            // if there's a slash at the end, this is a directory entry
584 terminx 1499
            if (toupperlookup[buf[i]] == '/') { ftype = CACHE1D_FIND_DIR; buf[i] = 0; }
109 terminx 1500
            else ftype = CACHE1D_FIND_FILE;
1501
 
1502
            // skip over the common characters at the beginning of the base path and the zip entry
584 terminx 1503
            for (j=0; buf[j] && path[j]; j++)
1504
            {
109 terminx 1505
                if (toupperlookup[ path[j] ] == toupperlookup[ buf[j] ]) continue;
1506
                break;
1507
            }
1508
            // we've now hopefully skipped the common path component at the beginning.
1509
            // if that's true, we should be staring at a null byte in path and either any character in buf
1510
            // if j==0, or a slash if j>0
584 terminx 1511
            if ((!path[0] && buf[j]) || (!path[j] && toupperlookup[ buf[j] ] == '/'))
1512
            {
109 terminx 1513
                if (j>0) j++;
1514
 
1515
                // yep, so now we shift what follows back to the start of buf and while we do that,
1516
                // keep an eye out for any more slashes which would mean this entry has sub-entities
1517
                // and is useless to us.
1518
                for (i = 0; (buf[i] = buf[j]) && toupperlookup[buf[j]] != '/'; i++,j++) ;
1519
                if (toupperlookup[buf[j]] == '/') continue;     // damn, try next entry
584 terminx 1520
            }
1521
            else
1522
            {
109 terminx 1523
                // if we're here it means we have a situation where:
1524
                //   path = foo
1525
                //   buf = foobar...
1526
                // or
1527
                //   path = foobar
1528
                //   buf = foo...
1529
                // which would mean the entry is higher up in the directory tree and is also useless
1530
                continue;
1531
            }
1532
 
1533
            if ((type & CACHE1D_FIND_DIR) && ftype != CACHE1D_FIND_DIR) continue;
1534
            if ((type & CACHE1D_FIND_FILE) && ftype != CACHE1D_FIND_FILE) continue;
1535
 
1536
            // the entry is in the clear
584 terminx 1537
            switch (klistaddentry(&rec, buf, ftype, CACHE1D_SOURCE_ZIP))
1538
            {
331 terminx 1539
            case -1:
1540
                goto failure;
109 terminx 1541
                //case 1: initprintf("<ZIP>:%s dropped for lower priority\n", buf); break;
1542
                //case 0: initprintf("<ZIP>:%s accepted\n", buf); break;
331 terminx 1543
            default:
1544
                break;
109 terminx 1545
            }
1546
        }
1547
    }
3484 helixhorne 1548
#endif
109 terminx 1549
    // then, grp files
4188 helixhorne 1550
    if (!(type & CACHE1D_FIND_NOCURDIR))  // TEMP, until we have sorted out fs.listpath() API
584 terminx 1551
    if (!pathsearchmode && !*path && (type & CACHE1D_FIND_FILE))
1552
    {
109 terminx 1553
        char buf[13];
1205 terminx 1554
        int32_t i,j;
109 terminx 1555
        buf[12] = 0;
1229 terminx 1556
        for (i=0; i<MAXGROUPFILES; i++)
584 terminx 1557
        {
109 terminx 1558
            if (groupfil[i] == -1) continue;
1229 terminx 1559
            for (j=gnumfiles[i]-1; j>=0; j--)
109 terminx 1560
            {
1561
                Bmemcpy(buf,&gfilelist[i][j<<4],12);
1562
                if (!Bwildmatch(buf,mask)) continue;
584 terminx 1563
                switch (klistaddentry(&rec, buf, CACHE1D_FIND_FILE, CACHE1D_SOURCE_GRP))
1564
                {
331 terminx 1565
                case -1:
1566
                    goto failure;
109 terminx 1567
                    //case 1: initprintf("<GRP>:%s dropped for lower priority\n", workspace); break;
1568
                    //case 0: initprintf("<GRP>:%s accepted\n", workspace); break;
331 terminx 1569
                default:
1570
                    break;
109 terminx 1571
                }
1572
            }
1573
        }
1574
    }
1575
 
584 terminx 1576
    if (pathsearchmode && (type & CACHE1D_FIND_DRIVE))
1577
    {
109 terminx 1578
        char *drives, *drp;
1579
        drives = Bgetsystemdrives();
584 terminx 1580
        if (drives)
1581
        {
1582
            for (drp=drives; *drp; drp+=strlen(drp)+1)
1583
            {
1584
                if (klistaddentry(&rec, drp, CACHE1D_FIND_DRIVE, CACHE1D_SOURCE_DRIVE) < 0)
1585
                {
1527 terminx 1586
                    Bfree(drives);
109 terminx 1587
                    goto failure;
1588
                }
1589
            }
1527 terminx 1590
            Bfree(drives);
109 terminx 1591
        }
1592
    }
1593
 
1527 terminx 1594
    Bfree(path);
4175 helixhorne 1595
    // XXX: may be NULL if no file was listed, and thus indistinguishable from
1596
    // an error condition.
109 terminx 1597
    return rec;
99 terminx 1598
failure:
1527 terminx 1599
    Bfree(path);
109 terminx 1600
    klistfree(rec);
1601
    return NULL;
99 terminx 1602
}
1603
 
3153 helixhorne 1604
 
1605
#endif // #ifdef CACHE1D_COMPRESS_ONLY / else
1606
 
1607
 
109 terminx 1608
//Internal LZW variables
99 terminx 1609
#define LZWSIZE 16384           //Watch out for shorts!
2366 helixhorne 1610
#define LZWSIZEPAD (LZWSIZE+(LZWSIZE>>4))
99 terminx 1611
 
3880 helixhorne 1612
// lzwrawbuf LZWSIZE+1 (formerly): see (*) below
1613
// XXX: lzwrawbuf size increased again :-/
1614
static char lzwtmpbuf[LZWSIZEPAD], lzwrawbuf[LZWSIZEPAD], lzwcompbuf[LZWSIZEPAD];
2366 helixhorne 1615
static int16_t lzwbuf2[LZWSIZEPAD], lzwbuf3[LZWSIZEPAD];
1616
 
2361 helixhorne 1617
static int32_t lzwcompress(const char *lzwinbuf, int32_t uncompleng, char *lzwoutbuf);
1618
static int32_t lzwuncompress(const char *lzwinbuf, int32_t compleng, char *lzwoutbuf);
99 terminx 1619
 
3153 helixhorne 1620
#ifndef CACHE1D_COMPRESS_ONLY
1621
static int32_t kdfread_func(intptr_t fil, void *outbuf, int32_t length)
99 terminx 1622
{
3153 helixhorne 1623
    return kread((int32_t)fil, outbuf, length);
1624
}
1625
 
1626
static void dfwrite_func(intptr_t fp, const void *inbuf, int32_t length)
1627
{
1628
    Bfwrite(inbuf, length, 1, (BFILE *)fp);
1629
}
1630
#else
1631
# define kdfread_func NULL
1632
# define dfwrite_func NULL
1633
#endif
1634
 
1635
// These two follow the argument order of the C functions "read" and "write":
1636
// handle, buffer, length.
1637
C1D_STATIC int32_t (*c1d_readfunc)(intptr_t, void *, int32_t) = kdfread_func;
1638
C1D_STATIC void (*c1d_writefunc)(intptr_t, const void *, int32_t) = dfwrite_func;
1639
 
1640
 
1641
////////// COMPRESSED READ //////////
1642
 
3879 helixhorne 1643
static uint32_t decompress_part(intptr_t f, uint32_t *kgoalptr)
3153 helixhorne 1644
{
3879 helixhorne 1645
    int16_t leng;
1646
 
1647
    // Read compressed length first.
1648
    if (c1d_readfunc(f, &leng, 2) != 2)
3153 helixhorne 1649
        return 1;
3879 helixhorne 1650
    leng = B_LITTLE16(leng);
1651
 
3153 helixhorne 1652
    if (c1d_readfunc(f,lzwcompbuf, leng) != leng)
1653
        return 1;
3879 helixhorne 1654
 
1655
    *kgoalptr = lzwuncompress(lzwcompbuf, leng, lzwrawbuf);
3153 helixhorne 1656
    return 0;
1657
}
1658
 
1659
// Read from 'f' into 'buffer'.
1660
C1D_STATIC int32_t c1d_read_compressed(void *buffer, bsize_t dasizeof, bsize_t count, intptr_t f)
1661
{
4837 helixhorne 1662
    char *ptr = (char *)buffer;
99 terminx 1663
 
2369 helixhorne 1664
    if (dasizeof > LZWSIZE)
1665
    {
4838 helixhorne 1666
        count *= dasizeof;
1667
        dasizeof = 1;
2369 helixhorne 1668
    }
1669
 
4837 helixhorne 1670
    uint32_t kgoal;
99 terminx 1671
 
4837 helixhorne 1672
    if (decompress_part(f, &kgoal))
1673
        return -1;
99 terminx 1674
 
3153 helixhorne 1675
    Bmemcpy(ptr, lzwrawbuf, (int32_t)dasizeof);
99 terminx 1676
 
4837 helixhorne 1677
    uint32_t k = (int32_t)dasizeof;
1678
 
1679
    for (uint32_t i=1; i<count; i++)
109 terminx 1680
    {
1681
        if (k >= kgoal)
1682
        {
3879 helixhorne 1683
            k = decompress_part(f, &kgoal);
3153 helixhorne 1684
            if (k) return -1;
109 terminx 1685
        }
2368 helixhorne 1686
 
4837 helixhorne 1687
        uint32_t j = 0;
4665 terminx 1688
 
1689
        if (dasizeof >= 4)
1690
        {
1691
            for (; j<dasizeof-4; j+=4)
1692
            {
4837 helixhorne 1693
                ptr[j+dasizeof] = ((ptr[j]+lzwrawbuf[j+k])&255);
1694
                ptr[j+1+dasizeof] = ((ptr[j+1]+lzwrawbuf[j+1+k])&255);
1695
                ptr[j+2+dasizeof] = ((ptr[j+2]+lzwrawbuf[j+2+k])&255);
1696
                ptr[j+3+dasizeof] = ((ptr[j+3]+lzwrawbuf[j+3+k])&255);
4665 terminx 1697
            }
1698
        }
1699
 
1700
        for (; j<dasizeof; j++)
2368 helixhorne 1701
            ptr[j+dasizeof] = ((ptr[j]+lzwrawbuf[j+k])&255);
1702
 
109 terminx 1703
        k += dasizeof;
1704
        ptr += dasizeof;
1705
    }
2366 helixhorne 1706
 
109 terminx 1707
    return count;
99 terminx 1708
}
1709
 
3153 helixhorne 1710
int32_t kdfread(void *buffer, bsize_t dasizeof, bsize_t count, int32_t fil)
2369 helixhorne 1711
{
3153 helixhorne 1712
    return c1d_read_compressed(buffer, dasizeof, count, (intptr_t)fil);
1713
}
2369 helixhorne 1714
 
1715
 
3153 helixhorne 1716
////////// COMPRESSED WRITE //////////
1717
 
1718
static uint32_t compress_part(uint32_t k, intptr_t f)
1719
{
3879 helixhorne 1720
    const int16_t leng = (int16_t)lzwcompress(lzwrawbuf, k, lzwcompbuf);
1721
    const int16_t swleng = B_LITTLE16(leng);
1722
 
3153 helixhorne 1723
    c1d_writefunc(f, &swleng, 2);
1724
    c1d_writefunc(f, lzwcompbuf, leng);
3879 helixhorne 1725
 
2369 helixhorne 1726
    return 0;
1727
}
1728
 
3153 helixhorne 1729
// Write from 'buffer' to 'f'.
1730
C1D_STATIC void c1d_write_compressed(const void *buffer, bsize_t dasizeof, bsize_t count, intptr_t f)
99 terminx 1731
{
4665 terminx 1732
    const char *ptr = (char*)buffer;
99 terminx 1733
 
4838 helixhorne 1734
    if (dasizeof > LZWSIZE)
2369 helixhorne 1735
    {
4838 helixhorne 1736
        count *= dasizeof;
1737
        dasizeof = 1;
2369 helixhorne 1738
    }
1739
 
1740
    Bmemcpy(lzwrawbuf, ptr, (int32_t)dasizeof);
1741
 
4837 helixhorne 1742
    uint32_t k = dasizeof;
109 terminx 1743
    if (k > LZWSIZE-dasizeof)
3153 helixhorne 1744
        k = compress_part(k, f);
99 terminx 1745
 
4837 helixhorne 1746
    for (uint32_t i=1; i<count; i++)
109 terminx 1747
    {
4837 helixhorne 1748
        uint32_t j = 0;
4665 terminx 1749
 
1750
        if (dasizeof >= 4)
1751
        {
1752
            for (; j<dasizeof-4; j+=4)
1753
            {
4837 helixhorne 1754
                lzwrawbuf[j+k] = ((ptr[j+dasizeof]-ptr[j])&255);
1755
                lzwrawbuf[j+1+k] = ((ptr[j+1+dasizeof]-ptr[j+1])&255);
1756
                lzwrawbuf[j+2+k] = ((ptr[j+2+dasizeof]-ptr[j+2])&255);
1757
                lzwrawbuf[j+3+k] = ((ptr[j+3+dasizeof]-ptr[j+3])&255);
4665 terminx 1758
            }
1759
        }
1760
 
1761
        for (; j<dasizeof; j++)
2368 helixhorne 1762
            lzwrawbuf[j+k] = ((ptr[j+dasizeof]-ptr[j])&255);
2369 helixhorne 1763
 
109 terminx 1764
        k += dasizeof;
1765
        if (k > LZWSIZE-dasizeof)
3153 helixhorne 1766
            k = compress_part(k, f);
2369 helixhorne 1767
 
109 terminx 1768
        ptr += dasizeof;
1769
    }
2369 helixhorne 1770
 
109 terminx 1771
    if (k > 0)
3153 helixhorne 1772
        compress_part(k, f);
99 terminx 1773
}
1774
 
3153 helixhorne 1775
void dfwrite(const void *buffer, bsize_t dasizeof, bsize_t count, BFILE *fil)
1776
{
1777
    c1d_write_compressed(buffer, dasizeof, count, (intptr_t)fil);
1778
}
1779
 
1780
////////// CORE COMPRESSION FUNCTIONS //////////
1781
 
2361 helixhorne 1782
static int32_t lzwcompress(const char *lzwinbuf, int32_t uncompleng, char *lzwoutbuf)
99 terminx 1783
{
2369 helixhorne 1784
    int32_t i, addr, addrcnt, *intptr;
1205 terminx 1785
    int32_t bytecnt1, bitcnt, numbits, oneupnumbits;
1786
    int16_t *shortptr;
99 terminx 1787
 
2369 helixhorne 1788
    int16_t *const lzwcodehead = lzwbuf2;
1789
    int16_t *const lzwcodenext = lzwbuf3;
1790
 
4665 terminx 1791
    for (i=255; i>=4; i-=4)
2368 helixhorne 1792
    {
4665 terminx 1793
        lzwtmpbuf[i]   = i,   lzwcodenext[i]   = (i+1)&255;
1794
        lzwtmpbuf[i-1] = i-1, lzwcodenext[i-1] = (i)  &255;
1795
        lzwtmpbuf[i-2] = i-2, lzwcodenext[i-2] = (i-1)&255;
1796
        lzwtmpbuf[i-3] = i-3, lzwcodenext[i-3] = (i-2)&255;
1797
        lzwcodehead[i] = lzwcodehead[i-1] = lzwcodehead[i-2] = lzwcodehead[i-3] = -1;
1798
    }
1799
 
1800
    for (; i>=0; i--)
1801
    {
2368 helixhorne 1802
        lzwtmpbuf[i] = i;
2369 helixhorne 1803
        lzwcodenext[i] = (i+1)&255;
1804
        lzwcodehead[i] = -1;
2368 helixhorne 1805
    }
1806
 
2369 helixhorne 1807
    Bmemset(lzwoutbuf, 0, 4+uncompleng+1);
1808
//    clearbuf(lzwoutbuf,((uncompleng+15)+3)>>2,0L);
99 terminx 1809
 
109 terminx 1810
    addrcnt = 256; bytecnt1 = 0; bitcnt = (4<<3);
1811
    numbits = 8; oneupnumbits = (1<<8);
1812
    do
1813
    {
1814
        addr = lzwinbuf[bytecnt1];
1815
        do
1816
        {
2369 helixhorne 1817
            int32_t newaddr;
1818
 
4665 terminx 1819
            if (++bytecnt1 == uncompleng)
2369 helixhorne 1820
                break;  // (*) see XXX below
2368 helixhorne 1821
 
2369 helixhorne 1822
            if (lzwcodehead[addr] < 0)
1823
            {
1824
                lzwcodehead[addr] = addrcnt;
1825
                break;
1826
            }
2368 helixhorne 1827
 
2369 helixhorne 1828
            newaddr = lzwcodehead[addr];
2367 helixhorne 1829
            while (lzwtmpbuf[newaddr] != lzwinbuf[bytecnt1])
109 terminx 1830
            {
2369 helixhorne 1831
                if (lzwcodenext[newaddr] < 0)
1832
                {
1833
                    lzwcodenext[newaddr] = addrcnt;
1834
                    break;
1835
                }
1836
                newaddr = lzwcodenext[newaddr];
109 terminx 1837
            }
2368 helixhorne 1838
 
2369 helixhorne 1839
            if (lzwcodenext[newaddr] == addrcnt)
1840
                break;
109 terminx 1841
            addr = newaddr;
584 terminx 1842
        }
1843
        while (addr >= 0);
2368 helixhorne 1844
 
2369 helixhorne 1845
        lzwtmpbuf[addrcnt] = lzwinbuf[bytecnt1];  // XXX: potential oob access of lzwinbuf via (*) above
1846
        lzwcodehead[addrcnt] = -1;
1847
        lzwcodenext[addrcnt] = -1;
99 terminx 1848
 
1205 terminx 1849
        intptr = (int32_t *)&lzwoutbuf[bitcnt>>3];
109 terminx 1850
        intptr[0] |= B_LITTLE32(addr<<(bitcnt&7));
1851
        bitcnt += numbits;
1852
        if ((addr&((oneupnumbits>>1)-1)) > ((addrcnt-1)&((oneupnumbits>>1)-1)))
1853
            bitcnt--;
99 terminx 1854
 
109 terminx 1855
        addrcnt++;
2368 helixhorne 1856
        if (addrcnt > oneupnumbits)
1857
            { numbits++; oneupnumbits <<= 1; }
584 terminx 1858
    }
1859
    while ((bytecnt1 < uncompleng) && (bitcnt < (uncompleng<<3)));
99 terminx 1860
 
1205 terminx 1861
    intptr = (int32_t *)&lzwoutbuf[bitcnt>>3];
109 terminx 1862
    intptr[0] |= B_LITTLE32(addr<<(bitcnt&7));
1863
    bitcnt += numbits;
1864
    if ((addr&((oneupnumbits>>1)-1)) > ((addrcnt-1)&((oneupnumbits>>1)-1)))
1865
        bitcnt--;
99 terminx 1866
 
1205 terminx 1867
    shortptr = (int16_t *)lzwoutbuf;
1868
    shortptr[0] = B_LITTLE16((int16_t)uncompleng);
3879 helixhorne 1869
 
109 terminx 1870
    if (((bitcnt+7)>>3) < uncompleng)
1871
    {
1205 terminx 1872
        shortptr[1] = B_LITTLE16((int16_t)addrcnt);
3879 helixhorne 1873
        return (bitcnt+7)>>3;
109 terminx 1874
    }
2368 helixhorne 1875
 
3879 helixhorne 1876
    // Failed compressing, mark this in the stream.
2368 helixhorne 1877
    shortptr[1] = 0;
4665 terminx 1878
 
1879
    for (i=0; i<uncompleng-4; i+=4)
1880
    {
2368 helixhorne 1881
        lzwoutbuf[i+4] = lzwinbuf[i];
4665 terminx 1882
        lzwoutbuf[i+5] = lzwinbuf[i+1];
1883
        lzwoutbuf[i+6] = lzwinbuf[i+2];
1884
        lzwoutbuf[i+7] = lzwinbuf[i+3];
1885
    }
2368 helixhorne 1886
 
4665 terminx 1887
    for (; i<uncompleng; i++)
1888
        lzwoutbuf[i+4] = lzwinbuf[i];
1889
 
3879 helixhorne 1890
    return uncompleng+4;
99 terminx 1891
}
1892
 
2361 helixhorne 1893
static int32_t lzwuncompress(const char *lzwinbuf, int32_t compleng, char *lzwoutbuf)
99 terminx 1894
{
2368 helixhorne 1895
    int32_t currstr, numbits, oneupnumbits;
3879 helixhorne 1896
    int32_t i, bitcnt, outbytecnt;
99 terminx 1897
 
2368 helixhorne 1898
    const int16_t *const shortptr = (const int16_t *)lzwinbuf;
1899
    const int32_t strtot = B_LITTLE16(shortptr[1]);
1900
    const int32_t uncompleng = B_LITTLE16(shortptr[0]);
1901
 
109 terminx 1902
    if (strtot == 0)
1903
    {
3879 helixhorne 1904
        if (lzwoutbuf==lzwrawbuf && lzwinbuf==lzwcompbuf)
1905
        {
1906
            Bassert((compleng-4)+3+0u < sizeof(lzwrawbuf));
1907
            Bassert((compleng-4)+3+0u < sizeof(lzwcompbuf)-4);
1908
        }
1909
 
3153 helixhorne 1910
        Bmemcpy(lzwoutbuf, lzwinbuf+4, (compleng-4)+3);
2368 helixhorne 1911
        return uncompleng;
109 terminx 1912
    }
2368 helixhorne 1913
 
4665 terminx 1914
    for (i=255; i>=4; i-=4)
2368 helixhorne 1915
    {
4665 terminx 1916
        lzwbuf2[i]   = lzwbuf3[i]   = i;
1917
        lzwbuf2[i-1] = lzwbuf3[i-1] = i-1;
1918
        lzwbuf2[i-2] = lzwbuf3[i-2] = i-2;
1919
        lzwbuf2[i-3] = lzwbuf3[i-3] = i-3;
2368 helixhorne 1920
    }
1921
 
4665 terminx 1922
    lzwbuf2[i]   = lzwbuf3[i]   = i;
1923
    lzwbuf2[i-1] = lzwbuf3[i-1] = i-1;
1924
    lzwbuf2[i-2] = lzwbuf3[i-2] = i-2;
1925
 
109 terminx 1926
    currstr = 256; bitcnt = (4<<3); outbytecnt = 0;
1927
    numbits = 8; oneupnumbits = (1<<8);
1928
    do
1929
    {
2368 helixhorne 1930
        const int32_t *const intptr = (const int32_t *)&lzwinbuf[bitcnt>>3];
1931
 
3879 helixhorne 1932
        int32_t dat = ((B_LITTLE32(intptr[0])>>(bitcnt&7)) & (oneupnumbits-1));
1933
        int32_t leng;
1934
 
109 terminx 1935
        bitcnt += numbits;
1936
        if ((dat&((oneupnumbits>>1)-1)) > ((currstr-1)&((oneupnumbits>>1)-1)))
584 terminx 1937
            { dat &= ((oneupnumbits>>1)-1); bitcnt--; }
99 terminx 1938
 
109 terminx 1939
        lzwbuf3[currstr] = dat;
99 terminx 1940
 
1229 terminx 1941
        for (leng=0; dat>=256; leng++,dat=lzwbuf3[dat])
2367 helixhorne 1942
            lzwtmpbuf[leng] = lzwbuf2[dat];
99 terminx 1943
 
109 terminx 1944
        lzwoutbuf[outbytecnt++] = dat;
4665 terminx 1945
 
1946
        for (i=leng-1; i>=4; i-=4, outbytecnt+=4)
1947
        {
1948
            lzwoutbuf[outbytecnt]   = lzwtmpbuf[i];
1949
            lzwoutbuf[outbytecnt+1] = lzwtmpbuf[i-1];
1950
            lzwoutbuf[outbytecnt+2] = lzwtmpbuf[i-2];
1951
            lzwoutbuf[outbytecnt+3] = lzwtmpbuf[i-3];
1952
        }
1953
 
1954
        for (; i>=0; i--)
2368 helixhorne 1955
            lzwoutbuf[outbytecnt++] = lzwtmpbuf[i];
99 terminx 1956
 
109 terminx 1957
        lzwbuf2[currstr-1] = dat; lzwbuf2[currstr] = dat;
1958
        currstr++;
2368 helixhorne 1959
        if (currstr > oneupnumbits)
1960
            { numbits++; oneupnumbits <<= 1; }
584 terminx 1961
    }
1962
    while (currstr < strtot);
2368 helixhorne 1963
 
1964
    return uncompleng;
99 terminx 1965
}
1966
 
1967
/*
1968
 * vim:ts=4:sw=4:
1969
 */