Rewrite of the stage module.
[cantaveria.git] / zip.c
blob855bb347924012c89fba4293df0fce6eb4fe4250
1 /*
2 zip
3 read from a zip file
5 Copyright (C) 2009 Evan Rinehart
7 This software comes with no warranty.
8 1. This software can be used for any purpose, good or evil.
9 2. Modifications must retain this license, at least in spirit.
13 read files from a zip file.
15 *depends on zlib
16 *zip archive may be in a file, or accessed using a reader struct
17 *reader struct must support seeking
18 *directory listing is supported
19 *various errors with the archive and i/o are reported via zip_geterror()
21 *files in the archive are accessed sequentially, no seek supported
22 *writing is not supported
23 *zip64 not supported
24 *only inflate compression method supported
29 #include <stdio.h>
30 #include <string.h>
31 #include <stdlib.h>
33 #include <zlib.h>
35 #include <zip.h>
37 #define BUF_SIZE 1024
38 #define TABLE_SIZE 128
39 #define EBUF_SIZE 256
41 struct record {
42 char* filename;
43 unsigned offset;
44 unsigned clen;
45 unsigned ulen;
46 unsigned method;
47 struct record* next;
48 struct record* contents;
51 struct zip_archive {
52 int (*read)(void* userdata, byte buf[], int count);
53 int (*seek)(void* userdata, int offset, int whence);
54 void (*close)(void* userdata);
55 void* userdata;
57 struct record* table[TABLE_SIZE];
58 int ptr;
61 struct zip_file {
62 zip_archive* arc;
63 int cptr, clen;
64 int uptr, ulen;
65 int offset;
66 byte inbuf[BUF_SIZE];
67 int eof;
68 z_stream strm;
71 struct zip_dir {
72 struct record* ptr;
76 static char errbuf[EBUF_SIZE] = "";
79 /* internal routines */
81 static void set_error(char* msg){
82 strncpy(errbuf, msg, EBUF_SIZE);
83 errbuf[EBUF_SIZE-1] = '\0';
86 static void out_of_memory(){
87 set_error("OUT OF MEMORY");
90 /* http://www.cse.yorku.ca/~oz/hash.html */
91 static unsigned long hash(char* str){
92 unsigned char* ptr = (unsigned char*)str;
93 unsigned long hash = 5381;
94 int c;
95 while((c = *ptr++))
96 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
97 return hash;
101 static struct record* make_record(char* filename, int ulen, int clen, int offset, int method){
102 struct record* r = malloc(sizeof(struct record));
103 if(r == NULL){
104 return NULL;
106 r->filename = strdup(filename);
107 r->clen = clen;
108 r->ulen = ulen;
109 r->offset = offset;
110 r->method = method;
111 r->next = NULL;
112 r->contents = NULL;
113 return r;
116 static struct record* copy_record(struct record* rec){
117 return make_record(rec->filename, rec->ulen, rec->clen, rec->offset, rec->method);
120 static struct record* get_record(zip_archive* arc, char* filename){
121 int i = hash(filename) % TABLE_SIZE;
122 struct record* ptr = arc->table[i];
123 while(ptr){
124 if(strcmp(filename, ptr->filename) == 0){
125 return ptr;
127 ptr = ptr->next;
129 return NULL;
132 static void hash_insert(zip_archive* arc, struct record* rec){
133 int i = hash(rec->filename) % TABLE_SIZE;
134 struct record* ptr = arc->table[i];
136 if(get_record(arc, rec->filename)){
137 fprintf(stderr, "zip: can't insert %s twice\n", rec->filename);
138 return;
141 if(ptr == NULL){
142 arc->table[i] = rec;
143 return;
146 rec->next = ptr;
147 arc->table[i] = rec;
150 static char* baseof(char* path){
151 if(strlen(path) < 2) return NULL;
152 int ptr = strlen(path) - 2;
153 while(ptr > 0 && path[ptr] != '/') ptr--;
154 if(ptr == 0) return NULL;
155 char* result = malloc(ptr+5);
156 memcpy(result, path, ptr+1);
157 result[ptr+1] = '\0';
158 return result;
161 static void contents_insert(struct record* item, struct record* dir){
162 item->next = dir->contents;
163 dir->contents = item;
166 static void print_hash(zip_archive* arc){
167 int i;
168 struct record* rec;
169 for(i=0; i<TABLE_SIZE; i++){
170 printf("table[%d]: ",i);
171 rec = arc->table[i];
172 while(rec){
173 printf("%p ", rec);
174 rec = rec->next;
176 printf("\n");
180 static void print_zipfile(zip_file* f){
181 printf("(+0x%x, %u/%uB, cptr +0x%x, uptr %u)\n",
182 f->offset, f->clen, f->ulen, f->cptr, f->uptr);
185 static void print_record(struct record* rec){
186 printf("(%s, %uB, %uB, +0x%x, method %u)\n", rec->filename, rec->clen, rec->ulen, rec->offset, rec->method);
189 static char* method_str(unsigned method){
190 switch(method){
191 case 0: return "uncompressed";
192 case 1: return "shrink";
193 case 2:
194 case 3:
195 case 4:
196 case 5: return "reduce";
197 case 6: return "implode";
198 case 8: return "deflate";
199 case 9: return "deflate64";
200 case 10: return "IBM TERSE (old)";
201 case 12: return "bzip2";
202 case 14: return "lzma";
203 case 18: return "IBM TERSE (new)";
204 case 19: return "IBM LZ77 z";
205 case 97: return "WavPack";
206 case 98: return "PPMd";
207 default: return "unknown";
211 static void unrecognized_method(unsigned method){
212 char buf[64];
213 snprintf(buf, 64, "unrecognized compression method '%s'", method_str(method));
214 buf[63] = '\0';
215 set_error(buf);
218 static void set_record(zip_archive* arc, char* filename, int ulen, int clen, int offset, int method){
219 struct record* rec;
220 struct record* dir;
221 char* dirname;
222 if(get_record(arc, filename)) return;
223 rec = make_record(filename, ulen, clen, offset, method);
224 hash_insert(arc, rec);
225 dirname = baseof(filename);
226 if(dirname == NULL) return;
227 if(get_record(arc, dirname) == NULL)
228 set_record(arc, dirname, 0, 0, 0, 0);
229 dir = get_record(arc, dirname);
230 contents_insert(copy_record(rec), dir);
231 free(dirname);
236 static int read_bytes(zip_archive* arc, void* buf, int count){
237 return arc->read(arc->userdata, buf, count);
240 static int read_chunk(zip_archive* arc, void* buf, int count){
241 int n = read_bytes(arc, buf, count);
242 if(n < 0){
243 set_error("read error");
244 return -1;
246 if(n < count){
247 set_error("not enough data");
248 return -1;
250 return 0;
253 static int skip(zip_archive* arc, int count){
254 char buf[256];
255 int n;
256 int total = 0;
258 while(total != count){
259 int diff = count - total;
260 int chunk = diff < 256 ? diff : 256;
261 n = read_bytes(arc, buf, chunk);
262 if(n < 0){
263 set_error("read error");
264 return -1;
266 if(n < chunk){
267 set_error("not enough data");
268 return -1;
270 total += n;
273 return 0;
276 static int read_long(zip_archive* arc, unsigned* n){
277 unsigned char b[4];
278 int e = read_bytes(arc, b, 4);
279 if(e < 0){
280 set_error("read error");
281 return -1;
283 if(e < 4){
284 set_error("not enough data");
285 return -1;
287 *n = b[0] | (b[1]<<8) | (b[2]<<16) | (b[3]<<24);
288 return 0;
291 static int read_short(zip_archive* arc, unsigned* n){
292 unsigned char b[2];
293 int e = read_bytes(arc, b, 2);
294 if(e < 0){
295 set_error("read error");
296 return -1;
298 if(e < 2){
299 set_error("not enough data");
300 return -1;
302 *n = b[0] | (b[1]<<8);
303 return 0;
307 static int parse_local_header(zip_archive* arc){
308 unsigned L1, L2;
309 char* filename;
310 unsigned method;
311 unsigned clen, ulen;
312 unsigned offset;
313 unsigned flags;
316 skip(arc, 2) ||
317 read_short(arc, &flags) ||
318 read_short(arc, &method) ||
319 skip(arc, 8) ||
320 read_long(arc, &clen) ||
321 read_long(arc, &ulen) ||
322 read_short(arc, &L1) ||
323 read_short(arc, &L2)
325 return -1;
328 filename = malloc(L1+1);
330 read_chunk(arc, filename, L1) ||
331 skip(arc, L2) ||
332 skip(arc, clen)
334 free(filename);
335 return -1;
338 filename[L1] = '\0';
339 offset = arc->ptr + 30 + L1 + L2;
340 arc->ptr = offset + clen + (flags&(1<<3) ? 12 : 0);
342 set_record(arc, filename, ulen, clen, offset, method);
344 free(filename);
346 return 0;
349 static int build_directory(zip_archive* arc){
350 while(1){
351 unsigned sig;
352 if(read_long(arc, &sig) < 0) return -1;
353 else if(sig == 0x04034b50) parse_local_header(arc);
354 else break;
357 return 0;
361 static void free_directory_r(struct record* r){
362 if(r->contents) free_directory_r(r->contents);
363 if(r->next) free_directory_r(r->next);
364 free(r->filename);
365 free(r);
368 static void free_directory(zip_archive* arc){
369 int i;
370 for(i=0; i<TABLE_SIZE; i++){
371 if(arc->table[i]) free_directory_r(arc->table[i]);
376 static int fill_inbuf(zip_file* f){
377 int nread, nwant;
378 int nleftstream, nleftbuffer;
380 /* shift everything left */
381 memmove(f->inbuf, f->strm.next_in, f->strm.avail_in);
382 f->strm.next_in = f->inbuf;
384 /* fill the buffer */
385 if(f->arc->seek(f->arc->userdata, f->offset+f->cptr, SEEK_SET) < 0){
386 set_error("archive seek error");
387 return -1;
390 /* you want the minimum of
391 a) bytes needed to fill the buffer and
392 b) bytes left in the compressed stream */
393 nleftstream = f->clen - f->cptr;
394 nleftbuffer = BUF_SIZE - f->strm.avail_in;
395 nwant = nleftstream < nleftbuffer ? nleftstream : nleftbuffer;
396 nread = f->arc->read(f->arc->userdata, f->inbuf+f->strm.avail_in, nwant);
397 if(nread < 0){
398 set_error("archive read error");
399 return -1;
402 f->cptr += nread;
403 f->strm.avail_in += nread;
404 return nread;
407 static int inflate_chunk(zip_file* f, byte buf[], int count){
408 f->strm.next_out = buf;
409 f->strm.avail_out = count;
410 int e = inflate(&f->strm, Z_SYNC_FLUSH);
411 switch(e){
412 case Z_OK:
413 return count - f->strm.avail_out;
414 case Z_STREAM_END:
415 f->eof = 1;
416 return count - f->strm.avail_out;
417 case Z_BUF_ERROR:
418 return 0;
419 case Z_NEED_DICT:
420 set_error("inflate needs a preset dictionary at this point");
421 return -1;
422 case Z_DATA_ERROR:
423 set_error("inflate data error (input corrupted or in wrong format)");
424 return -1;
425 case Z_STREAM_ERROR:
426 set_error("inflate stream error (inconsistent stream structure)");
427 return -1;
428 case Z_MEM_ERROR:
429 set_error("inflate out of memory");
430 return -1;
431 default:
432 set_error("inflate error (unknown)");
433 return -1;
438 /* these are callbacks for the default archive reader, filesystem i/o */
439 static int file_read(void* f, byte buf[], int count){
440 return fread(buf, 1, count, f);
443 static int file_seek(void* f, int offset, int whence){
444 return fseek(f, offset, whence);
447 static void file_close(void* f){
448 fclose(f);
457 /* public methods */
459 zip_archive* zip_aropenf(char* filename){
460 FILE* f = fopen(filename, "r");
461 if(f == NULL){
462 set_error("i/o error");
463 return NULL;
465 zip_reader rd = {file_read, file_seek, file_close, f};
466 return zip_aropen(&rd);
469 zip_archive* zip_aropen(zip_reader* rd){
470 int i;
471 zip_archive* arc = malloc(sizeof(zip_archive));
472 if(arc == NULL){
473 out_of_memory();
474 return NULL;
476 arc->read = rd->read;
477 arc->seek = rd->seek;
478 arc->close = rd->close;
479 arc->userdata = rd->userdata;
480 arc->ptr = 0;
482 for(i=0; i<TABLE_SIZE; i++){
483 arc->table[i] = NULL;
486 if(build_directory(arc) < 0){
487 free_directory(arc);
488 free(arc);
489 return NULL;
492 return arc;
495 void zip_arclose(zip_archive* arc){
496 arc->close(arc->userdata);
497 free_directory(arc);
498 free(arc);
501 zip_file* zip_fopen(zip_archive* arc, char* path){
502 struct record* r = get_record(arc, path);
503 if(r == NULL){
504 set_error("file not found");
505 return NULL;
508 if(r->filename[strlen(r->filename)-1] == '/'){
509 set_error("cannot open directory");
510 return NULL;
513 if(r->method != 8){
514 unrecognized_method(r->method);
515 return NULL;
518 zip_file* f = malloc(sizeof(zip_file));
519 if(f == NULL){
520 out_of_memory();
521 return NULL;
524 f->eof = 0;
525 f->arc = arc;
526 f->strm.zalloc = NULL;
527 f->strm.zfree = NULL;
528 f->strm.opaque = NULL;
529 f->strm.next_in = f->inbuf;
530 f->strm.avail_in = 0;
531 f->strm.next_out = NULL;
532 f->strm.avail_out = 0;
533 f->clen = r->clen;
534 f->cptr = 0;
535 f->ulen = r->ulen;
536 f->uptr = 0;
537 f->offset = r->offset;
539 int e = inflateInit2(&f->strm, -15);
540 if(e != Z_OK){
541 switch(e){
542 case Z_MEM_ERROR: set_error("memory"); break;
543 case Z_STREAM_ERROR: set_error("stream"); break;
545 free(f);
546 return NULL;
549 return f;
552 void zip_fclose(zip_file* f){
553 inflateEnd(&f->strm);
554 free(f);
557 int zip_fread(zip_file* f, byte buf[], int count){
558 int total = 0;
559 int n;
560 int sentry = 0; /* annoying */
562 while(count > 0 && !f->eof){
563 n = inflate_chunk(f, buf+total, count);
565 if(n < 0) return -1;
566 if(n == 0){
567 /* need more input */
568 if(fill_inbuf(f) < 0) return -1;
569 if(sentry == 1){
570 set_error("unable to satisfy buffer requirements");
571 return -1;
573 sentry = 1;
574 continue;
577 sentry = 0;
578 count -= n;
579 total += n;
581 f->uptr += n;
582 if(f->uptr == f->ulen){
583 f->eof = 1;
587 return total;
590 int zip_feof(zip_file* f){
591 return f->eof;
597 zip_dir* zip_opendir(zip_archive* arc, char* path){
598 if(path[strlen(path)-1] != '/'){
599 set_error("path does not specify directory");
600 return NULL;
603 zip_dir* dir = malloc(sizeof(zip_dir));
604 if(dir == NULL){
605 out_of_memory();
606 return NULL;
609 struct record* r = get_record(arc, path);
610 if(r == NULL){
611 set_error("file not found");
612 free(dir);
613 return NULL;
616 dir->ptr = r->contents;
617 return dir;
620 char* zip_readdir(zip_dir* dir){
621 if(dir->ptr == NULL){
622 /* no more entries, not an error */
623 return NULL;
625 else{
626 char* filename = dir->ptr->filename;
627 dir->ptr = dir->ptr->next;
628 return filename;
632 void zip_closedir(zip_dir* dir){
633 free(dir);
637 char* zip_geterror(){
638 return errbuf;