1 
2 #ifdef _WIN32
3 // for FILENAME_CASE_CHECK
4 # define NEED_SHELLAPI_H
5 # include "windows_inc.h"
6 #endif
7 
8 #include "baselayer.h"
9 #include "cache1d.h"
10 #include "compat.h"
11 #include "klzw.h"
12 #include "lz4.h"
13 #include "osd.h"
14 #include "pragmas.h"
15 #include "vfs.h"
16 #include "cache1d.h"
17 
18 #ifdef __cplusplus
19 extern "C" {
20 #endif
21 char toupperlookup[256] =
22 {
23     0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
24     0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
25     0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
26     0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
27     0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
28     0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,
29     0x60,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
30     0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x7b,0x7c,0x7d,0x7e,0x7f,
31     0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
32     0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
33     0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
34     0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
35     0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
36     0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,
37     0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,
38     0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff
39 };
40 #ifdef __cplusplus
41 }
42 #endif
43 #ifdef WITHKPLIB
44 #include "kplib.h"
45 
46 //Insert '|' in front of filename
47 //Doing this tells kzopen to load the file only if inside a .ZIP file
kzipopen(const char * filnam)48 static intptr_t kzipopen(const char *filnam)
49 {
50     uint32_t i;
51     char newst[BMAX_PATH+8];
52 
53     newst[0] = '|';
54     for (i=0; i < BMAX_PATH+4 && filnam[i]; i++) newst[i+1] = filnam[i];
55     newst[i+1] = 0;
56     return kzopen(newst);
57 }
58 
59 #endif
60 
61 char *kpzbuf = NULL;
62 int32_t kpzbufsiz;
63 
kpzbufloadfil(buildvfs_kfd const handle)64 int32_t kpzbufloadfil(buildvfs_kfd const handle)
65 {
66     int32_t const leng = kfilelength(handle);
67     if (leng > kpzbufsiz)
68     {
69         kpzbuf = (char *) Xrealloc(kpzbuf, leng+1);
70         kpzbufsiz = leng;
71         if (!kpzbuf)
72             return 0;
73     }
74 
75     kpzbuf[leng] = 0;  // FIXME: buf[leng] read in kpegrend(), see BUF_LENG_READ
76     kread(handle, kpzbuf, leng);
77 
78     return leng;
79 }
80 
kpzbufload(char const * const filnam)81 int32_t kpzbufload(char const * const filnam)
82 {
83     buildvfs_kfd const handle = kopen4load(filnam, 0);
84     if (handle == buildvfs_kfd_invalid)
85         return 0;
86 
87     int32_t const leng = kpzbufloadfil(handle);
88 
89     kclose(handle);
90 
91     return leng;
92 }
93 
94 #ifdef USE_PHYSFS
95 
96 int32_t numgroupfiles;
97 
uninitgroupfile(void)98 void uninitgroupfile(void)
99 {
100     PHYSFS_deinit();
101 }
102 
103 #include <sys/stat.h>
104 
klseek(buildvfs_kfd handle,int32_t offset,int32_t whence)105 int32_t klseek(buildvfs_kfd handle, int32_t offset, int32_t whence)
106 {
107     // TODO: replace klseek calls with _{abs,cur,end} versions
108 
109     if (whence == SEEK_CUR)
110         offset += PHYSFS_tell(handle);
111     else if (whence == SEEK_END)
112         offset += PHYSFS_fileLength(handle);
113 
114     PHYSFS_seek(handle, offset);
115     return PHYSFS_tell(handle);
116 }
117 
118 #endif
119 
120 #include <errno.h>
121 
122 typedef struct _searchpath
123 {
124     struct _searchpath *next;
125     char *path;
126     size_t pathlen;		// to save repeated calls to strlen()
127     int32_t user;
128 } searchpath_t;
129 static searchpath_t *searchpathhead = NULL;
130 static size_t maxsearchpathlen = 0;
131 int32_t pathsearchmode = 0;
132 
133 #ifndef USE_PHYSFS
134 
listsearchpath(int32_t initp)135 char *listsearchpath(int32_t initp)
136 {
137     static searchpath_t *sp;
138 
139     if (initp)
140         sp = searchpathhead;
141     else if (sp != NULL)
142         sp = sp->next;
143 
144     return sp ? sp->path : NULL;
145 }
146 
addsearchpath_user(const char * p,int32_t user)147 int32_t addsearchpath_user(const char *p, int32_t user)
148 {
149     struct Bstat st;
150     char *s;
151     searchpath_t *srch;
152     char *path = Xstrdup(p);
153 
154     if (path[Bstrlen(path)-1] == '\\')
155         path[Bstrlen(path)-1] = 0; // hack for stat() returning ENOENT on paths ending in a backslash
156 
157     if (Bstat(path, &st) < 0)
158     {
159         Xfree(path);
160         if (errno == ENOENT) return -2;
161         return -1;
162     }
163     if (!(st.st_mode & BS_IFDIR))
164     {
165         Xfree(path);
166         return -1;
167     }
168 
169     srch = (searchpath_t *)Xmalloc(sizeof(searchpath_t));
170 
171     srch->next    = searchpathhead;
172     srch->pathlen = Bstrlen(path)+1;
173     srch->path    = (char *)Xmalloc(srch->pathlen + 1);
174 
175     Bstrcpy(srch->path, path);
176     for (s=srch->path; *s; s++) { }
177     s--;
178 
179     if (s<srch->path || toupperlookup[*s] != '/')
180         Bstrcat(srch->path, "/");
181 
182     searchpathhead = srch;
183     if (srch->pathlen > maxsearchpathlen)
184         maxsearchpathlen = srch->pathlen;
185 
186     Bcorrectfilename(srch->path,0);
187 
188     srch->user = user;
189 
190     initprintf("Using %s for game data\n", srch->path);
191 
192     Xfree(path);
193     return 0;
194 }
195 
removesearchpath(const char * p)196 int32_t removesearchpath(const char *p)
197 {
198     searchpath_t *srch;
199     char *s;
200     char *path = (char *)Xmalloc(Bstrlen(p) + 2);
201 
202     Bstrcpy(path, p);
203 
204     if (path[Bstrlen(path)-1] == '\\')
205         path[Bstrlen(path)-1] = 0;
206 
207     for (s=path; *s; s++) { }
208     s--;
209 
210     if (s<path || toupperlookup[*s] != '/')
211         Bstrcat(path, "/");
212 
213     Bcorrectfilename(path,0);
214 
215     for (srch = searchpathhead; srch; srch = srch->next)
216     {
217         if (!Bstrncmp(path, srch->path, srch->pathlen))
218         {
219 //            initprintf("Removing %s from path stack\n", path);
220 
221             if (srch == searchpathhead)
222                 searchpathhead = srch->next;
223             else
224             {
225                 searchpath_t *sp;
226 
227                 for (sp = searchpathhead; sp; sp = sp->next)
228                 {
229                     if (sp->next == srch)
230                     {
231 //                        initprintf("matched %s\n", srch->path);
232                         sp->next = srch->next;
233                         break;
234                     }
235                 }
236             }
237 
238             Xfree(srch->path);
239             Xfree(srch);
240             break;
241         }
242     }
243 
244     Xfree(path);
245     return 0;
246 }
247 
removesearchpaths_withuser(int32_t usermask)248 void removesearchpaths_withuser(int32_t usermask)
249 {
250     searchpath_t *next;
251 
252     for (searchpath_t *srch = searchpathhead; srch; srch = next)
253     {
254         next = srch->next;
255 
256         if (srch->user & usermask)
257         {
258 
259             if (srch == searchpathhead)
260                 searchpathhead = srch->next;
261             else
262             {
263                 searchpath_t *sp;
264 
265                 for (sp = searchpathhead; sp; sp = sp->next)
266                 {
267                     if (sp->next == srch)
268                     {
269                         sp->next = srch->next;
270                         break;
271                     }
272                 }
273             }
274 
275             Xfree(srch->path);
276             Xfree(srch);
277         }
278     }
279 }
280 
findfrompath(const char * fn,char ** where)281 int32_t findfrompath(const char *fn, char **where)
282 {
283     // pathsearchmode == 0: tests current dir and then the dirs of the path stack
284     // pathsearchmode == 1: tests fn without modification, then like for pathsearchmode == 0
285 
286     if (pathsearchmode)
287     {
288         // test unmolested filename first
289         if (buildvfs_exists(fn))
290         {
291             *where = Xstrdup(fn);
292             return 0;
293         }
294 #ifndef _WIN32
295         else
296         {
297             char *tfn = Bstrtolower(Xstrdup(fn));
298 
299             if (buildvfs_exists(tfn))
300             {
301                 *where = tfn;
302                 return 0;
303             }
304 
305             Bstrupr(tfn);
306 
307             if (buildvfs_exists(tfn))
308             {
309                 *where = tfn;
310                 return 0;
311             }
312 
313             Xfree(tfn);
314         }
315 #endif
316     }
317 
318     char const *cpfn;
319 
320     for (cpfn = fn; toupperlookup[*cpfn] == '/'; cpfn++) { }
321     char *ffn = Xstrdup(cpfn);
322 
323     Bcorrectfilename(ffn,0);	// compress relative paths
324 
325     int32_t allocsiz = max<int>(maxsearchpathlen, 2);	// "./" (aka. curdir)
326     allocsiz += strlen(ffn);
327     allocsiz += 1;	// a nul
328 
329     char *pfn = (char *)Xmalloc(allocsiz);
330 
331     strcpy(pfn, "./");
332     strcat(pfn, ffn);
333     if (buildvfs_exists(pfn))
334     {
335         *where = pfn;
336         Xfree(ffn);
337         return 0;
338     }
339 
340     for (searchpath_t *sp = searchpathhead; sp; sp = sp->next)
341     {
342         char *tfn = Xstrdup(ffn);
343 
344         strcpy(pfn, sp->path);
345         strcat(pfn, ffn);
346         //initprintf("Trying %s\n", pfn);
347         if (buildvfs_exists(pfn))
348         {
349             *where = pfn;
350             Xfree(ffn);
351             Xfree(tfn);
352             return 0;
353         }
354 
355 #ifndef _WIN32
356         //Check with all lowercase
357         strcpy(pfn, sp->path);
358         Bstrtolower(tfn);
359         strcat(pfn, tfn);
360         if (buildvfs_exists(pfn))
361         {
362             *where = pfn;
363             Xfree(ffn);
364             Xfree(tfn);
365             return 0;
366         }
367 
368         //Check again with uppercase
369         strcpy(pfn, sp->path);
370         Bstrupr(tfn);
371         strcat(pfn, tfn);
372         if (buildvfs_exists(pfn))
373         {
374             *where = pfn;
375             Xfree(ffn);
376             Xfree(tfn);
377             return 0;
378         }
379 #endif
380         Xfree(tfn);
381     }
382 
383     Xfree(pfn); Xfree(ffn);
384     return -1;
385 }
386 
387 #if defined(_WIN32) && defined(DEBUGGINGAIDS)
388 # define FILENAME_CASE_CHECK
389 #endif
390 
openfrompath_internal(const char * fn,char ** where,int32_t flags,int32_t mode)391 static buildvfs_kfd openfrompath_internal(const char *fn, char **where, int32_t flags, int32_t mode)
392 {
393     if (findfrompath(fn, where) < 0)
394         return -1;
395 
396     return Bopen(*where, flags, mode);
397 }
398 
openfrompath(const char * fn,int32_t flags,int32_t mode)399 buildvfs_kfd openfrompath(const char *fn, int32_t flags, int32_t mode)
400 {
401     char *pfn = NULL;
402 
403     buildvfs_kfd h = openfrompath_internal(fn, &pfn, flags, mode);
404 
405     Xfree(pfn);
406 
407     return h;
408 }
409 
fopenfrompath(const char * fn,const char * mode)410 buildvfs_FILE fopenfrompath(const char *fn, const char *mode)
411 {
412     int32_t fh;
413     buildvfs_FILE h;
414     int32_t bmode = 0, smode = 0;
415     const char *c;
416 
417     for (c=mode; c[0];)
418     {
419         if (c[0] == 'r' && c[1] == '+') { bmode = BO_RDWR; smode = BS_IREAD|BS_IWRITE; c+=2; }
420         else if (c[0] == 'r')                { bmode = BO_RDONLY; smode = BS_IREAD; c+=1; }
421         else if (c[0] == 'w' && c[1] == '+') { bmode = BO_RDWR|BO_CREAT|BO_TRUNC; smode = BS_IREAD|BS_IWRITE; c+=2; }
422         else if (c[0] == 'w')                { bmode = BO_WRONLY|BO_CREAT|BO_TRUNC; smode = BS_IREAD|BS_IWRITE; c+=2; }
423         else if (c[0] == 'a' && c[1] == '+') { bmode = BO_RDWR|BO_CREAT; smode=BS_IREAD|BS_IWRITE; c+=2; }
424         else if (c[0] == 'a')                { bmode = BO_WRONLY|BO_CREAT; smode=BS_IREAD|BS_IWRITE; c+=1; }
425         else if (c[0] == 'b')                { bmode |= BO_BINARY; c+=1; }
426         else if (c[1] == 't')                { bmode |= BO_TEXT; c+=1; }
427         else c++;
428     }
429     fh = openfrompath(fn,bmode,smode);
430     if (fh < 0) return NULL;
431 
432     h = fdopen(fh,mode);
433     if (!h) close(fh);
434 
435     return h;
436 }
437 
438 #define MAXGROUPFILES 8     // Warning: Fix groupfil if this is changed
439 #define MAXOPENFILES 64     // Warning: Fix filehan if this is changed
440 
441 enum {
442     GRP_RESERVED_ID_START = 254,
443 
444     GRP_ZIP = GRP_RESERVED_ID_START,
445     GRP_FILESYSTEM = GRP_RESERVED_ID_START + 1,
446 };
447 
448 EDUKE32_STATIC_ASSERT(MAXGROUPFILES <= GRP_RESERVED_ID_START);
449 
450 int32_t numgroupfiles = 0;
451 static int32_t gnumfiles[MAXGROUPFILES];
452 static intptr_t groupfil[MAXGROUPFILES] = {-1,-1,-1,-1,-1,-1,-1,-1};
453 static int32_t groupfilpos[MAXGROUPFILES];
454 static uint8_t groupfilgrp[MAXGROUPFILES];
455 static char *gfilelist[MAXGROUPFILES];
456 static char *groupname[MAXGROUPFILES];
457 static int32_t *gfileoffs[MAXGROUPFILES];
458 
459 static uint8_t filegrp[MAXOPENFILES];
460 static int32_t filepos[MAXOPENFILES];
461 static intptr_t filehan[MAXOPENFILES] =
462 {
463     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
464     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
465     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
466     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
467 };
468 
469 #ifdef WITHKPLIB
470 static char filenamsav[MAXOPENFILES][260];
471 static int32_t kzcurhand = -1;
472 
cache1d_file_fromzip(buildvfs_kfd fil)473 int32_t cache1d_file_fromzip(buildvfs_kfd fil)
474 {
475     return (filegrp[fil] == GRP_ZIP);
476 }
477 #endif
478 
479 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);
480 static int32_t kread_grp(int32_t handle, void *buffer, int32_t leng);
481 static int32_t klseek_grp(int32_t handle, int32_t offset, int32_t whence);
482 static void kclose_grp(int32_t handle);
483 
initgroupfile(const char * filename)484 int initgroupfile(const char *filename)
485 {
486     char buf[70];
487 
488     // translate all backslashes (0x5c) to forward slashes (0x2f)
489     toupperlookup[0x5c] = 0x2f;
490 
491     if (filename == NULL)
492         return -1;
493 
494     // Technically you should be able to load more zips even if your GRPs are maxed out,
495     // but this system is already enough of a disaster.
496     if (numgroupfiles >= MAXGROUPFILES)
497         return -1;
498 
499     char *zfn = NULL;
500 
501     if (kopen_internal(filename, &zfn, 0, 0, 0, numgroupfiles, groupfilgrp, groupfil, groupfilpos) < 0)
502         return -1;
503 
504 #ifdef WITHKPLIB
505     // check if ZIP
506     if (zfn)
507     {
508         kread_grp(numgroupfiles, buf, 4);
509         if (buf[0] == 0x50 && buf[1] == 0x4B && buf[2] == 0x03 && buf[3] == 0x04)
510         {
511             kclose_grp(numgroupfiles);
512 
513             kzaddstack(zfn);
514             Xfree(zfn);
515             return MAXGROUPFILES;
516         }
517         klseek_grp(numgroupfiles,0,BSEEK_SET);
518 
519         Xfree(zfn);
520     }
521 #else
522     Xfree(zfn);
523 #endif
524 
525     // check if GRP
526     kread_grp(numgroupfiles,buf,16);
527     if (!Bmemcmp(buf, "KenSilverman", 12))
528     {
529         gnumfiles[numgroupfiles] = B_LITTLE32(*((int32_t *)&buf[12]));
530 
531         gfilelist[numgroupfiles] = (char *)Xmalloc(gnumfiles[numgroupfiles]<<4);
532         gfileoffs[numgroupfiles] = (int32_t *)Xmalloc((gnumfiles[numgroupfiles]+1)<<2);
533 
534         kread_grp(numgroupfiles,gfilelist[numgroupfiles],gnumfiles[numgroupfiles]<<4);
535 
536         int32_t j = (gnumfiles[numgroupfiles]+1)<<4;
537         for (bssize_t i=0; i<gnumfiles[numgroupfiles]; i++)
538         {
539             int32_t const k = B_LITTLE32(*((int32_t *)&gfilelist[numgroupfiles][(i<<4)+12]));
540             gfilelist[numgroupfiles][(i<<4)+12] = 0;
541             gfileoffs[numgroupfiles][i] = j;
542             j += k;
543         }
544         gfileoffs[numgroupfiles][gnumfiles[numgroupfiles]] = j;
545         groupname[numgroupfiles] = Xstrdup(filename);
546         return numgroupfiles++;
547     }
548     klseek_grp(numgroupfiles, 0, BSEEK_SET);
549 
550     // check if SSI
551     // this performs several checks because there is no "SSI" magic
552     int32_t version;
553     kread_grp(numgroupfiles, &version, 4);
554     version = B_LITTLE32(version);
555     while (version == 1 || version == 2) // if
556     {
557         char zerobuf[70];
558         Bmemset(zerobuf, 0, 70);
559 
560         int32_t numfiles;
561         kread_grp(numgroupfiles, &numfiles, 4);
562         numfiles = B_LITTLE32(numfiles);
563 
564         uint8_t temp, temp2;
565 
566         // get the string length
567         kread_grp(numgroupfiles, &temp, 1);
568         if (temp > 31) // 32 bytes allocated for the string
569             break;
570         // seek to the end of the string
571         klseek_grp(numgroupfiles, temp, BSEEK_CUR);
572         // verify everything remaining is a null terminator
573         temp = 32 - temp;
574         kread_grp(numgroupfiles, buf, temp);
575         if (Bmemcmp(buf, zerobuf, temp))
576             break;
577 
578         if (version == 2)
579         {
580             // get the string length
581             kread_grp(numgroupfiles, &temp, 1);
582             if (temp > 11) // 12 bytes allocated for the string
583                 break;
584             // seek to the end of the string
585             klseek_grp(numgroupfiles, temp, BSEEK_CUR);
586             // verify everything remaining is a null terminator
587             temp = 12 - temp;
588             kread_grp(numgroupfiles, buf, temp);
589             if (Bmemcmp(buf, zerobuf, temp))
590                 break;
591         }
592 
593         temp2 = 0;
594         for (int i=0;i<3;i++)
595         {
596             // get the string length
597             kread_grp(numgroupfiles, &temp, 1);
598             if (temp > 70) // 70 bytes allocated for the string
599             {
600                 temp2 = 1;
601                 break;
602             }
603             // seek to the end of the string
604             klseek_grp(numgroupfiles, temp, BSEEK_CUR);
605             // verify everything remaining is a null terminator
606             temp = 70 - temp;
607             if (temp == 0)
608                 continue;
609             kread_grp(numgroupfiles, buf, temp);
610             temp2 |= !!Bmemcmp(buf, zerobuf, temp);
611         }
612         if (temp2)
613             break;
614 
615         // Passed all the tests: read data.
616 
617         gnumfiles[numgroupfiles] = numfiles;
618 
619         gfilelist[numgroupfiles] = (char *)Xmalloc(gnumfiles[numgroupfiles]<<4);
620         gfileoffs[numgroupfiles] = (int32_t *)Xmalloc((gnumfiles[numgroupfiles]+1)<<2);
621 
622         int32_t j = (version == 2 ? 267 : 254) + (numfiles * 121), k;
623         for (bssize_t i = 0; i < numfiles; i++)
624         {
625             // get the string length
626             kread_grp(numgroupfiles, &temp, 1);
627             if (temp > 12)
628                 temp = 12;
629             // read the file name
630             kread_grp(numgroupfiles, &gfilelist[numgroupfiles][i<<4], temp);
631             gfilelist[numgroupfiles][(i<<4)+temp] = 0;
632 
633             // skip to the end of the 12 bytes
634             klseek_grp(numgroupfiles, 12-temp, BSEEK_CUR);
635 
636             // get the file size
637             kread_grp(numgroupfiles, &k, 4);
638             k = B_LITTLE32(k);
639 
640             // record the offset of the file in the SSI
641             gfileoffs[numgroupfiles][i] = j;
642             j += k;
643 
644             // skip unknown data
645             klseek_grp(numgroupfiles, 104, BSEEK_CUR);
646         }
647         gfileoffs[numgroupfiles][gnumfiles[numgroupfiles]] = j;
648         groupname[numgroupfiles] = Xstrdup(filename);
649         return numgroupfiles++;
650     }
651 
652     kclose_grp(numgroupfiles);
653     return -1;
654 }
655 
uninitgroupfile(void)656 void uninitgroupfile(void)
657 {
658     int32_t i;
659 
660     for (i=numgroupfiles-1; i>=0; i--)
661         if (groupfil[i] != -1)
662         {
663             DO_FREE_AND_NULL(gfilelist[i]);
664             DO_FREE_AND_NULL(gfileoffs[i]);
665             DO_FREE_AND_NULL(groupname[i]);
666 
667             Bclose(groupfil[i]);
668             groupfil[i] = -1;
669         }
670     numgroupfiles = 0;
671 
672     // JBF 20040111: "close" any files open in groups
673     for (i=0; i<MAXOPENFILES; i++)
674     {
675         if (filegrp[i] < GRP_RESERVED_ID_START)   // JBF 20040130: not external or ZIPped
676             filehan[i] = -1;
677     }
678 }
679 
680 #ifdef FILENAME_CASE_CHECK
681 // See
682 // http://stackoverflow.com/questions/74451/getting-actual-file-name-with-proper-casing-on-windows
683 // for relevant discussion.
684 
685 static char fnbuf[BMAX_PATH];
686 int fnofs;
687 
688 int32_t (*check_filename_casing_fn)(void) = NULL;
689 
690 // -1: failure, 0: match, 1: mismatch
check_filename_mismatch(const char * const filename,int ofs)691 static int32_t check_filename_mismatch(const char * const filename, int ofs)
692 {
693     if (!GetShortPathNameA(filename, fnbuf, BMAX_PATH)) return -1;
694     if (!GetLongPathNameA(fnbuf, fnbuf, BMAX_PATH)) return -1;
695 
696     fnofs = ofs;
697 
698     int len = Bstrlen(fnbuf+ofs);
699 
700     char const * const fn = filename+ofs;
701 
702     if (!Bstrncmp(fnbuf+ofs, fn, len))
703         return 0;
704 
705     char * const tfn = Bstrtolower(Xstrdup(fn));
706 
707     if (!Bstrncmp(fnbuf+ofs, tfn, len))
708     {
709         Xfree(tfn);
710         return 0;
711     }
712 
713     Bstrupr(tfn);
714 
715     if (!Bstrncmp(fnbuf+ofs, tfn, len))
716     {
717         Xfree(tfn);
718         return 0;
719     }
720 
721     Xfree(tfn);
722 
723     return 1;
724 }
725 #endif
726 
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)727 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)
728 {
729     buildvfs_kfd fil;
730     if (searchfirst == 0 && (fil = openfrompath_internal(filename, lastpfn, BO_BINARY|BO_RDONLY, BS_IREAD)) >= 0)
731     {
732 #ifdef FILENAME_CASE_CHECK
733         if (checkcase && check_filename_casing_fn && check_filename_casing_fn())
734         {
735             int32_t status;
736             char *cp, *lastslash;
737 
738             // convert all slashes to backslashes because SHGetFileInfo()
739             // complains else!
740             lastslash = *lastpfn;
741             for (cp=*lastpfn; *cp; cp++)
742                 if (*cp=='/')
743                 {
744                     *cp = '\\';
745                     lastslash = cp;
746                 }
747             if (lastslash != *lastpfn)
748                 lastslash++;
749 
750             status = check_filename_mismatch(*lastpfn, lastslash-*lastpfn);
751 
752             if (status == -1)
753             {
754 //                initprintf("SHGetFileInfo failed with error code %lu\n", GetLastError());
755             }
756             else if (status == 1)
757             {
758                 initprintf("warning: case mismatch: passed \"%s\", real \"%s\"\n",
759                            lastslash, fnbuf+fnofs);
760             }
761         }
762 #else
763         UNREFERENCED_PARAMETER(checkcase);
764 #endif
765         arraygrp[newhandle] = GRP_FILESYSTEM;
766         arrayhan[newhandle] = fil;
767         arraypos[newhandle] = 0;
768         return newhandle;
769     }
770 
771     for (; toupperlookup[*filename] == '/'; filename++) { }
772 
773 #ifdef WITHKPLIB
774     if (tryzip)
775     {
776         intptr_t i;
777         if ((kzcurhand != newhandle) && (kztell() >= 0))
778         {
779             if (kzcurhand >= 0) arraypos[kzcurhand] = kztell();
780             kzclose();
781             kzcurhand = -1;
782         }
783         if (searchfirst != 1 && (i = kzipopen(filename)) != 0)
784         {
785             kzcurhand = newhandle;
786             arraygrp[newhandle] = GRP_ZIP;
787             arrayhan[newhandle] = i;
788             arraypos[newhandle] = 0;
789             strcpy(filenamsav[newhandle],filename);
790             return newhandle;
791         }
792     }
793 #else
794     UNREFERENCED_PARAMETER(tryzip);
795 #endif
796 
797     for (bssize_t k = searchfirst != 1 ? numgroupfiles-1 : 0; k >= 0; --k)
798     {
799         if (groupfil[k] < 0)
800             continue;
801 
802         for (bssize_t i = gnumfiles[k]-1; i >= 0; --i)
803         {
804             char const * const gfileptr = (char *)&gfilelist[k][i<<4];
805 
806             unsigned int j;
807             for (j = 0; j < 13; ++j)
808             {
809                 if (!filename[j]) break;
810                 if (toupperlookup[filename[j]] != toupperlookup[gfileptr[j]])
811                     goto gnumfiles_continue;
812             }
813             if (j<13 && gfileptr[j]) continue;   // JBF: because e1l1.map might exist before e1l1
814             if (j==13 && filename[j]) continue;   // JBF: long file name
815 
816             arraygrp[newhandle] = k;
817             arrayhan[newhandle] = i;
818             arraypos[newhandle] = 0;
819             return newhandle;
820 
821 gnumfiles_continue: ;
822         }
823     }
824 
825     return -1;
826 }
827 
krename(int32_t crcval,int32_t filenum,const char * newname)828 void krename(int32_t crcval, int32_t filenum, const char *newname)
829 {
830     Bstrncpy((char *)&gfilelist[crcval][filenum<<4], newname, 12);
831 }
832 
kfileparent(int32_t const handle)833 char const * kfileparent(int32_t const handle)
834 {
835     int32_t const groupnum = filegrp[handle];
836 
837     if ((unsigned)groupnum >= MAXGROUPFILES || groupfil[groupnum] == -1)
838         return NULL;
839 
840     return groupname[groupnum];
841 }
842 
kopen4load(const char * filename,char searchfirst)843 int32_t kopen4load(const char *filename, char searchfirst)
844 {
845     int32_t newhandle = MAXOPENFILES-1;
846 
847     if (filename==NULL)
848         return -1;
849 
850     while (filehan[newhandle] != -1)
851     {
852         newhandle--;
853         if (newhandle < 0)
854         {
855             initprintf("TOO MANY FILES OPEN IN FILE GROUPING SYSTEM!");
856             Bexit(EXIT_SUCCESS);
857         }
858     }
859 
860     char *lastpfn = NULL;
861 
862     int32_t h = kopen_internal(filename, &lastpfn, searchfirst, 1, 1, newhandle, filegrp, filehan, filepos);
863 
864     Xfree(lastpfn);
865 
866     return h;
867 }
868 
869 char g_modDir[BMAX_PATH] = "/";
870 
kopen4loadfrommod(const char * fileName,char searchfirst)871 buildvfs_kfd kopen4loadfrommod(const char *fileName, char searchfirst)
872 {
873     buildvfs_kfd kFile = buildvfs_kfd_invalid;
874 
875     if (g_modDir[0] != '/' || g_modDir[1] != 0)
876     {
877         static char staticFileName[BMAX_PATH];
878         Bsnprintf(staticFileName, sizeof(staticFileName), "%s/%s", g_modDir, fileName);
879         kFile = kopen4load(staticFileName, searchfirst);
880     }
881 
882     return (kFile == buildvfs_kfd_invalid) ? kopen4load(fileName, searchfirst) : kFile;
883 }
884 
kread_internal(int32_t handle,void * buffer,int32_t leng,const uint8_t * arraygrp,const intptr_t * arrayhan,int32_t * arraypos)885 int32_t kread_internal(int32_t handle, void *buffer, int32_t leng, const uint8_t *arraygrp, const intptr_t *arrayhan, int32_t *arraypos)
886 {
887     int32_t filenum = arrayhan[handle];
888     int32_t groupnum = arraygrp[handle];
889 
890     if (groupnum == GRP_FILESYSTEM) return Bread(filenum,buffer,leng);
891 #ifdef WITHKPLIB
892     else if (groupnum == GRP_ZIP)
893     {
894         if (kzcurhand != handle)
895         {
896             if (kztell() >= 0) { arraypos[kzcurhand] = kztell(); kzclose(); }
897             kzcurhand = handle;
898             kzipopen(filenamsav[handle]);
899             kzseek(arraypos[handle],SEEK_SET);
900         }
901         return kzread(buffer,leng);
902     }
903 #endif
904 
905     if (EDUKE32_PREDICT_FALSE(groupfil[groupnum] == -1))
906         return 0;
907 
908     int32_t rootgroupnum = groupnum;
909     int32_t i = 0;
910     while (groupfilgrp[rootgroupnum] != GRP_FILESYSTEM)
911     {
912         i += gfileoffs[groupfilgrp[rootgroupnum]][groupfil[rootgroupnum]];
913         rootgroupnum = groupfilgrp[rootgroupnum];
914     }
915     if (EDUKE32_PREDICT_TRUE(groupfil[rootgroupnum] != -1))
916     {
917         i += gfileoffs[groupnum][filenum]+arraypos[handle];
918         if (i != groupfilpos[rootgroupnum])
919         {
920             Blseek(groupfil[rootgroupnum],i,BSEEK_SET);
921             groupfilpos[rootgroupnum] = i;
922         }
923         leng = min(leng,(gfileoffs[groupnum][filenum+1]-gfileoffs[groupnum][filenum])-arraypos[handle]);
924         leng = Bread(groupfil[rootgroupnum],buffer,leng);
925         arraypos[handle] += leng;
926         groupfilpos[rootgroupnum] += leng;
927         return leng;
928     }
929 
930     return 0;
931 }
932 
klseek_internal(int32_t handle,int32_t offset,int32_t whence,const uint8_t * arraygrp,intptr_t * arrayhan,int32_t * arraypos)933 int32_t klseek_internal(int32_t handle, int32_t offset, int32_t whence, const uint8_t *arraygrp, intptr_t *arrayhan, int32_t *arraypos)
934 {
935     int32_t const groupnum = arraygrp[handle];
936 
937     if (groupnum == GRP_FILESYSTEM) return Blseek(arrayhan[handle],offset,whence);
938 #ifdef WITHKPLIB
939     else if (groupnum == GRP_ZIP)
940     {
941         if (kzcurhand != handle)
942         {
943             if (kztell() >= 0) { arraypos[kzcurhand] = kztell(); kzclose(); }
944             kzcurhand = handle;
945             kzipopen(filenamsav[handle]);
946             kzseek(arraypos[handle],SEEK_SET);
947         }
948         return kzseek(offset,whence);
949     }
950 #endif
951 
952     if (groupfil[groupnum] != -1)
953     {
954         switch (whence)
955         {
956         case BSEEK_SET:
957             arraypos[handle] = offset; break;
958         case BSEEK_END:
959         {
960             int32_t const i = arrayhan[handle];
961             arraypos[handle] = (gfileoffs[groupnum][i+1]-gfileoffs[groupnum][i])+offset;
962             break;
963         }
964         case BSEEK_CUR:
965             arraypos[handle] += offset; break;
966         }
967         return arraypos[handle];
968     }
969     return -1;
970 }
971 
kfilelength_internal(int32_t handle,const uint8_t * arraygrp,intptr_t * arrayhan,int32_t * arraypos)972 int32_t kfilelength_internal(int32_t handle, const uint8_t *arraygrp, intptr_t *arrayhan, int32_t *arraypos)
973 {
974     int32_t const groupnum = arraygrp[handle];
975     if (groupnum == GRP_FILESYSTEM)
976     {
977         return buildvfs_length(arrayhan[handle]);
978     }
979 #ifdef WITHKPLIB
980     else if (groupnum == GRP_ZIP)
981     {
982         if (kzcurhand != handle)
983         {
984             if (kztell() >= 0) { arraypos[kzcurhand] = kztell(); kzclose(); }
985             kzcurhand = handle;
986             kzipopen(filenamsav[handle]);
987             kzseek(arraypos[handle],SEEK_SET);
988         }
989         return kzfilelength();
990     }
991 #endif
992     int32_t const i = arrayhan[handle];
993     return gfileoffs[groupnum][i+1]-gfileoffs[groupnum][i];
994 }
995 
ktell_internal(int32_t handle,const uint8_t * arraygrp,intptr_t * arrayhan,int32_t * arraypos)996 int32_t ktell_internal(int32_t handle, const uint8_t *arraygrp, intptr_t *arrayhan, int32_t *arraypos)
997 {
998     int32_t groupnum = arraygrp[handle];
999 
1000     if (groupnum == GRP_FILESYSTEM) return Blseek(arrayhan[handle],0,BSEEK_CUR);
1001 #ifdef WITHKPLIB
1002     else if (groupnum == GRP_ZIP)
1003     {
1004         if (kzcurhand != handle)
1005         {
1006             if (kztell() >= 0) { arraypos[kzcurhand] = kztell(); kzclose(); }
1007             kzcurhand = handle;
1008             kzipopen(filenamsav[handle]);
1009             kzseek(arraypos[handle],SEEK_SET);
1010         }
1011         return kztell();
1012     }
1013 #endif
1014     if (groupfil[groupnum] != -1)
1015         return arraypos[handle];
1016     return -1;
1017 }
1018 
kclose_internal(int32_t handle,const uint8_t * arraygrp,intptr_t * arrayhan)1019 void kclose_internal(int32_t handle, const uint8_t *arraygrp, intptr_t *arrayhan)
1020 {
1021     if (handle < 0) return;
1022     if (arraygrp[handle] == GRP_FILESYSTEM) Bclose(arrayhan[handle]);
1023 #ifdef WITHKPLIB
1024     else if (arraygrp[handle] == GRP_ZIP)
1025     {
1026         kzclose();
1027         kzcurhand = -1;
1028     }
1029 #endif
1030     arrayhan[handle] = -1;
1031 }
1032 
kread(int32_t handle,void * buffer,int32_t leng)1033 int32_t kread(int32_t handle, void *buffer, int32_t leng)
1034 {
1035     return kread_internal(handle, buffer, leng, filegrp, filehan, filepos);
1036 }
klseek(int32_t handle,int32_t offset,int32_t whence)1037 int32_t klseek(int32_t handle, int32_t offset, int32_t whence)
1038 {
1039     return klseek_internal(handle, offset, whence, filegrp, filehan, filepos);
1040 }
kfilelength(int32_t handle)1041 int32_t kfilelength(int32_t handle)
1042 {
1043     return kfilelength_internal(handle, filegrp, filehan, filepos);
1044 }
ktell(int32_t handle)1045 int32_t ktell(int32_t handle)
1046 {
1047     return ktell_internal(handle, filegrp, filehan, filepos);
1048 }
kclose(int32_t handle)1049 void kclose(int32_t handle)
1050 {
1051     return kclose_internal(handle, filegrp, filehan);
1052 }
1053 
kread_grp(int32_t handle,void * buffer,int32_t leng)1054 static int32_t kread_grp(int32_t handle, void *buffer, int32_t leng)
1055 {
1056     return kread_internal(handle, buffer, leng, groupfilgrp, groupfil, groupfilpos);
1057 }
klseek_grp(int32_t handle,int32_t offset,int32_t whence)1058 static int32_t klseek_grp(int32_t handle, int32_t offset, int32_t whence)
1059 {
1060     return klseek_internal(handle, offset, whence, groupfilgrp, groupfil, groupfilpos);
1061 }
kclose_grp(int32_t handle)1062 static void kclose_grp(int32_t handle)
1063 {
1064     return kclose_internal(handle, groupfilgrp, groupfil);
1065 }
1066 #endif
1067 
klistaddentry(BUILDVFS_FIND_REC ** rec,const char * name,int32_t type,int32_t source)1068 int32_t klistaddentry(BUILDVFS_FIND_REC **rec, const char *name, int32_t type, int32_t source)
1069 {
1070     BUILDVFS_FIND_REC *r = NULL, *attach = NULL;
1071 
1072     if (*rec)
1073     {
1074         int32_t insensitive, v;
1075         BUILDVFS_FIND_REC *last = NULL;
1076 
1077         for (attach = *rec; attach; last = attach, attach = attach->next)
1078         {
1079             if (type == BUILDVFS_FIND_DRIVE) continue;	// we just want to get to the end for drives
1080 #ifdef _WIN32
1081             insensitive = 1;
1082 #else
1083             if (source == BUILDVFS_SOURCE_GRP || attach->source == BUILDVFS_SOURCE_GRP)
1084                 insensitive = 1;
1085             else if (source == BUILDVFS_SOURCE_ZIP || attach->source == BUILDVFS_SOURCE_ZIP)
1086                 insensitive = 1;
1087             else
1088             {
1089                 extern int16_t editstatus;  // XXX
1090                 insensitive = !editstatus;
1091             }
1092             // ^ in the game, don't show file list case-sensitive
1093 #endif
1094             if (insensitive) v = Bstrcasecmp(name, attach->name);
1095             else v = Bstrcmp(name, attach->name);
1096 
1097             // sorted list
1098             if (v > 0) continue;	// item to add is bigger than the current one
1099             // so look for something bigger than us
1100             if (v < 0)  			// item to add is smaller than the current one
1101             {
1102                 attach = NULL;		// so wedge it between the current item and the one before
1103                 break;
1104             }
1105 
1106             // matched
1107             if (source >= attach->source) return 1;	// item to add is of lower priority
1108             r = attach;
1109             break;
1110         }
1111 
1112         // wasn't found in the list, so attach to the end
1113         if (!attach) attach = last;
1114     }
1115 
1116     if (r)
1117     {
1118         r->type = type;
1119         r->source = source;
1120         return 0;
1121     }
1122 
1123     r = (BUILDVFS_FIND_REC *)Xmalloc(sizeof(BUILDVFS_FIND_REC)+strlen(name)+1);
1124 
1125     r->name = (char *)r + sizeof(BUILDVFS_FIND_REC); strcpy(r->name, name);
1126     r->type = type;
1127     r->source = source;
1128     r->usera = r->userb = NULL;
1129 
1130     if (!attach)  	// we are the first item
1131     {
1132         r->prev = NULL;
1133         r->next = *rec;
1134         if (*rec)(*rec)->prev = r;
1135         *rec = r;
1136     }
1137     else
1138     {
1139         r->prev = attach;
1140         r->next = attach->next;
1141         if (attach->next) attach->next->prev = r;
1142         attach->next = r;
1143     }
1144 
1145     return 0;
1146 }
1147 
klistfree(BUILDVFS_FIND_REC * rec)1148 void klistfree(BUILDVFS_FIND_REC *rec)
1149 {
1150     BUILDVFS_FIND_REC *n;
1151 
1152     while (rec)
1153     {
1154         n = rec->next;
1155         Xfree(rec);
1156         rec = n;
1157     }
1158 }
1159 
klistpath(const char * _path,const char * mask,int32_t type)1160 BUILDVFS_FIND_REC *klistpath(const char *_path, const char *mask, int32_t type)
1161 {
1162     BUILDVFS_FIND_REC *rec = NULL;
1163     char *path;
1164 
1165     // pathsearchmode == 0: enumerates a path in the virtual filesystem
1166     // pathsearchmode == 1: enumerates the system filesystem path passed in
1167 
1168     path = Xstrdup(_path);
1169 
1170     // we don't need any leading dots and slashes or trailing slashes either
1171     {
1172         int32_t i,j;
1173         for (i=0; path[i] == '.' || toupperlookup[path[i]] == '/';) i++;
1174         for (j=0; (path[j] = path[i]); j++,i++) ;
1175         while (j>0 && toupperlookup[path[j-1]] == '/') j--;
1176         path[j] = 0;
1177         //initprintf("Cleaned up path = \"%s\"\n",path);
1178     }
1179 
1180     if (*path && (type & BUILDVFS_FIND_DIR))
1181     {
1182         if (klistaddentry(&rec, "..", BUILDVFS_FIND_DIR, BUILDVFS_SOURCE_CURDIR) < 0)
1183         {
1184             Xfree(path);
1185             klistfree(rec);
1186             return NULL;
1187         }
1188     }
1189 
1190     if (!(type & BUILDVFS_OPT_NOSTACK))  	// current directory and paths in the search stack
1191     {
1192 
1193         int32_t stackdepth = BUILDVFS_SOURCE_CURDIR;
1194 
1195 
1196 #ifdef USE_PHYSFS
1197         char **rc = PHYSFS_enumerateFiles("");
1198         char **i;
1199 
1200         for (i = rc; *i != NULL; i++)
1201         {
1202             char * name = *i;
1203 
1204             if ((name[0] == '.' && name[1] == 0) ||
1205                     (name[0] == '.' && name[1] == '.' && name[2] == 0))
1206                 continue;
1207 
1208             bool const isdir = buildvfs_isdir(name);
1209             if ((type & BUILDVFS_FIND_DIR) && !isdir) continue;
1210             if ((type & BUILDVFS_FIND_FILE) && isdir) continue;
1211             if (!Bwildmatch(name, mask)) continue;
1212             switch (klistaddentry(&rec, name,
1213                                   isdir ? BUILDVFS_FIND_DIR : BUILDVFS_FIND_FILE,
1214                                   stackdepth))
1215             {
1216             case -1: goto failure;
1217                 //case 1: initprintf("%s:%s dropped for lower priority\n", d,dirent->name); break;
1218                 //case 0: initprintf("%s:%s accepted\n", d,dirent->name); break;
1219             default:
1220                 break;
1221             }
1222         }
1223 
1224         PHYSFS_freeList(rc);
1225 #else
1226         static const char *const CUR_DIR = "./";
1227         // Adjusted for the following "autoload" dir fix - NY00123
1228         searchpath_t *search = NULL;
1229         const char *d = pathsearchmode ? _path : CUR_DIR;
1230         char buf[BMAX_PATH];
1231         BDIR *dir;
1232         struct Bdirent *dirent;
1233         do
1234         {
1235             if (d==CUR_DIR && (type & BUILDVFS_FIND_NOCURDIR))
1236                 goto next;
1237 
1238             strcpy(buf, d);
1239             if (!pathsearchmode)
1240             {
1241                 // Fix for "autoload" dir in multi-user environments - NY00123
1242                 strcat(buf, path);
1243                 if (*path) strcat(buf, "/");
1244             }
1245             dir = Bopendir(buf);
1246             if (dir)
1247             {
1248                 while ((dirent = Breaddir(dir)))
1249                 {
1250                     if ((dirent->name[0] == '.' && dirent->name[1] == 0) ||
1251                             (dirent->name[0] == '.' && dirent->name[1] == '.' && dirent->name[2] == 0))
1252                         continue;
1253                     if ((type & BUILDVFS_FIND_DIR) && !(dirent->mode & BS_IFDIR)) continue;
1254                     if ((type & BUILDVFS_FIND_FILE) && (dirent->mode & BS_IFDIR)) continue;
1255                     if (!Bwildmatch(dirent->name, mask)) continue;
1256                     switch (klistaddentry(&rec, dirent->name,
1257                                           (dirent->mode & BS_IFDIR) ? BUILDVFS_FIND_DIR : BUILDVFS_FIND_FILE,
1258                                           stackdepth))
1259                     {
1260                     case -1: goto failure;
1261                         //case 1: initprintf("%s:%s dropped for lower priority\n", d,dirent->name); break;
1262                         //case 0: initprintf("%s:%s accepted\n", d,dirent->name); break;
1263                     default:
1264                         break;
1265                     }
1266                 }
1267                 Bclosedir(dir);
1268             }
1269 next:
1270             if (pathsearchmode)
1271                 break;
1272 
1273             if (!search)
1274             {
1275                 search = searchpathhead;
1276                 stackdepth = BUILDVFS_SOURCE_PATH;
1277             }
1278             else
1279             {
1280                 search = search->next;
1281                 stackdepth++;
1282             }
1283 
1284             if (search)
1285                 d = search->path;
1286         }
1287         while (search);
1288 #endif
1289     }
1290 
1291 #ifndef USE_PHYSFS
1292 #ifdef WITHKPLIB
1293     if (!(type & BUILDVFS_FIND_NOCURDIR))  // TEMP, until we have sorted out fs.listpath() API
1294     if (!pathsearchmode)  	// next, zip files
1295     {
1296         char buf[BMAX_PATH+4];
1297         int32_t i, j, ftype;
1298         strcpy(buf,path);
1299         if (*path) strcat(buf,"/");
1300         strcat(buf,mask);
1301         for (kzfindfilestart(buf); kzfindfile(buf);)
1302         {
1303             if (buf[0] != '|') continue;	// local files we don't need
1304 
1305             // scan for the end of the string and shift
1306             // everything left a char in the process
1307             for (i=1; (buf[i-1]=buf[i]); i++)
1308             {
1309                 /* do nothing */
1310             }
1311             i-=2;
1312             if (i < 0)
1313                 i = 0;
1314 
1315             // if there's a slash at the end, this is a directory entry
1316             if (toupperlookup[buf[i]] == '/') { ftype = BUILDVFS_FIND_DIR; buf[i] = 0; }
1317             else ftype = BUILDVFS_FIND_FILE;
1318 
1319             // skip over the common characters at the beginning of the base path and the zip entry
1320             for (j=0; buf[j] && path[j]; j++)
1321             {
1322                 if (toupperlookup[ path[j] ] == toupperlookup[ buf[j] ]) continue;
1323                 break;
1324             }
1325             // we've now hopefully skipped the common path component at the beginning.
1326             // if that's true, we should be staring at a null byte in path and either any character in buf
1327             // if j==0, or a slash if j>0
1328             if ((!path[0] && buf[j]) || (!path[j] && toupperlookup[ buf[j] ] == '/'))
1329             {
1330                 if (j>0) j++;
1331 
1332                 // yep, so now we shift what follows back to the start of buf and while we do that,
1333                 // keep an eye out for any more slashes which would mean this entry has sub-entities
1334                 // and is useless to us.
1335                 for (i = 0; (buf[i] = buf[j]) && toupperlookup[buf[j]] != '/'; i++,j++) ;
1336                 if (toupperlookup[buf[j]] == '/') continue;	// damn, try next entry
1337             }
1338             else
1339             {
1340                 // if we're here it means we have a situation where:
1341                 //   path = foo
1342                 //   buf = foobar...
1343                 // or
1344                 //   path = foobar
1345                 //   buf = foo...
1346                 // which would mean the entry is higher up in the directory tree and is also useless
1347                 continue;
1348             }
1349 
1350             if ((type & BUILDVFS_FIND_DIR) && ftype != BUILDVFS_FIND_DIR) continue;
1351             if ((type & BUILDVFS_FIND_FILE) && ftype != BUILDVFS_FIND_FILE) continue;
1352 
1353             // the entry is in the clear
1354             switch (klistaddentry(&rec, buf, ftype, BUILDVFS_SOURCE_ZIP))
1355             {
1356             case -1:
1357                 goto failure;
1358                 //case 1: initprintf("<ZIP>:%s dropped for lower priority\n", buf); break;
1359                 //case 0: initprintf("<ZIP>:%s accepted\n", buf); break;
1360             default:
1361                 break;
1362             }
1363         }
1364     }
1365 #endif
1366     // then, grp files
1367     if (!(type & BUILDVFS_FIND_NOCURDIR))  // TEMP, until we have sorted out fs.listpath() API
1368     if (!pathsearchmode && !*path && (type & BUILDVFS_FIND_FILE))
1369     {
1370         char buf[13];
1371         int32_t i,j;
1372         buf[12] = 0;
1373         for (i=0; i<MAXGROUPFILES; i++)
1374         {
1375             if (groupfil[i] == -1) continue;
1376             for (j=gnumfiles[i]-1; j>=0; j--)
1377             {
1378                 Bmemcpy(buf,&gfilelist[i][j<<4],12);
1379                 if (!Bwildmatch(buf,mask)) continue;
1380                 switch (klistaddentry(&rec, buf, BUILDVFS_FIND_FILE, BUILDVFS_SOURCE_GRP))
1381                 {
1382                 case -1:
1383                     goto failure;
1384                     //case 1: initprintf("<GRP>:%s dropped for lower priority\n", workspace); break;
1385                     //case 0: initprintf("<GRP>:%s accepted\n", workspace); break;
1386                 default:
1387                     break;
1388                 }
1389             }
1390         }
1391     }
1392 #endif
1393 
1394     if (pathsearchmode && (type & BUILDVFS_FIND_DRIVE))
1395     {
1396         char *drives, *drp;
1397         drives = Bgetsystemdrives();
1398         if (drives)
1399         {
1400             for (drp=drives; *drp; drp+=strlen(drp)+1)
1401             {
1402                 if (klistaddentry(&rec, drp, BUILDVFS_FIND_DRIVE, BUILDVFS_SOURCE_DRIVE) < 0)
1403                 {
1404                     Xfree(drives);
1405                     goto failure;
1406                 }
1407             }
1408             Xfree(drives);
1409         }
1410     }
1411 
1412     Xfree(path);
1413     // XXX: may be NULL if no file was listed, and thus indistinguishable from
1414     // an error condition.
1415     return rec;
1416 failure:
1417     Xfree(path);
1418     klistfree(rec);
1419     return NULL;
1420 }
1421 
1422 
kdfread_func(intptr_t fil,void * outbuf,int32_t length)1423 static int32_t kdfread_func(intptr_t fil, void *outbuf, int32_t length)
1424 {
1425     return kread((buildvfs_kfd)fil, outbuf, length);
1426 }
1427 
dfwrite_func(intptr_t fp,const void * inbuf,int32_t length)1428 static void dfwrite_func(intptr_t fp, const void *inbuf, int32_t length)
1429 {
1430     buildvfs_fwrite(inbuf, length, 1, (buildvfs_FILE)fp);
1431 }
1432 
1433 
kdfread(void * buffer,int dasizeof,int count,buildvfs_kfd fil)1434 int32_t kdfread(void *buffer, int dasizeof, int count, buildvfs_kfd fil)
1435 {
1436     return klzw_read_compressed(buffer, dasizeof, count, (intptr_t)fil, kdfread_func);
1437 }
1438 
1439 // LZ4_COMPRESSION_ACCELERATION_VALUE can be tuned for performance/space trade-off
1440 // (lower number = higher compression ratio, higher number = faster compression speed)
1441 #define LZ4_COMPRESSION_ACCELERATION_VALUE 5
1442 
1443 static char compressedDataStackBuf[131072];
1444 int32_t lz4CompressionLevel = LZ4_COMPRESSION_ACCELERATION_VALUE;
1445 
kdfread_LZ4(void * buffer,int dasizeof,int count,buildvfs_kfd fil)1446 int32_t kdfread_LZ4(void *buffer, int dasizeof, int count, buildvfs_kfd fil)
1447 {
1448     int32_t leng;
1449 
1450     // read compressed data length
1451     if (kread_and_test(fil, &leng, sizeof(leng)))
1452         return -1;
1453 
1454     leng = B_LITTLE32(leng);
1455 
1456     char *pCompressedData = compressedDataStackBuf;
1457 
1458     if (leng > ARRAY_SSIZE(compressedDataStackBuf))
1459         pCompressedData = (char *)Xaligned_alloc(16, leng);
1460 
1461     if (kread_and_test(fil, pCompressedData, leng))
1462         return -1;
1463 
1464     int32_t decompressedLength = LZ4_decompress_safe(pCompressedData, (char*) buffer, leng, dasizeof*count);
1465 
1466     if (pCompressedData != compressedDataStackBuf)
1467         Xaligned_free(pCompressedData);
1468 
1469     return decompressedLength/dasizeof;
1470 }
1471 
1472 
dfwrite(const void * buffer,int dasizeof,int count,buildvfs_FILE fil)1473 void dfwrite(const void *buffer, int dasizeof, int count, buildvfs_FILE fil)
1474 {
1475     klzw_write_compressed(buffer, dasizeof, count, (intptr_t)fil, dfwrite_func);
1476 }
1477 
dfwrite_LZ4(const void * buffer,int dasizeof,int count,buildvfs_FILE fil)1478 void dfwrite_LZ4(const void *buffer, int dasizeof, int count, buildvfs_FILE fil)
1479 {
1480     char *    pCompressedData   = compressedDataStackBuf;
1481     int const maxCompressedSize = LZ4_compressBound(dasizeof * count);
1482 
1483     if (maxCompressedSize > ARRAY_SSIZE(compressedDataStackBuf))
1484         pCompressedData = (char *)Xaligned_alloc(16, maxCompressedSize);
1485 
1486     int const leng = LZ4_compress_fast((const char*) buffer, pCompressedData, dasizeof*count, maxCompressedSize, lz4CompressionLevel);
1487     int const swleng = B_LITTLE32(leng);
1488 
1489     buildvfs_fwrite(&swleng, sizeof(swleng), 1, fil);
1490     buildvfs_fwrite(pCompressedData, leng, 1, fil);
1491 
1492     if (pCompressedData != compressedDataStackBuf)
1493         Xaligned_free(pCompressedData);
1494 }
1495