Subversion Repositories eduke32

Rev

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