initial import
[picodrive.git] / unzip / unzip.c
diff --git a/unzip/unzip.c b/unzip/unzip.c
new file mode 100644 (file)
index 0000000..4f1786c
--- /dev/null
@@ -0,0 +1,623 @@
+#include "unzip.h"\r
+\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <ctype.h>\r
+#include <assert.h>\r
+\r
+#ifdef __SYMBIAN32__\r
+#include <ezlib.h>\r
+#else\r
+#include "zlib/zlib.h"\r
+#endif\r
+\r
+/* public globals */\r
+//int  gUnzipQuiet = 0;                /* flag controls error messages */\r
+\r
+#define ERROR_CORRUPT "The zipfile seems to be corrupt, please check it"\r
+#define ERROR_FILESYSTEM "Your filesystem seems to be corrupt, please check it"\r
+#define ERROR_UNSUPPORTED "The format of this zipfile is not supported, please recompress it"\r
+\r
+#define INFLATE_INPUT_BUFFER_MAX 16384\r
+#ifndef MIN\r
+#define MIN(x,y) ((x)<(y)?(x):(y))\r
+#endif\r
+\r
+\r
+// notaz\r
+#ifdef __DEBUG_PRINT\r
+void dprintf(char *format, ...);\r
+#define logerror dprintf\r
+void errormsg(const char* extmsg, const char* usermsg, const char* zipname)\r
+{\r
+       dprintf("Error in zipfile %s: %s", zipname, extmsg);\r
+}\r
+#else\r
+#define logerror(x...)\r
+#define errormsg(x...)\r
+#endif\r
+\r
+/* Print a error message */\r
+//void errormsg(const char* extmsg, const char* usermsg, const char* zipname) {\r
+       /* Output to the user with no internal detail */\r
+//     if (!gUnzipQuiet)\r
+//             printf("Error in zipfile %s\n%s\n", zipname, usermsg);\r
+       /* Output to log file with all informations */\r
+//     logerror("Error in zipfile %s: %s\n", zipname, extmsg);\r
+//     printf("Error in zipfile %s: %s\n", zipname, extmsg);\r
+//}\r
+\r
+/* -------------------------------------------------------------------------\r
+   Unzip support\r
+ ------------------------------------------------------------------------- */\r
+\r
+/* Use these to avoid structure padding and byte-ordering problems */\r
+static UINT16 read_word (char *buf) {\r
+   unsigned char *ubuf = (unsigned char *) buf;\r
+\r
+   return ((UINT16)ubuf[1] << 8) | (UINT16)ubuf[0];\r
+}\r
+\r
+/* Use these to avoid structure padding and byte-ordering problems */\r
+static UINT32 read_dword (char *buf) {\r
+   unsigned char *ubuf = (unsigned char *) buf;\r
+\r
+   return ((UINT32)ubuf[3] << 24) | ((UINT32)ubuf[2] << 16) | ((UINT32)ubuf[1] << 8) | (UINT32)ubuf[0];\r
+}\r
+\r
+/* Locate end-of-central-dir sig in buffer and return offset\r
+   out:\r
+       *offset offset of cent dir start in buffer\r
+   return:\r
+       ==0 not found\r
+       !=0 found, *offset valid\r
+*/\r
+static int ecd_find_sig (char *buffer, int buflen, int *offset)\r
+{\r
+       static char ecdsig[] = { 'P', 'K', 0x05, 0x06 };\r
+       int i;\r
+       for (i=buflen-22; i>=0; i--) {\r
+               if (memcmp(buffer+i, ecdsig, 4) == 0) {\r
+                       *offset = i;\r
+                       return 1;\r
+               }\r
+       }\r
+       return 0;\r
+}\r
+\r
+/* Read ecd data in zip structure\r
+   in:\r
+     zip->fp, zip->length zip file\r
+   out:\r
+     zip->ecd, zip->ecd_length ecd data\r
+*/\r
+static int ecd_read(ZIP* zip) {\r
+       char* buf;\r
+       int buf_length = 1024; /* initial buffer length */\r
+\r
+       while (1) {\r
+               int offset;\r
+\r
+               if (buf_length > zip->length)\r
+                       buf_length = zip->length;\r
+\r
+               if (fseek(zip->fp, zip->length - buf_length, SEEK_SET) != 0) {\r
+                       return -1;\r
+               }\r
+\r
+               /* allocate buffer */\r
+               buf = (char*)malloc( buf_length );\r
+               if (!buf) {\r
+                       return -1;\r
+               }\r
+\r
+               if (fread( buf, buf_length, 1, zip->fp ) != 1) {\r
+                       free(buf);\r
+                       return -1;\r
+               }\r
+\r
+               if (ecd_find_sig(buf, buf_length, &offset)) {\r
+                       zip->ecd_length = buf_length - offset;\r
+\r
+                       zip->ecd = (char*)malloc( zip->ecd_length );\r
+                       if (!zip->ecd) {\r
+                               free(buf);\r
+                               return -1;\r
+                       }\r
+\r
+                       memcpy(zip->ecd, buf + offset, zip->ecd_length);\r
+\r
+                       free(buf);\r
+                       return 0;\r
+               }\r
+\r
+               free(buf);\r
+\r
+               if (buf_length < zip->length) {\r
+                       /* double buffer */\r
+                       buf_length = 2*buf_length;\r
+\r
+                       logerror("Retry reading of zip ecd for %d bytes\n",buf_length);\r
+\r
+               } else {\r
+                       return -1;\r
+               }\r
+       }\r
+}\r
+\r
+/* offsets in end of central directory structure */\r
+#define ZIPESIG                0x00\r
+#define ZIPEDSK                0x04\r
+#define ZIPECEN                0x06\r
+#define ZIPENUM                0x08\r
+#define ZIPECENN       0x0a\r
+#define ZIPECSZ                0x0c\r
+#define ZIPEOFST       0x10\r
+#define ZIPECOML       0x14\r
+#define ZIPECOM                0x16\r
+\r
+/* offsets in central directory entry structure */\r
+#define ZIPCENSIG      0x0\r
+#define ZIPCVER                0x4\r
+#define ZIPCOS         0x5\r
+#define        ZIPCVXT         0x6\r
+#define        ZIPCEXOS        0x7\r
+#define ZIPCFLG                0x8\r
+#define ZIPCMTHD       0xa\r
+#define ZIPCTIM                0xc\r
+#define ZIPCDAT                0xe\r
+#define ZIPCCRC                0x10\r
+#define ZIPCSIZ                0x14\r
+#define ZIPCUNC                0x18\r
+#define ZIPCFNL                0x1c\r
+#define ZIPCXTL                0x1e\r
+#define ZIPCCML                0x20\r
+#define ZIPDSK         0x22\r
+#define ZIPINT         0x24\r
+#define ZIPEXT         0x26\r
+#define ZIPOFST                0x2a\r
+#define ZIPCFN         0x2e\r
+\r
+/* offsets in local file header structure */\r
+#define ZIPLOCSIG      0x00\r
+#define ZIPVER         0x04\r
+#define ZIPGENFLG      0x06\r
+#define ZIPMTHD                0x08\r
+#define ZIPTIME                0x0a\r
+#define ZIPDATE                0x0c\r
+#define ZIPCRC         0x0e\r
+#define ZIPSIZE                0x12\r
+#define ZIPUNCMP       0x16\r
+#define ZIPFNLN                0x1a\r
+#define ZIPXTRALN      0x1c\r
+#define ZIPNAME                0x1e\r
+\r
+/* Opens a zip stream for reading\r
+   return:\r
+     !=0 success, zip stream\r
+     ==0 error\r
+*/\r
+ZIP* openzip(const char* zipfile) {\r
+       /* allocate */\r
+       ZIP* zip = (ZIP*)malloc( sizeof(ZIP) );\r
+       if (!zip) {\r
+               return 0;\r
+       }\r
+\r
+       /* open */\r
+       zip->fp = fopen(zipfile, "rb");\r
+       if (!zip->fp) {\r
+               errormsg ("Opening for reading", ERROR_FILESYSTEM, zipfile);\r
+               free(zip);\r
+               return 0;\r
+       }\r
+\r
+       /* go to end */\r
+       if (fseek(zip->fp, 0L, SEEK_END) != 0) {\r
+               errormsg ("Seeking to end", ERROR_FILESYSTEM, zipfile);\r
+               fclose(zip->fp);\r
+               free(zip);\r
+               return 0;\r
+       }\r
+\r
+       /* get length */\r
+       zip->length = ftell(zip->fp);\r
+       if (zip->length < 0) {\r
+               errormsg ("Get file size", ERROR_FILESYSTEM, zipfile);\r
+               fclose(zip->fp);\r
+               free(zip);\r
+               return 0;\r
+       }\r
+       if (zip->length == 0) {\r
+               errormsg ("Empty file", ERROR_CORRUPT, zipfile);\r
+               fclose(zip->fp);\r
+               free(zip);\r
+               return 0;\r
+       }\r
+\r
+       /* read ecd data */\r
+       if (ecd_read(zip)!=0) {\r
+               errormsg ("Reading ECD (end of central directory)", ERROR_CORRUPT, zipfile);\r
+               fclose(zip->fp);\r
+               free(zip);\r
+               return 0;\r
+       }\r
+\r
+       /* compile ecd info */\r
+       zip->end_of_cent_dir_sig = read_dword (zip->ecd+ZIPESIG);\r
+       zip->number_of_this_disk = read_word (zip->ecd+ZIPEDSK);\r
+       zip->number_of_disk_start_cent_dir = read_word (zip->ecd+ZIPECEN);\r
+       zip->total_entries_cent_dir_this_disk = read_word (zip->ecd+ZIPENUM);\r
+       zip->total_entries_cent_dir = read_word (zip->ecd+ZIPECENN);\r
+       zip->size_of_cent_dir = read_dword (zip->ecd+ZIPECSZ);\r
+       zip->offset_to_start_of_cent_dir = read_dword (zip->ecd+ZIPEOFST);\r
+       zip->zipfile_comment_length = read_word (zip->ecd+ZIPECOML);\r
+       zip->zipfile_comment = zip->ecd+ZIPECOM;\r
+\r
+       /* verify that we can work with this zipfile (no disk spanning allowed) */\r
+       if ((zip->number_of_this_disk != zip->number_of_disk_start_cent_dir) ||\r
+               (zip->total_entries_cent_dir_this_disk != zip->total_entries_cent_dir) ||\r
+               (zip->total_entries_cent_dir < 1)) {\r
+               errormsg("Cannot span disks", ERROR_UNSUPPORTED, zipfile);\r
+               free(zip->ecd);\r
+               fclose(zip->fp);\r
+               free(zip);\r
+               return 0;\r
+       }\r
+\r
+       if (fseek(zip->fp, zip->offset_to_start_of_cent_dir, SEEK_SET)!=0) {\r
+               errormsg ("Seeking to central directory", ERROR_CORRUPT, zipfile);\r
+               free(zip->ecd);\r
+               fclose(zip->fp);\r
+               free(zip);\r
+               return 0;\r
+       }\r
+\r
+       /* read from start of central directory */\r
+       zip->cd = (char*)malloc( zip->size_of_cent_dir );\r
+       if (!zip->cd) {\r
+               free(zip->ecd);\r
+               fclose(zip->fp);\r
+               free(zip);\r
+               return 0;\r
+       }\r
+\r
+       if (fread(zip->cd, zip->size_of_cent_dir, 1, zip->fp)!=1) {\r
+               errormsg ("Reading central directory", ERROR_CORRUPT, zipfile);\r
+               free(zip->cd);\r
+               free(zip->ecd);\r
+               fclose(zip->fp);\r
+               free(zip);\r
+               return 0;\r
+       }\r
+\r
+       /* reset ent */\r
+       zip->ent.name = 0;\r
+\r
+       /* rewind */\r
+       zip->cd_pos = 0;\r
+\r
+       /* file name */\r
+       zip->zip = (char*)malloc(strlen(zipfile)+1);\r
+       if (!zip->zip) {\r
+               free(zip->cd);\r
+               free(zip->ecd);\r
+               fclose(zip->fp);\r
+               free(zip);\r
+               return 0;\r
+       }\r
+       strcpy(zip->zip, zipfile);\r
+\r
+       return zip;\r
+}\r
+\r
+/* Reads the current entry from a zip stream\r
+   in:\r
+     zip opened zip\r
+   return:\r
+     !=0 success\r
+     ==0 error\r
+*/\r
+struct zipent* readzip(ZIP* zip) {\r
+\r
+       /* end of directory */\r
+       if (zip->cd_pos >= zip->size_of_cent_dir)\r
+               return 0;\r
+\r
+       /* compile zipent info */\r
+       zip->ent.cent_file_header_sig = read_dword (zip->cd+zip->cd_pos+ZIPCENSIG);\r
+       zip->ent.version_made_by = *(zip->cd+zip->cd_pos+ZIPCVER);\r
+       zip->ent.host_os = *(zip->cd+zip->cd_pos+ZIPCOS);\r
+       zip->ent.version_needed_to_extract = *(zip->cd+zip->cd_pos+ZIPCVXT);\r
+       zip->ent.os_needed_to_extract = *(zip->cd+zip->cd_pos+ZIPCEXOS);\r
+       zip->ent.general_purpose_bit_flag = read_word (zip->cd+zip->cd_pos+ZIPCFLG);\r
+       zip->ent.compression_method = read_word (zip->cd+zip->cd_pos+ZIPCMTHD);\r
+       zip->ent.last_mod_file_time = read_word (zip->cd+zip->cd_pos+ZIPCTIM);\r
+       zip->ent.last_mod_file_date = read_word (zip->cd+zip->cd_pos+ZIPCDAT);\r
+       zip->ent.crc32 = read_dword (zip->cd+zip->cd_pos+ZIPCCRC);\r
+       zip->ent.compressed_size = read_dword (zip->cd+zip->cd_pos+ZIPCSIZ);\r
+       zip->ent.uncompressed_size = read_dword (zip->cd+zip->cd_pos+ZIPCUNC);\r
+       zip->ent.filename_length = read_word (zip->cd+zip->cd_pos+ZIPCFNL);\r
+       zip->ent.extra_field_length = read_word (zip->cd+zip->cd_pos+ZIPCXTL);\r
+       zip->ent.file_comment_length = read_word (zip->cd+zip->cd_pos+ZIPCCML);\r
+       zip->ent.disk_number_start = read_word (zip->cd+zip->cd_pos+ZIPDSK);\r
+       zip->ent.internal_file_attrib = read_word (zip->cd+zip->cd_pos+ZIPINT);\r
+       zip->ent.external_file_attrib = read_dword (zip->cd+zip->cd_pos+ZIPEXT);\r
+       zip->ent.offset_lcl_hdr_frm_frst_disk = read_dword (zip->cd+zip->cd_pos+ZIPOFST);\r
+\r
+    /* check to see if filename length is illegally long (past the size of this directory\r
+       entry) */\r
+    if (zip->cd_pos + ZIPCFN + zip->ent.filename_length > zip->size_of_cent_dir)\r
+    {\r
+        errormsg("Invalid filename length in directory", ERROR_CORRUPT,zip->zip);\r
+        return 0;\r
+    }\r
+\r
+       /* copy filename */\r
+       free(zip->ent.name);\r
+       zip->ent.name = (char*)malloc(zip->ent.filename_length + 1);\r
+       memcpy(zip->ent.name, zip->cd+zip->cd_pos+ZIPCFN, zip->ent.filename_length);\r
+       zip->ent.name[zip->ent.filename_length] = 0;\r
+\r
+       /* skip to next entry in central dir */\r
+       zip->cd_pos += ZIPCFN + zip->ent.filename_length + zip->ent.extra_field_length + zip->ent.file_comment_length;\r
+\r
+       return &zip->ent;\r
+}\r
+\r
+/* Closes a zip stream */\r
+void closezip(ZIP* zip) {\r
+       /* release all */\r
+       free(zip->ent.name);\r
+       free(zip->cd);\r
+       free(zip->ecd);\r
+       /* only if not suspended */\r
+       if (zip->fp)\r
+               fclose(zip->fp);\r
+       free(zip->zip);\r
+       free(zip);\r
+}\r
+\r
+/* Suspend access to a zip file (release file handler)\r
+   in:\r
+      zip opened zip\r
+   note:\r
+     A suspended zip is automatically reopened at first call of\r
+     readuncompressd() or readcompressed() functions\r
+*/\r
+void suspendzip(ZIP* zip) {\r
+       if (zip->fp) {\r
+               fclose(zip->fp);\r
+               zip->fp = 0;\r
+       }\r
+}\r
+\r
+/* Revive a suspended zip file (reopen file handler)\r
+   in:\r
+     zip suspended zip\r
+   return:\r
+       zip success\r
+       ==0 error (zip must be closed with closezip)\r
+*/\r
+static ZIP* revivezip(ZIP* zip) {\r
+       if (!zip->fp) {\r
+               zip->fp = fopen(zip->zip, "rb");\r
+               if (!zip->fp) {\r
+                       return 0;\r
+               }\r
+       }\r
+       return zip;\r
+\r
+}\r
+\r
+/* Reset a zip stream to the first entry\r
+   in:\r
+     zip opened zip\r
+   note:\r
+     ZIP file must be opened and not suspended\r
+*/\r
+void rewindzip(ZIP* zip) {\r
+       zip->cd_pos = 0;\r
+}\r
+\r
+/* Seek zip->fp to compressed data\r
+   return:\r
+       ==0 success\r
+       <0 error\r
+*/\r
+int seekcompresszip(ZIP* zip, struct zipent* ent) {\r
+       char buf[ZIPNAME];\r
+       long offset;\r
+\r
+       if (!zip->fp) {\r
+               if (!revivezip(zip))\r
+                       return -1;\r
+       }\r
+\r
+       if (fseek(zip->fp, ent->offset_lcl_hdr_frm_frst_disk, SEEK_SET)!=0) {\r
+               errormsg ("Seeking to header", ERROR_CORRUPT, zip->zip);\r
+               return -1;\r
+       }\r
+\r
+       if (fread(buf, ZIPNAME, 1, zip->fp)!=1) {\r
+               errormsg ("Reading header", ERROR_CORRUPT, zip->zip);\r
+               return -1;\r
+       }\r
+\r
+       {\r
+               UINT16 filename_length = read_word (buf+ZIPFNLN);\r
+               UINT16 extra_field_length = read_word (buf+ZIPXTRALN);\r
+\r
+               /* calculate offset to data and fseek() there */\r
+               offset = ent->offset_lcl_hdr_frm_frst_disk + ZIPNAME + filename_length + extra_field_length;\r
+\r
+               if (fseek(zip->fp, offset, SEEK_SET) != 0) {\r
+                       errormsg ("Seeking to compressed data", ERROR_CORRUPT, zip->zip);\r
+                       return -1;\r
+               }\r
+\r
+       }\r
+\r
+       return 0;\r
+}\r
+\r
+/* Inflate a file\r
+   in:\r
+   in_file stream to inflate\r
+   in_size size of the compressed data to read\r
+   out_size size of decompressed data\r
+   out:\r
+   out_data buffer for decompressed data\r
+   return:\r
+   ==0 ok\r
+\r
+   990525 rewritten for use with zlib MLR\r
+*/\r
+static int inflate_file(FILE* in_file, unsigned in_size, unsigned char* out_data, unsigned out_size)\r
+{\r
+    int err;\r
+       unsigned char* in_buffer;\r
+    z_stream d_stream; /* decompression stream */\r
+\r
+    d_stream.zalloc = 0;\r
+    d_stream.zfree = 0;\r
+    d_stream.opaque = 0;\r
+\r
+       d_stream.next_in  = 0;\r
+       d_stream.avail_in = 0;\r
+    d_stream.next_out = out_data;\r
+    d_stream.avail_out = out_size;\r
+\r
+    err = inflateInit2(&d_stream, -MAX_WBITS);\r
+       /* windowBits is passed < 0 to tell that there is no zlib header.\r
+        * Note that in this case inflate *requires* an extra "dummy" byte\r
+        * after the compressed stream in order to complete decompression and\r
+        * return Z_STREAM_END.\r
+        */\r
+    if (err != Z_OK)\r
+       {\r
+               logerror("inflateInit error: %d\n", err);\r
+        return -1;\r
+       }\r
+\r
+       in_buffer = (unsigned char*)malloc(INFLATE_INPUT_BUFFER_MAX+1);\r
+       if (!in_buffer)\r
+               return -1;\r
+\r
+    for (;;)\r
+       {\r
+               if (in_size <= 0)\r
+               {\r
+                       logerror("inflate error: compressed size too small\n");\r
+                       free (in_buffer);\r
+                       return -1;\r
+               }\r
+               d_stream.next_in  = in_buffer;\r
+               d_stream.avail_in = fread (in_buffer, 1, MIN(in_size, INFLATE_INPUT_BUFFER_MAX), in_file);\r
+               in_size -= d_stream.avail_in;\r
+               if (in_size == 0)\r
+                       d_stream.avail_in++; /* add dummy byte at end of compressed data */\r
+\r
+        err = inflate(&d_stream, Z_NO_FLUSH);\r
+        if (err == Z_STREAM_END)\r
+                       break;\r
+               if (err != Z_OK)\r
+               {\r
+                       logerror("inflate error: %d\n", err);\r
+                       free (in_buffer);\r
+                       return -1;\r
+               }\r
+    }\r
+\r
+    err = inflateEnd(&d_stream);\r
+       if (err != Z_OK)\r
+       {\r
+               logerror("inflateEnd error: %d\n", err);\r
+               free (in_buffer);\r
+               return -1;\r
+       }\r
+\r
+       free (in_buffer);\r
+\r
+       if ((d_stream.avail_out > 0) || (in_size > 0))\r
+       {\r
+               logerror("zip size mismatch. %i\n", in_size);\r
+               return -1;\r
+       }\r
+\r
+       return 0;\r
+}\r
+\r
+/* Read compressed data\r
+   out:\r
+       data compressed data read\r
+   return:\r
+       ==0 success\r
+       <0 error\r
+*/\r
+int readcompresszip(ZIP* zip, struct zipent* ent, char* data) {\r
+       int err = seekcompresszip(zip,ent);\r
+       if (err!=0)\r
+               return err;\r
+\r
+       if (fread(data, ent->compressed_size, 1, zip->fp)!=1) {\r
+               errormsg ("Reading compressed data", ERROR_CORRUPT, zip->zip);\r
+               return -1;\r
+       }\r
+\r
+       return 0;\r
+}\r
+\r
+/* Read UNcompressed data\r
+   out:\r
+       data UNcompressed data\r
+   return:\r
+       ==0 success\r
+       <0 error\r
+*/\r
+int readuncompresszip(ZIP* zip, struct zipent* ent, char* data) {\r
+       if (ent->compression_method == 0x0000) {\r
+               /* file is not compressed, simply stored */\r
+\r
+               /* check if size are equal */\r
+               if (ent->compressed_size != ent->uncompressed_size) {\r
+                       errormsg("Wrong uncompressed size in store compression", ERROR_CORRUPT,zip->zip);\r
+                       return -3;\r
+               }\r
+\r
+               return readcompresszip(zip,ent,data);\r
+       } else if (ent->compression_method == 0x0008) {\r
+               /* file is compressed using "Deflate" method */\r
+               if (ent->version_needed_to_extract > 0x14) {\r
+                       errormsg("Version too new", ERROR_UNSUPPORTED,zip->zip);\r
+                       return -2;\r
+               }\r
+\r
+               if (ent->os_needed_to_extract != 0x00) {\r
+                       errormsg("OS not supported", ERROR_UNSUPPORTED,zip->zip);\r
+                       return -2;\r
+               }\r
+\r
+               if (ent->disk_number_start != zip->number_of_this_disk) {\r
+                       errormsg("Cannot span disks", ERROR_UNSUPPORTED,zip->zip);\r
+                       return -2;\r
+               }\r
+\r
+               /* read compressed data */\r
+               if (seekcompresszip(zip,ent)!=0) {\r
+                       return -1;\r
+               }\r
+\r
+               /* configure inflate */\r
+               if (inflate_file( zip->fp, ent->compressed_size, (unsigned char*)data, ent->uncompressed_size))\r
+               {\r
+                       errormsg("Inflating compressed data", ERROR_CORRUPT, zip->zip);\r
+                       return -3;\r
+               }\r
+\r
+               return 0;\r
+       } else {\r
+               errormsg("Compression method unsupported", ERROR_UNSUPPORTED, zip->zip);\r
+               return -2;\r
+       }\r
+}\r