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