#include "externals.h" #include "internals.h" #include "idf.h" #include "list.h" #include typedef struct _idfd_t { struct list_head list; unsigned long offset; unsigned long size; idf_t idf; } idfd_t; struct _idff_t { struct list_head list; idfd_t *dir; unsigned long pos; FILE *fd; }; static void free_idfd(void *p) { idfd_t *dir = (idfd_t*)p; if (!p) return; while (!list_empty(&dir->list)) { idff_t idff; idff = (idff_t)(dir->list.next); idf_close(idff); } free(p); } struct _idf_t { unsigned long size; FILE *fd; char *path; cache_t dir; int idfonly; }; void idf_done(idf_t idf) { if (!idf) return; if (idf->path) free(idf->path); if (idf->dir) cache_free(idf->dir); if (idf->fd) fclose(idf->fd); free(idf); } void idf_shrink(idf_t idf) { if (!idf) return; if (idf->dir) cache_shrink(idf->dir); } static int read_word(FILE *fd, unsigned long *w) { unsigned char word[4]; if (fread(word, 1, 4, fd) != 4) return -1; *w = (unsigned long)word[0] | ((unsigned long)word[1] << 8) | ((unsigned long)word[2] << 16) | ((unsigned long)word[3] << 24); return 0; } static int write_word(FILE *fd, unsigned long w) { char word[4]; word[0] = w & 0xff; word[1] = (w & 0xff00) >> 8; word[2] = (w & 0xff0000) >> 16; word[3] = (w & 0xff000000) >> 24; if (fwrite(word, 1, 4, fd) != 4) return -1; return 0; } int idf_magic(const char *fname) { char sign[4]; FILE *fd = fopen(dirpath(fname), "rb"); if (!fd) return 0; if (fread(sign, 1, 4, fd) != 4) { fclose(fd); return 0; } fclose(fd); if (!memcmp(sign, "IDF1", 4)) return 1; return 0; } idf_t idf_init(const char *fname) { char sign[4]; unsigned long dir_size; char *fp = dirpath(fname); idf_t idf = malloc(sizeof(struct _idf_t)); if (!idf) return NULL; idf->path = strdup(fname); if (!idf->path) goto err; idf->idfonly = 0; idf->fd = fopen(fp, "rb"); idf->dir = cache_init(-1, free_idfd); if (!idf->fd || !idf->dir) goto err; if (fseek(idf->fd, 0, SEEK_END)) goto err; idf->size = ftell(idf->fd); if (idf->size < 0) goto err; if (fseek(idf->fd, 0, SEEK_SET)) goto err; if (fread(sign, 1, 4, idf->fd) != 4) goto err; if (memcmp(sign, "IDF1", 4)) goto err; if (read_word(idf->fd, &dir_size)) goto err; if (dir_size > idf->size) goto err; while (dir_size > 0) { unsigned long off; unsigned long size; unsigned char sz; char name[256]; idfd_t *e; if (fread(&sz, 1, 1, idf->fd) != 1) goto err; if (fread(name, 1, sz, idf->fd) != sz) goto err; name[sz] = 0; if (read_word(idf->fd, &off)) goto err; if (read_word(idf->fd, &size)) goto err; e = malloc(sizeof(idfd_t)); if (!e) goto err; e->offset = off; e->size = size; e->idf = idf; INIT_LIST_HEAD(&e->list); if (cache_add(idf->dir, name, e)) { free(e); goto err; } cache_forget(idf->dir, e); /* use like hash */ // fprintf(stderr,"Parsed: '%s' @ %ld, %ld\n", name, off, size); dir_size -= (1 + sz + 4 + 4); } return idf; err: idf_done(idf); return NULL; } typedef struct { struct list_head list; char *path; long size; } idf_item_t; static int fcopy(FILE *to, const char *fname) { int rc = -1; char buff[4096]; FILE *fd; fd = fopen(fname, "rb"); if (!fd) return -1; while (!feof(fd)) { int s = fread(buff, 1, sizeof(buff), fd); if (!s) { if (feof(fd)) break; goto err; } if (fwrite(buff, 1, s, to) != s) goto err; } rc = 0; err: fclose(fd); return rc; } static int idf_tree(const char *path, struct list_head *list, const char *fname) { DIR *d; struct dirent *de; if (!path) return 0; d = opendir(dirpath(path)); if (!d) { if (!access(dirpath(path), R_OK) && fname) { FILE *fd; idf_item_t *i; fd = fopen(dirpath(path), "rb"); i = malloc(sizeof(idf_item_t)); if (!i) return -1; INIT_LIST_HEAD(&i->list); if (!(i->path = strdup(fname))) goto err; if (fseek(fd, 0, SEEK_END) < 0) goto err; if ((i->size = ftell(fd)) < 0) goto err; fclose(fd); fprintf(stderr, "Added file: '%s' size: %ld\n", path, i->size); list_add(&i->list, list); return 0; err: if (i->path) free(i->path); free(i); return -1; } return 0; } while ((de = readdir(d))) { char *p; if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; p = getfilepath(path, de->d_name); if (p) { char *pp = getfilepath(fname, de->d_name); if (pp) { idf_tree(p, list, pp); free(pp); } free(p); } } closedir(d); return 0; } int idf_create(const char *file, const char *path) { int rc = -1, i; FILE *fd; char *p; unsigned long off = 0; long dict_size = 0; struct list_head *pos; LIST_HEAD(items); p = strdup(path); if (!p) return -1; unix_path(p); i = strlen(p) - 1; while (i >= 0 && p[i] == '/') { p[i --] = 0; } idf_tree(p, &items, NULL); free(p); list_for_each(pos, &items) { idf_item_t *it = (idf_item_t *)pos; dict_size += (1 + strlen(it->path) + 4 + 4); } fd = fopen(dirpath(file), "wb"); fwrite("IDF1", 1, 4, fd); write_word(fd, dict_size); off = 4 + 4 + dict_size; list_for_each(pos, &items) { unsigned char s; idf_item_t *it = (idf_item_t *)pos; s = strlen(it->path); if (fwrite(&s, 1, 1, fd) != 1) goto err; p = strdup(it->path); if (!p) goto err; tolow(p); /* in idf always lowcase */ if (fwrite(p, 1, s, fd) != s) { free(p); goto err; } free(p); if (write_word(fd, off) < 0) goto err; if (write_word(fd, it->size) < 0) goto err; off += it->size; } list_for_each(pos, &items) { idf_item_t *it = (idf_item_t *)pos; char *p; p = getfilepath(path, it->path); if (p) { int rc = fcopy(fd, p); free(p); if (rc) { fprintf(stderr, "Error while copy file '%s'...\n", it->path); goto err; } } } rc = 0; err: if (rc) fprintf(stderr, "Error creating idf file...\n"); while (!list_empty(&items)) { idf_item_t *it = (idf_item_t *)items.next; free(it->path); list_del(&it->list); free(it); } fclose(fd); return rc; } int idf_seek(idff_t fil, int offset, int whence) { idfd_t *dir = fil->dir; switch (whence) { case SEEK_SET: if (offset < 0 || offset > dir->size) { return -1; } fil->pos = offset; break; case SEEK_END: if (dir->size + offset > dir->size || dir->size + offset < 0) { return -1; } fil->pos = dir->size + offset; break; case SEEK_CUR: if (fil->pos + offset > dir->size || fil->pos + offset < 0) { return -1; } fil->pos += offset; break; } if (!fseek(fil->fd, fil->pos + dir->offset, SEEK_SET)) return fil->pos; return -1; } static int idfrw_seek(struct SDL_RWops *context, int offset, int whence) { idff_t fil = (idff_t)context->hidden.unknown.data1; return idf_seek(fil, offset, whence); } int idf_read(idff_t fil, void *ptr, int size, int maxnum) { int rc = 0; long pos; idfd_t *dir = fil->dir; if (fseek(fil->fd, dir->offset + fil->pos, SEEK_SET) < 0) { return 0; } #if 1 while (maxnum) { pos = ftell(fil->fd); fil->pos = pos - dir->offset; if (fil->pos + size > dir->size) { break; } if (fread(ptr, size, 1, fil->fd) != 1) break; // fil->pos += size; ptr += size; maxnum --; rc ++; } #else rc = fread(ptr, size, maxnum, fil->fd); #endif pos = ftell(fil->fd); fil->pos = pos - dir->offset; return rc; } static int idfrw_read(struct SDL_RWops *context, void *ptr, int size, int maxnum) { idff_t fil = (idff_t)context->hidden.unknown.data1; return idf_read(fil, ptr, size, maxnum); } int idf_close(idff_t fil) { if (fil) { fclose(fil->fd); list_del(&fil->list); free(fil); } return 0; /* nothing todo */ } static int idfrw_close(struct SDL_RWops *context) { if (context) { idff_t fil = (idff_t)context->hidden.unknown.data1; idf_close(fil); SDL_FreeRW(context); } return 0; } #if 0 int idf_extract(idf_t idf, const char *fname) { FILE *out; int size; idfd_t *dir = NULL; char buff[4096]; if (idf) dir = cache_lookup(idf->dir, fname); if (!dir) return -1; fseek(idf->fd, dir->offset, SEEK_SET); out = fopen("out.bin", "wb"); size = dir->size; while (size>0) { int s ; if (size < sizeof(buff)) s = fread(buff, 1, size, idf->fd); else s = fread(buff, 1, sizeof(buff), idf->fd); fwrite(buff, 1, s, out); size -= s; fprintf(stderr, "size = %d\n", size); } fclose(out); return 0; } #endif int idf_eof(idff_t idf) { if (!idf) return 1; if (idf->pos >= idf->dir->size) return 1; return 0; } int idf_error(idff_t idf) { if (!idf || !idf->fd) return -1; return ferror(idf->fd); } int idf_only(idf_t idf, int fl) { int i; if (!idf) return -1; if (fl == -1) return idf->idfonly; i = idf->idfonly; idf->idfonly = fl; return i; } idff_t idf_open(idf_t idf, const char *fname) { idfd_t *dir = NULL; idff_t fil = NULL; char *p; if (!idf || !fname) return NULL; p = strdup(fname); if (!p) return NULL; tolow(p); if (idf) dir = cache_lookup(idf->dir, p); free(p); if (!dir) return NULL; fil = malloc(sizeof(struct _idff_t)); if (!fil) return NULL; INIT_LIST_HEAD(&fil->list); fil->dir = dir; fil->pos = 0; fil->fd = fopen(dirpath(idf->path), "rb"); if (!fil->fd) goto err; list_add(&fil->list, &dir->list); return fil; err: free(fil); return NULL; } int idf_access(idf_t idf, const char *fname) { idfd_t *dir = NULL; if (idf) dir = cache_lookup(idf->dir, fname); if (!dir) return -1; return 0; } char *idf_gets(idff_t idf, char *b, int size) { int rc, rc2; if (!idf) return NULL; if (!size) return NULL; rc = idf_read(idf, b, 1, size); if (rc < 0) return NULL; if (!rc && idf_eof(idf)) return NULL; if (!idf_eof(idf)) b[rc - 1] = 0; else { if (rc < size) b[rc] = 0; else b[size - 1] = 0; } rc2 = strcspn(b, "\n"); b[rc2] = 0; idf_seek(idf, - (rc - rc2 - 1), SEEK_CUR); return b; } SDL_RWops *RWFromIdf(idf_t idf, const char *fname) { idff_t fil = NULL; SDL_RWops *n; fil = idf_open(idf, fname); if (!fil) { if (!idf || !idf->idfonly) return SDL_RWFromFile(dirpath(fname), "rb"); return NULL; } n = SDL_AllocRW(); if (!n) goto err; n->seek = idfrw_seek; n->read = idfrw_read; n->close = idfrw_close; n->hidden.unknown.data1 = fil; return n; err: if (n) SDL_FreeRW(n); free(fil); return NULL; }