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