added assfile
[raydungeon] / libs / assfile / tar.c
diff --git a/libs/assfile/tar.c b/libs/assfile/tar.c
new file mode 100644 (file)
index 0000000..0a0910b
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+assfile - library for accessing assets with an fopen/fread-like interface
+Copyright (C) 2018  John Tsiombikas <nuclear@member.fsf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "tar.h"
+
+#define MAX_NAME_LEN   100
+#define MAX_PREFIX_LEN 131
+
+struct header {
+       char name[MAX_NAME_LEN];
+       char mode[8];
+       char uid[8], gid[8];
+       char size[12];
+       char mtime[12];
+       char chksum[8];
+       char typeflag;
+       char linkname[100];
+       char magic[6];
+       char version[2];
+       char uname[32], gname[32];
+       char devmajor[8], devminor[8];
+       char prefix[MAX_PREFIX_LEN];
+       char atime[12], ctime[12];
+};
+
+struct node {
+       struct tar_entry file;
+       struct node *next;
+};
+
+
+int load_tar(struct tar *tar, const char *fname)
+{
+       int i, res = -1;
+       struct node *node, *head = 0, *tail = 0;
+       char buf[512];
+       struct header *hdr;
+       unsigned long offset = 0, size, blksize;
+       char *path, *endp;
+
+       if(!(tar->fp = fopen(fname, "rb"))) {
+               fprintf(stderr, "load_tar: failed to open %s: %s\n", fname, strerror(errno));
+               return -1;
+       }
+       tar->num_files = 0;
+       tar->files = 0;
+
+       while(fread(buf, 1, sizeof buf, tar->fp) == sizeof buf) {
+               hdr = (struct header*)buf;
+
+               offset += 512;
+               size = strtol(hdr->size, &endp, 8);
+               if(endp == hdr->size || size <= 0) {
+                       continue;       /* skip invalid */
+               }
+               blksize = ((size - 1) | 0x1ff) + 1;     /* round to next 512-block */
+
+               /* verify filesize reasonable */
+               fseek(tar->fp, size - 1, SEEK_CUR);
+               if(fgetc(tar->fp) == -1) {
+                       break;  /* invalid and reached EOF */
+               }
+               fseek(tar->fp, blksize - size, SEEK_CUR);
+
+               if(memcmp(hdr->magic, "ustar", 5) == 0) {
+                       int nlen = strnlen(hdr->name, MAX_NAME_LEN);
+                       int plen = strnlen(hdr->prefix, MAX_PREFIX_LEN);
+                       if(!(path = malloc(nlen + plen + 1))) {
+                               perror("failed to allocate file path string");
+                               goto end;
+                       }
+                       memcpy(path, hdr->prefix, plen);
+                       memcpy(path + plen, hdr->name, nlen);
+                       path[nlen + plen] = 0;
+               } else {
+                       int nlen = strnlen(hdr->name, MAX_NAME_LEN);
+                       if(!(path = malloc(nlen + 1))) {
+                               perror("failed to allocate file path string");
+                               goto end;
+                       }
+                       memcpy(path, hdr->name, nlen);
+                       path[nlen] = 0;
+               }
+
+               if(!(node = malloc(sizeof *node))) {
+                       perror("failed to allocate file list node");
+                       goto end;
+               }
+               node->file.path = path;
+               node->file.offset = offset;
+               node->file.size = size;
+               node->next = 0;
+               if(head) {
+                       tail->next = node;
+                       tail = node;
+               } else {
+                       head = tail = node;
+               }
+               tar->num_files++;
+
+               /*printf(" %s - size %ld @%ld\n", node->file.path, node->file.size, node->file.offset);*/
+
+               offset += blksize;
+       }
+
+       if(!(tar->files = malloc(tar->num_files * sizeof *tar->files))) {
+               perror("failed to allocate file list");
+               goto end;
+       }
+
+       node = head;
+       for(i=0; i<tar->num_files; i++) {
+               tar->files[i] = node->file;
+               node = node->next;
+       }
+
+       if(tar->num_files > 0) {
+               res = 0;
+       }
+
+end:
+       while(head) {
+               node = head;
+               head = head->next;
+               if(node) {
+                       if(res == -1) {
+                               free(node->file.path);
+                       }
+                       free(node);
+               }
+       }
+       return res;
+}
+
+
+void close_tar(struct tar *tar)
+{
+       if(!tar) return;
+
+       if(tar->fp) {
+               fclose(tar->fp);
+               tar->fp = 0;
+       }
+       if(tar->files && tar->num_files > 0) {
+               int i;
+               for(i=0; i<tar->num_files; i++) {
+                       free(tar->files[i].path);
+               }
+               free(tar->files);
+               tar->files = 0;
+               tar->num_files = 0;
+       }
+}