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