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