1 /*
2 * Copyright 2009-2017 Peter Kosyh <p.kosyh at gmail.com>
3 *
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 *
23 */
24
25 #include "system.h"
26 #include "idf.h"
27 #include "util.h"
28 #include "cache.h"
29 #include "list.h"
30
31 typedef struct _idfd_t {
32 struct list_head list;
33 unsigned long offset;
34 unsigned long size;
35 idf_t idf;
36 } idfd_t;
37
38
39 struct _idff_t {
40 struct list_node list;
41 idfd_t *dir;
42 unsigned long pos;
43 FILE *fd;
44 };
45
46 struct _idf_t {
47 unsigned long size;
48 FILE *fd;
49 char *path;
50 char cwd[PATH_MAX];
51 cache_t dir;
52 int idfonly;
53 };
54
free_idfd(void * p)55 static void free_idfd(void *p)
56 {
57 idfd_t *dir = (idfd_t*)p;
58 if (!p)
59 return;
60
61 while (!list_empty(&dir->list)) {
62 idff_t idff;
63 idff = list_top(&dir->list, struct _idff_t, list);
64 idf_close(idff);
65 }
66
67 free(p);
68 }
69
idf_done(idf_t idf)70 void idf_done(idf_t idf)
71 {
72 if (!idf)
73 return;
74 if (idf->path)
75 free(idf->path);
76 if (idf->dir)
77 cache_free(idf->dir);
78 if (idf->fd)
79 fclose(idf->fd);
80 free(idf);
81 }
82
idf_shrink(idf_t idf)83 void idf_shrink(idf_t idf)
84 {
85 if (!idf)
86 return;
87 if (idf->dir)
88 cache_shrink(idf->dir);
89 }
90
read_word(FILE * fd,unsigned long * w)91 static int read_word(FILE *fd, unsigned long *w)
92 {
93 unsigned char word[4];
94 if (fread(word, 1, 4, fd) != 4)
95 return -1;
96 *w = (unsigned long)word[0] | ((unsigned long)word[1] << 8) |
97 ((unsigned long)word[2] << 16) |
98 ((unsigned long)word[3] << 24);
99 return 0;
100 }
101
write_word(FILE * fd,unsigned long w)102 static int write_word(FILE *fd, unsigned long w)
103 {
104 char word[4];
105
106 word[0] = w & 0xff;
107 word[1] = (w & 0xff00) >> 8;
108 word[2] = (w & 0xff0000) >> 16;
109 word[3] = (w & 0xff000000) >> 24;
110
111 if (fwrite(word, 1, 4, fd) != 4)
112 return -1;
113 return 0;
114 }
115
idf_magic(const char * fname)116 int idf_magic(const char *fname)
117 {
118 char sign[4];
119 FILE *fd = fopen(dirpath(fname), "rb");
120 if (!fd)
121 return 0;
122 if (fread(sign, 1, 4, fd) != 4) {
123 fclose(fd);
124 return 0;
125 }
126 fclose(fd);
127 if (!memcmp(sign, "IDF1", 4))
128 return 1;
129 return 0;
130 }
idf_setdir(idf_t idf,const char * path)131 int idf_setdir(idf_t idf, const char *path)
132 {
133 if (idf && path)
134 strcpy(idf->cwd, path);
135 return 0;
136 }
137
idf_getdir(idf_t idf)138 char *idf_getdir(idf_t idf)
139 {
140 if (!idf)
141 return NULL;
142 return idf->cwd;
143 }
144
idf_init(const char * fname)145 idf_t idf_init(const char *fname)
146 {
147 char sign[4];
148 unsigned long dir_size;
149 char *fp = dirpath(fname);
150 idf_t idf = malloc(sizeof(struct _idf_t));
151 if (!idf)
152 return NULL;
153 memset(idf, 0, sizeof(struct _idf_t));
154 idf->path = strdup(fname);
155 if (!idf->path)
156 goto err;
157 idf->idfonly = 0;
158 idf->fd = fopen(fp, "rb");
159 idf->dir = cache_init(-1, free_idfd);
160 idf->cwd[0] = 0;
161 if (!idf->fd || !idf->dir)
162 goto err;
163 if (fseek(idf->fd, 0, SEEK_END))
164 goto err;
165 idf->size = ftell(idf->fd);
166 if ((int)idf->size < 0)
167 goto err;
168 if (fseek(idf->fd, 0, SEEK_SET))
169 goto err;
170 if (fread(sign, 1, 4, idf->fd) != 4)
171 goto err;
172 if (memcmp(sign, "IDF1", 4))
173 goto err;
174 if (read_word(idf->fd, &dir_size))
175 goto err;
176 if (dir_size > idf->size)
177 goto err;
178 while (dir_size > 0) {
179 unsigned long off;
180 unsigned long size;
181 unsigned char sz;
182 char name[256];
183 idfd_t *e;
184 if (fread(&sz, 1, 1, idf->fd) != 1)
185 goto err;
186 if (fread(name, 1, sz, idf->fd) != sz)
187 goto err;
188 name[sz] = 0;
189 if (read_word(idf->fd, &off))
190 goto err;
191 if (read_word(idf->fd, &size))
192 goto err;
193 e = malloc(sizeof(idfd_t));
194 if (!e)
195 goto err;
196 e->offset = off;
197 e->size = size;
198 e->idf = idf;
199 list_head_init(&e->list);
200 if (cache_add(idf->dir, name, e)) {
201 free(e);
202 goto err;
203 }
204 cache_forget(idf->dir, e); /* use like hash */
205 /* fprintf(stderr,"Parsed: '%s' @ %ld, %ld\n", name, off, size); */
206 dir_size -= (1 + sz + 4 + 4);
207 }
208 return idf;
209 err:
210 idf_done(idf);
211 return NULL;
212 }
213
214 typedef struct {
215 struct list_node list;
216 char *path;
217 long size;
218 struct list_head dir;
219 } idf_item_t;
220
221 typedef struct {
222 struct list_node list;
223 char *dname;
224 } idf_dir_item_t;
225
fcopy(FILE * to,const char * fname)226 static int fcopy(FILE *to, const char *fname)
227 {
228 int rc = -1;
229 char buff[4096];
230 FILE *fd;
231 fd = fopen(fname, "rb");
232 if (!fd)
233 return -1;
234 while (!feof(fd)) {
235 int s = fread(buff, 1, sizeof(buff), fd);
236 if (!s) {
237 if (feof(fd))
238 break;
239 goto err;
240 }
241 if (fwrite(buff, 1, s, to) != (size_t)s)
242 goto err;
243 }
244 rc = 0;
245 err:
246 fclose(fd);
247 return rc;
248 }
249
idf_tree(const char * path,struct list_head * list,const char * fname)250 static int idf_tree(const char *path, struct list_head *list, const char *fname)
251 {
252 idf_item_t *dir;
253 DIR *d;
254 struct dirent *de;
255 if (!path)
256 return 0;
257 d = opendir(dirpath(path));
258 if (!d) {
259 if (!access(dirpath(path), R_OK) && fname) {
260 FILE *fd; idf_item_t *i;
261 fd = fopen(dirpath(path), "rb");
262 if (!fd) {
263 fprintf(stderr, "Can not open file: '%s'\n", path);
264 return -1;
265 }
266 i = malloc(sizeof(idf_item_t));
267 if (!i) {
268 fclose(fd);
269 return -1;
270 }
271 /* list_head_init(&i->list); */
272 if (!(i->path = strdup(fname)))
273 goto err;
274 if (fseek(fd, 0, SEEK_END) < 0)
275 goto err;
276 if ((i->size = ftell(fd)) < 0)
277 goto err;
278 list_head_init(&i->dir);
279 fclose(fd);
280 fprintf(stderr, "Added file: '%s' size: %ld\n", path, i->size);
281 list_add(list, &i->list);
282 return 0;
283 err:
284 if (i->path)
285 free(i->path);
286 free(i);
287 return -1;
288 }
289 return 0;
290 }
291
292 dir = malloc(sizeof(idf_item_t));
293 if (!dir)
294 goto err1;
295 if (fname) {
296 char *d = malloc(strlen(fname) + 2);
297 if (!d)
298 goto err2;
299 strcpy(d, fname); strcat(d, "/");
300 dir->path = d;
301 } else {
302 dir->path = strdup("/");
303 }
304 dir->size = 0;
305 list_head_init(&dir->dir);
306
307 while ((de = readdir(d))) {
308 char *p;
309 idf_dir_item_t *di;
310
311 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
312 continue;
313 di = malloc(sizeof(idf_dir_item_t));
314 if (di) {
315 di->dname = strdup(de->d_name);
316 list_add(&dir->dir, &di->list);
317 dir->size += strlen(de->d_name) + 1;
318 }
319 p = getfilepath(path, de->d_name);
320 if (p) {
321 char *pp = getfilepath(fname, de->d_name);
322 if (pp) {
323 idf_tree(p, list, pp);
324 free(pp);
325 }
326 free(p);
327 }
328 }
329 list_add(list, &dir->list);
330 fprintf(stderr, "Added dir: '%s' size: %ld\n", dir->path, dir->size);
331 closedir(d);
332 return 0;
333 err2:
334 free(dir);
335 err1:
336 closedir(d);
337 return -1;
338 }
339
idf_create(const char * file,const char * path)340 int idf_create(const char *file, const char *path)
341 {
342 int rc = -1, i;
343 FILE *fd;
344 char *p;
345 unsigned long off = 0;
346 long dict_size = 0;
347 idf_item_t *pos = NULL;
348
349 LIST_HEAD(items);
350 p = strdup(path);
351 if (!p)
352 return -1;
353
354 unix_path(p);
355
356 i = strlen(p) - 1;
357
358 while (i >= 0 && p[i] == '/') {
359 p[i --] = 0;
360 }
361
362 idf_tree(p, &items, NULL);
363
364 free(p);
365
366 list_for_each(&items, pos, list) {
367 idf_item_t *it = pos;
368 dict_size += (1 + strlen(it->path) + 4 + 4);
369 }
370
371 fd = fopen(dirpath(file), "wb");
372 if (!fd)
373 goto err;
374 if (fwrite("IDF1", 1, 4, fd) != 4)
375 goto err;
376 if (write_word(fd, dict_size) < 0)
377 goto err;
378 off = 4 + 4 + dict_size;
379
380 list_for_each(&items, pos, list) {
381 unsigned char s;
382 idf_item_t *it = pos;
383 s = strlen(it->path);
384 if (fwrite(&s, 1, 1, fd) != 1)
385 goto err;
386 p = strdup(it->path);
387 if (!p)
388 goto err;
389 tolow(p); /* in idf always lowcase */
390 if (fwrite(p, 1, s, fd) != s) {
391 free(p);
392 goto err;
393 }
394 free(p);
395 if (write_word(fd, off) < 0)
396 goto err;
397 if (write_word(fd, it->size) < 0)
398 goto err;
399 off += it->size;
400 }
401
402 list_for_each(&items, pos, list) {
403 idf_item_t *it = pos;
404 char *p;
405
406 if (!list_empty(&it->dir)) { /* directory-file */
407 idf_dir_item_t *d = NULL;
408 list_for_each(&it->dir, d, list) {
409 fprintf(fd, "%s\n", d->dname);
410 }
411 continue;
412 }
413
414 p = getfilepath(path, it->path);
415 if (p) {
416 int rc = fcopy(fd, p);
417 free(p);
418 if (rc) {
419 fprintf(stderr, "Error while copy file '%s'...\n", it->path);
420 goto err;
421 }
422 }
423 }
424 rc = 0;
425 err:
426 if (rc)
427 fprintf(stderr, "Error creating idf file...\n");
428
429 while (!list_empty(&items)) {
430 idf_item_t *it = list_top(&items, idf_item_t, list);
431 while (!list_empty(&it->dir)) {
432 idf_dir_item_t *di = list_top(&it->dir, idf_dir_item_t, list);
433 free(di->dname);
434 list_del(&di->list);
435 free(di);
436 }
437 free(it->path);
438 list_del(&it->list);
439 free(it);
440 }
441 if (fd)
442 fclose(fd);
443 return rc;
444 }
445
446
idf_seek(idff_t fil,int offset,int whence)447 int idf_seek(idff_t fil, int offset, int whence)
448 {
449 idfd_t *dir = fil->dir;
450 switch (whence) {
451 case SEEK_SET:
452 if (offset < 0 || (unsigned int)offset > dir->size) {
453 return -1;
454 }
455 fil->pos = offset;
456 break;
457 case SEEK_END:
458 if (dir->size + offset > dir->size || (int)(dir->size + offset) < 0) {
459 return -1;
460 }
461 fil->pos = dir->size + offset;
462 break;
463 case SEEK_CUR:
464 if (fil->pos + offset > dir->size || (int)(fil->pos + offset) < 0) {
465 return -1;
466 }
467 fil->pos += offset;
468 break;
469 }
470 if (!fseek(fil->fd, fil->pos + dir->offset, SEEK_SET))
471 return fil->pos;
472 return -1;
473 }
474
idf_close(idff_t fil)475 int idf_close(idff_t fil)
476 {
477 if (fil) {
478 fclose(fil->fd);
479 list_del(&fil->list);
480 free(fil);
481 }
482 return 0; /* nothing todo */
483 }
484
485 #if 0
486 int idf_extract(idf_t idf, const char *fname)
487 {
488 FILE *out;
489 int size;
490 idfd_t *dir = NULL;
491 char buff[4096];
492 if (idf)
493 dir = cache_lookup(idf->dir, fname);
494 if (!dir)
495 return -1;
496 fseek(idf->fd, dir->offset, SEEK_SET);
497 out = fopen("out.bin", "wb");
498 size = dir->size;
499 while (size>0) {
500 int s ;
501 if (size < sizeof(buff))
502 s = fread(buff, 1, size, idf->fd);
503 else
504 s = fread(buff, 1, sizeof(buff), idf->fd);
505 fwrite(buff, 1, s, out);
506 size -= s;
507 fprintf(stderr, "size = %d\n", size);
508 }
509 fclose(out);
510 return 0;
511 }
512 #endif
idf_eof(idff_t idf)513 int idf_eof(idff_t idf)
514 {
515 if (!idf)
516 return 1;
517 if (idf->pos >= idf->dir->size)
518 return 1;
519 return 0;
520 }
521
idf_error(idff_t idf)522 int idf_error(idff_t idf)
523 {
524 if (!idf || !idf->fd)
525 return -1;
526 return ferror(idf->fd);
527 }
528
idf_only(idf_t idf,int fl)529 int idf_only(idf_t idf, int fl)
530 { int i;
531 if (!idf)
532 return -1;
533 if (fl == -1)
534 return idf->idfonly;
535 i = idf->idfonly;
536 idf->idfonly = fl;
537 return i;
538 }
539
idf_open(idf_t idf,const char * fname)540 idff_t idf_open(idf_t idf, const char *fname)
541 {
542 char *rp;
543 idfd_t *dir = NULL;
544 idff_t fil = NULL;
545 char *p;
546 if (!idf || !fname)
547 return NULL;
548 p = strdup(fname);
549 if (!p)
550 return NULL;
551 tolow(p);
552 rp = getfilepath(idf->cwd, p);
553 if (rp) {
554 dir = cache_lookup(idf->dir, rp);
555 free(rp);
556 }
557 free(p);
558 if (!dir)
559 return NULL;
560
561 fil = malloc(sizeof(struct _idff_t));
562 if (!fil)
563 return NULL;
564 /* list_head_init(&fil->list); */
565
566 fil->dir = dir;
567 fil->pos = 0;
568 fil->fd = fopen(dirpath(idf->path), "rb");
569 if (!fil->fd)
570 goto err;
571 list_add(&dir->list, &fil->list);
572 return fil;
573 err:
574 free(fil);
575 return NULL;
576 }
577
idf_access(idf_t idf,const char * fname)578 int idf_access(idf_t idf, const char *fname)
579 {
580 char *rp;
581 idfd_t *dir = NULL;
582 if (idf) {
583 rp = getfilepath(idf->cwd, fname);
584 if (rp) {
585 dir = cache_lookup(idf->dir, rp);
586 free(rp);
587 }
588 }
589 if (!dir)
590 return -1;
591 return 0;
592 }
593
idf_opendir(idf_t idf,const char * dname)594 idff_t idf_opendir(idf_t idf, const char *dname)
595 {
596 return idf_open(idf, dname);
597 }
598
idf_closedir(idff_t d)599 int idf_closedir(idff_t d)
600 {
601 return idf_close(d);
602 }
603
idf_readdir(idff_t d)604 char *idf_readdir(idff_t d)
605 {
606 char buff[256];
607 char *p = idf_gets(d, buff, sizeof(buff) - 1);
608 if (!p)
609 return p;
610 return strdup(p);
611 }
612
idf_gets(idff_t idf,char * b,int size)613 char *idf_gets(idff_t idf, char *b, int size)
614 {
615 int rc, rc2;
616 if (!idf)
617 return NULL;
618 if (!size)
619 return NULL;
620 rc = idf_read(idf, b, 1, size);
621 if (rc < 0)
622 return NULL;
623 if (!rc && idf_eof(idf))
624 return NULL;
625 if (!idf_eof(idf))
626 b[rc - 1] = 0;
627 else {
628 if (rc < size)
629 b[rc] = 0;
630 else
631 b[size - 1] = 0;
632 }
633 rc2 = strcspn(b, "\n");
634 b[rc2] = 0;
635 idf_seek(idf, - (rc - rc2 - 1), SEEK_CUR);
636 return b;
637 }
638
idf_read(idff_t fil,void * ptr,int size,int maxnum)639 int idf_read(idff_t fil, void *ptr, int size, int maxnum)
640 {
641 int rc = 0;
642 long pos;
643
644 idfd_t *dir = fil->dir;
645
646 if (fseek(fil->fd, dir->offset + fil->pos, SEEK_SET) < 0) {
647 return 0;
648 }
649 #if 1
650 while (maxnum) {
651 pos = ftell(fil->fd);
652 fil->pos = pos - dir->offset;
653
654 if (fil->pos + size > dir->size) {
655 break;
656 }
657
658 if (fread(ptr, size, 1, fil->fd) != 1)
659 break;
660 /* fil->pos += size; */
661 ptr = (char *)ptr + size;
662 maxnum --;
663 rc ++;
664 }
665 #else
666 rc = fread(ptr, size, maxnum, fil->fd);
667 #endif
668 pos = ftell(fil->fd);
669 fil->pos = pos - dir->offset;
670 return rc;
671 }
672
673 #ifdef _USE_SDL
674 #include <SDL.h>
675
676 #if SDL_VERSION_ATLEAST(2,0,0)
idfrw_seek(struct SDL_RWops * context,Sint64 offset,int whence)677 static Sint64 idfrw_seek(struct SDL_RWops *context, Sint64 offset, int whence)
678 #else
679 static int idfrw_seek(struct SDL_RWops *context, int offset, int whence)
680 #endif
681 {
682 idff_t fil = (idff_t)context->hidden.unknown.data1;
683 return idf_seek(fil, offset, whence);
684 }
685 #if SDL_VERSION_ATLEAST(2,0,0)
idfrw_read(struct SDL_RWops * context,void * ptr,size_t size,size_t maxnum)686 static size_t idfrw_read(struct SDL_RWops *context, void *ptr, size_t size, size_t maxnum)
687 #else
688 static int idfrw_read(struct SDL_RWops *context, void *ptr, int size, int maxnum)
689 #endif
690 {
691 idff_t fil = (idff_t)context->hidden.unknown.data1;
692 return idf_read(fil, ptr, size, maxnum);
693 }
694
idfrw_close(struct SDL_RWops * context)695 static int idfrw_close(struct SDL_RWops *context)
696 {
697 if (context) {
698 idff_t fil = (idff_t)context->hidden.unknown.data1;
699 idf_close(fil);
700 SDL_FreeRW(context);
701 }
702 return 0;
703 }
704 #if SDL_VERSION_ATLEAST(2,0,0)
idfrw_size(struct SDL_RWops * context)705 static Sint64 idfrw_size(struct SDL_RWops *context)
706 {
707 idff_t fil = (idff_t)context->hidden.unknown.data1;
708 if (!fil || !fil->dir)
709 return -1;
710 return fil->dir->size;
711 }
712 #endif
713
RWFromIdf(idf_t idf,const char * fname)714 SDL_RWops *RWFromIdf(idf_t idf, const char *fname)
715 {
716 idff_t fil = NULL;
717 SDL_RWops *n;
718 fil = idf_open(idf, fname);
719 if (!fil) {
720 if (!idf || !idf->idfonly)
721 return SDL_RWFromFile(dirpath(fname), "rb");
722 return NULL;
723 }
724 n = SDL_AllocRW();
725 if (!n)
726 goto err;
727 #if SDL_VERSION_ATLEAST(2,0,0)
728 n->size = idfrw_size;
729 #endif
730 n->seek = idfrw_seek;
731 n->read = idfrw_read;
732 n->close = idfrw_close;
733 n->hidden.unknown.data1 = fil;
734 return n;
735 err:
736 if (n)
737 SDL_FreeRW(n);
738 free(fil);
739 return NULL;
740 }
741 #endif
742