Initial Contribution
diff --git a/libzipfile/zipfile.c b/libzipfile/zipfile.c
new file mode 100644
index 0000000..b52d02d
--- /dev/null
+++ b/libzipfile/zipfile.c
@@ -0,0 +1,160 @@
+#include <zipfile/zipfile.h>
+
+#include "private.h"
+#include <stdlib.h>
+#include <string.h>
+#include <zlib.h>
+#define DEF_MEM_LEVEL 8                // normally in zutil.h?
+
+zipfile_t
+init_zipfile(const void* data, size_t size)
+{
+    int err;
+
+    Zipfile *file = malloc(sizeof(Zipfile));
+    if (file == NULL) return NULL;
+    memset(file, 0, sizeof(Zipfile));
+    file->buf = data;
+    file->bufsize = size;
+
+    err = read_central_dir(file);
+    if (err != 0) goto fail;
+
+    return file;
+fail:
+    free(file);
+    return NULL;
+}
+
+void
+release_zipfile(zipfile_t f)
+{
+    Zipfile* file = (Zipfile*)f;
+    Zipentry* entry = file->entries;
+    while (entry) {
+        Zipentry* next = entry->next;
+        free(entry);
+        entry = next;
+    }
+    free(file);
+}
+
+zipentry_t
+lookup_zipentry(zipfile_t f, const char* entryName)
+{
+    Zipfile* file = (Zipfile*)f;
+    Zipentry* entry = file->entries;
+    while (entry) {
+        if (0 == memcmp(entryName, entry->fileName, entry->fileNameLength)) {
+            return entry;
+        }
+        entry = entry->next;
+    }
+    return NULL;
+}
+
+size_t
+get_zipentry_size(zipentry_t entry)
+{
+    return ((Zipentry*)entry)->uncompressedSize;
+}
+
+char*
+get_zipentry_name(zipentry_t entry)
+{
+    Zipentry* e = (Zipentry*)entry;
+    int l = e->fileNameLength;
+    char* s = malloc(l+1);
+    memcpy(s, e->fileName, l);
+    s[l] = '\0';
+    return s;
+}
+
+enum {
+    STORED = 0,
+    DEFLATED = 8
+};
+
+static int
+uninflate(unsigned char* out, int unlen, const unsigned char* in, int clen)
+{
+    z_stream zstream;
+    unsigned long crc;
+    int err = 0;
+    int zerr;
+    
+    memset(&zstream, 0, sizeof(zstream));
+    zstream.zalloc = Z_NULL;
+    zstream.zfree = Z_NULL;
+    zstream.opaque = Z_NULL;
+    zstream.next_in = (void*)in;
+    zstream.avail_in = unlen;
+    zstream.next_out = (Bytef*) out;
+    zstream.avail_out = unlen;
+    zstream.data_type = Z_UNKNOWN;
+
+    // Use the undocumented "negative window bits" feature to tell zlib
+    // that there's no zlib header waiting for it.
+    zerr = inflateInit2(&zstream, -MAX_WBITS);
+    if (zerr != Z_OK) {
+        return -1;
+    }
+    
+    // uncompress the data
+    zerr = inflate(&zstream, Z_FINISH);
+    if (zerr != Z_STREAM_END) {
+        fprintf(stderr, "zerr=%d Z_STREAM_END=%d total_out=%lu\n", zerr, Z_STREAM_END,
+                    zstream.total_out);
+        err = -1;
+    }
+    
+     inflateEnd(&zstream);
+    return err;
+}
+
+int
+decompress_zipentry(zipentry_t e, void* buf, int bufsize)
+{
+    Zipentry* entry = (Zipentry*)e;
+    switch (entry->compressionMethod)
+    {
+        case STORED:
+            memcpy(buf, entry->data, entry->uncompressedSize);
+            return 0;
+        case DEFLATED:
+            return uninflate(buf, bufsize, entry->data, entry->compressedSize);
+        default:
+            return -1;
+    }
+}
+
+void
+dump_zipfile(FILE* to, zipfile_t file)
+{
+    Zipfile* zip = (Zipfile*)file;
+    Zipentry* entry = zip->entries;
+    int i;
+
+    fprintf(to, "entryCount=%d\n", zip->entryCount);
+    for (i=0; i<zip->entryCount; i++) {
+        fprintf(to, "  file \"");
+        fwrite(entry->fileName, entry->fileNameLength, 1, to);
+        fprintf(to, "\"\n");
+        entry = entry->next;
+    }
+}
+
+zipentry_t
+iterate_zipfile(zipfile_t file, void** cookie)
+{
+    Zipentry* entry = (Zipentry*)*cookie;
+    if (entry == NULL) {
+        Zipfile* zip = (Zipfile*)file;
+        *cookie = zip->entries;
+        return *cookie;
+    } else {
+        entry = entry->next;
+        *cookie = entry;
+        return entry;
+    }
+}