Home | History | Annotate | Line # | Download | only in minizip
      1 /*
      2   Additional tools for Minizip
      3   Code: Xavier Roche '2004
      4   License: Same as ZLIB (www.gzip.org)
      5 */
      6 
      7 /* Code */
      8 #include <stdio.h>
      9 #include <stdlib.h>
     10 #include <string.h>
     11 #include "zlib.h"
     12 #include "unzip.h"
     13 
     14 #define READ_8(adr)  ((unsigned char)*(adr))
     15 #define READ_16(adr) ( READ_8(adr) | (READ_8(adr+1) << 8) )
     16 #define READ_32(adr) ( READ_16(adr) | (READ_16((adr)+2) << 16) )
     17 
     18 #define WRITE_8(buff, n) do { \
     19   *((unsigned char*)(buff)) = (unsigned char) ((n) & 0xff); \
     20 } while(0)
     21 #define WRITE_16(buff, n) do { \
     22   WRITE_8((unsigned char*)(buff), n); \
     23   WRITE_8(((unsigned char*)(buff)) + 1, (n) >> 8); \
     24 } while(0)
     25 #define WRITE_32(buff, n) do { \
     26   WRITE_16((unsigned char*)(buff), (n) & 0xffff); \
     27   WRITE_16((unsigned char*)(buff) + 2, (n) >> 16); \
     28 } while(0)
     29 
     30 extern int ZEXPORT unzRepair(const char* file, const char* fileOut, const char* fileOutTmp, uLong* nRecovered, uLong* bytesRecovered) {
     31   int err = Z_OK;
     32   FILE* fpZip = fopen(file, "rb");
     33   FILE* fpOut = fopen(fileOut, "wb");
     34   FILE* fpOutCD = fopen(fileOutTmp, "wb");
     35   if (fpZip != NULL &&  fpOut != NULL) {
     36     int entries = 0;
     37     uLong totalBytes = 0;
     38     char header[30];
     39     char filename[1024];
     40     char extra[1024];
     41     int offset = 0;
     42     int offsetCD = 0;
     43     while ( fread(header, 1, 30, fpZip) == 30 ) {
     44       int currentOffset = offset;
     45 
     46       /* File entry */
     47       if (READ_32(header) == 0x04034b50) {
     48         unsigned int version = READ_16(header + 4);
     49         unsigned int gpflag = READ_16(header + 6);
     50         unsigned int method = READ_16(header + 8);
     51         unsigned int filetime = READ_16(header + 10);
     52         unsigned int filedate = READ_16(header + 12);
     53         unsigned int crc = READ_32(header + 14); /* crc */
     54         unsigned int cpsize = READ_32(header + 18); /* compressed size */
     55         unsigned int uncpsize = READ_32(header + 22); /* uncompressed sz */
     56         unsigned int fnsize = READ_16(header + 26); /* file name length */
     57         unsigned int extsize = READ_16(header + 28); /* extra field length */
     58         filename[0] = extra[0] = '\0';
     59 
     60         /* Header */
     61         if (fwrite(header, 1, 30, fpOut) == 30) {
     62           offset += 30;
     63         } else {
     64           err = Z_ERRNO;
     65           break;
     66         }
     67 
     68         /* Filename */
     69         if (fnsize > 0) {
     70           if (fnsize < sizeof(filename)) {
     71             if (fread(filename, 1, fnsize, fpZip) == fnsize) {
     72                 if (fwrite(filename, 1, fnsize, fpOut) == fnsize) {
     73                 offset += fnsize;
     74               } else {
     75                 err = Z_ERRNO;
     76                 break;
     77               }
     78             } else {
     79               err = Z_ERRNO;
     80               break;
     81             }
     82           } else {
     83             err = Z_ERRNO;
     84             break;
     85           }
     86         } else {
     87           err = Z_STREAM_ERROR;
     88           break;
     89         }
     90 
     91         /* Extra field */
     92         if (extsize > 0) {
     93           if (extsize < sizeof(extra)) {
     94             if (fread(extra, 1, extsize, fpZip) == extsize) {
     95               if (fwrite(extra, 1, extsize, fpOut) == extsize) {
     96                 offset += extsize;
     97                 } else {
     98                 err = Z_ERRNO;
     99                 break;
    100               }
    101             } else {
    102               err = Z_ERRNO;
    103               break;
    104             }
    105           } else {
    106             err = Z_ERRNO;
    107             break;
    108           }
    109         }
    110 
    111         /* Data */
    112         {
    113           int dataSize = cpsize;
    114           if (dataSize == 0) {
    115             dataSize = uncpsize;
    116           }
    117           if (dataSize > 0) {
    118             char* data = malloc(dataSize);
    119             if (data != NULL) {
    120               if ((int)fread(data, 1, dataSize, fpZip) == dataSize) {
    121                 if ((int)fwrite(data, 1, dataSize, fpOut) == dataSize) {
    122                   offset += dataSize;
    123                   totalBytes += dataSize;
    124                 } else {
    125                   err = Z_ERRNO;
    126                 }
    127               } else {
    128                 err = Z_ERRNO;
    129               }
    130               free(data);
    131               if (err != Z_OK) {
    132                 break;
    133               }
    134             } else {
    135               err = Z_MEM_ERROR;
    136               break;
    137             }
    138           }
    139         }
    140 
    141         /* Central directory entry */
    142         {
    143           char header[46];
    144           char* comment = "";
    145           int comsize = (int) strlen(comment);
    146           WRITE_32(header, 0x02014b50);
    147           WRITE_16(header + 4, version);
    148           WRITE_16(header + 6, version);
    149           WRITE_16(header + 8, gpflag);
    150           WRITE_16(header + 10, method);
    151           WRITE_16(header + 12, filetime);
    152           WRITE_16(header + 14, filedate);
    153           WRITE_32(header + 16, crc);
    154           WRITE_32(header + 20, cpsize);
    155           WRITE_32(header + 24, uncpsize);
    156           WRITE_16(header + 28, fnsize);
    157           WRITE_16(header + 30, extsize);
    158           WRITE_16(header + 32, comsize);
    159           WRITE_16(header + 34, 0);     /* disk # */
    160           WRITE_16(header + 36, 0);     /* int attrb */
    161           WRITE_32(header + 38, 0);     /* ext attrb */
    162           WRITE_32(header + 42, currentOffset);
    163           /* Header */
    164           if (fwrite(header, 1, 46, fpOutCD) == 46) {
    165             offsetCD += 46;
    166 
    167             /* Filename */
    168             if (fnsize > 0) {
    169               if (fwrite(filename, 1, fnsize, fpOutCD) == fnsize) {
    170                 offsetCD += fnsize;
    171               } else {
    172                 err = Z_ERRNO;
    173                 break;
    174               }
    175             } else {
    176               err = Z_STREAM_ERROR;
    177               break;
    178             }
    179 
    180             /* Extra field */
    181             if (extsize > 0) {
    182               if (fwrite(extra, 1, extsize, fpOutCD) == extsize) {
    183                 offsetCD += extsize;
    184               } else {
    185                 err = Z_ERRNO;
    186                 break;
    187               }
    188             }
    189 
    190             /* Comment field */
    191             if (comsize > 0) {
    192               if ((int)fwrite(comment, 1, comsize, fpOutCD) == comsize) {
    193                 offsetCD += comsize;
    194               } else {
    195                 err = Z_ERRNO;
    196                 break;
    197               }
    198             }
    199 
    200 
    201           } else {
    202             err = Z_ERRNO;
    203             break;
    204           }
    205         }
    206 
    207         /* Success */
    208         entries++;
    209 
    210       } else {
    211         break;
    212       }
    213     }
    214 
    215     /* Final central directory  */
    216     {
    217       int entriesZip = entries;
    218       char header[22];
    219       char* comment = ""; // "ZIP File recovered by zlib/minizip/mztools";
    220       int comsize = (int) strlen(comment);
    221       if (entriesZip > 0xffff) {
    222         entriesZip = 0xffff;
    223       }
    224       WRITE_32(header, 0x06054b50);
    225       WRITE_16(header + 4, 0);    /* disk # */
    226       WRITE_16(header + 6, 0);    /* disk # */
    227       WRITE_16(header + 8, entriesZip);   /* hack */
    228       WRITE_16(header + 10, entriesZip);  /* hack */
    229       WRITE_32(header + 12, offsetCD);    /* size of CD */
    230       WRITE_32(header + 16, offset);      /* offset to CD */
    231       WRITE_16(header + 20, comsize);     /* comment */
    232 
    233       /* Header */
    234       if (fwrite(header, 1, 22, fpOutCD) == 22) {
    235 
    236         /* Comment field */
    237         if (comsize > 0) {
    238           if ((int)fwrite(comment, 1, comsize, fpOutCD) != comsize) {
    239             err = Z_ERRNO;
    240           }
    241         }
    242 
    243       } else {
    244         err = Z_ERRNO;
    245       }
    246     }
    247 
    248     /* Final merge (file + central directory) */
    249     fclose(fpOutCD);
    250     if (err == Z_OK) {
    251       fpOutCD = fopen(fileOutTmp, "rb");
    252       if (fpOutCD != NULL) {
    253         int nRead;
    254         char buffer[8192];
    255         while ( (nRead = (int)fread(buffer, 1, sizeof(buffer), fpOutCD)) > 0) {
    256           if ((int)fwrite(buffer, 1, nRead, fpOut) != nRead) {
    257             err = Z_ERRNO;
    258             break;
    259           }
    260         }
    261         fclose(fpOutCD);
    262       }
    263     }
    264 
    265     /* Close */
    266     fclose(fpZip);
    267     fclose(fpOut);
    268 
    269     /* Wipe temporary file */
    270     (void)remove(fileOutTmp);
    271 
    272     /* Number of recovered entries */
    273     if (err == Z_OK) {
    274       if (nRecovered != NULL) {
    275         *nRecovered = entries;
    276       }
    277       if (bytesRecovered != NULL) {
    278         *bytesRecovered = totalBytes;
    279       }
    280     }
    281   } else {
    282     err = Z_STREAM_ERROR;
    283   }
    284   return err;
    285 }
    286