1275793eaSopenharmony_ci/*
2275793eaSopenharmony_ci  Additional tools for Minizip
3275793eaSopenharmony_ci  Code: Xavier Roche '2004
4275793eaSopenharmony_ci  License: Same as ZLIB (www.gzip.org)
5275793eaSopenharmony_ci*/
6275793eaSopenharmony_ci
7275793eaSopenharmony_ci/* Code */
8275793eaSopenharmony_ci#include <stdio.h>
9275793eaSopenharmony_ci#include <stdlib.h>
10275793eaSopenharmony_ci#include <string.h>
11275793eaSopenharmony_ci#include "zlib.h"
12275793eaSopenharmony_ci#include "unzip.h"
13275793eaSopenharmony_ci
14275793eaSopenharmony_ci#define READ_8(adr)  ((unsigned char)*(adr))
15275793eaSopenharmony_ci#define READ_16(adr) ( READ_8(adr) | (READ_8(adr+1) << 8) )
16275793eaSopenharmony_ci#define READ_32(adr) ( READ_16(adr) | (READ_16((adr)+2) << 16) )
17275793eaSopenharmony_ci
18275793eaSopenharmony_ci#define WRITE_8(buff, n) do { \
19275793eaSopenharmony_ci  *((unsigned char*)(buff)) = (unsigned char) ((n) & 0xff); \
20275793eaSopenharmony_ci} while(0)
21275793eaSopenharmony_ci#define WRITE_16(buff, n) do { \
22275793eaSopenharmony_ci  WRITE_8((unsigned char*)(buff), n); \
23275793eaSopenharmony_ci  WRITE_8(((unsigned char*)(buff)) + 1, (n) >> 8); \
24275793eaSopenharmony_ci} while(0)
25275793eaSopenharmony_ci#define WRITE_32(buff, n) do { \
26275793eaSopenharmony_ci  WRITE_16((unsigned char*)(buff), (n) & 0xffff); \
27275793eaSopenharmony_ci  WRITE_16((unsigned char*)(buff) + 2, (n) >> 16); \
28275793eaSopenharmony_ci} while(0)
29275793eaSopenharmony_ci
30275793eaSopenharmony_ciextern int ZEXPORT unzRepair(const char* file,
31275793eaSopenharmony_ci                             const char* fileOut,
32275793eaSopenharmony_ci                             const char* fileOutTmp,
33275793eaSopenharmony_ci                             uLong* nRecovered,
34275793eaSopenharmony_ci                             uLong* bytesRecovered)
35275793eaSopenharmony_ci{
36275793eaSopenharmony_ci  int err = Z_OK;
37275793eaSopenharmony_ci  FILE* fpZip = fopen(file, "rb");
38275793eaSopenharmony_ci  FILE* fpOut = fopen(fileOut, "wb");
39275793eaSopenharmony_ci  FILE* fpOutCD = fopen(fileOutTmp, "wb");
40275793eaSopenharmony_ci  if (fpZip != NULL &&  fpOut != NULL) {
41275793eaSopenharmony_ci    int entries = 0;
42275793eaSopenharmony_ci    uLong totalBytes = 0;
43275793eaSopenharmony_ci    char header[30];
44275793eaSopenharmony_ci    char filename[1024];
45275793eaSopenharmony_ci    char extra[1024];
46275793eaSopenharmony_ci    int offset = 0;
47275793eaSopenharmony_ci    int offsetCD = 0;
48275793eaSopenharmony_ci    while ( fread(header, 1, 30, fpZip) == 30 ) {
49275793eaSopenharmony_ci      int currentOffset = offset;
50275793eaSopenharmony_ci
51275793eaSopenharmony_ci      /* File entry */
52275793eaSopenharmony_ci      if (READ_32(header) == 0x04034b50) {
53275793eaSopenharmony_ci        unsigned int version = READ_16(header + 4);
54275793eaSopenharmony_ci        unsigned int gpflag = READ_16(header + 6);
55275793eaSopenharmony_ci        unsigned int method = READ_16(header + 8);
56275793eaSopenharmony_ci        unsigned int filetime = READ_16(header + 10);
57275793eaSopenharmony_ci        unsigned int filedate = READ_16(header + 12);
58275793eaSopenharmony_ci        unsigned int crc = READ_32(header + 14); /* crc */
59275793eaSopenharmony_ci        unsigned int cpsize = READ_32(header + 18); /* compressed size */
60275793eaSopenharmony_ci        unsigned int uncpsize = READ_32(header + 22); /* uncompressed sz */
61275793eaSopenharmony_ci        unsigned int fnsize = READ_16(header + 26); /* file name length */
62275793eaSopenharmony_ci        unsigned int extsize = READ_16(header + 28); /* extra field length */
63275793eaSopenharmony_ci        filename[0] = extra[0] = '\0';
64275793eaSopenharmony_ci
65275793eaSopenharmony_ci        /* Header */
66275793eaSopenharmony_ci        if (fwrite(header, 1, 30, fpOut) == 30) {
67275793eaSopenharmony_ci          offset += 30;
68275793eaSopenharmony_ci        } else {
69275793eaSopenharmony_ci          err = Z_ERRNO;
70275793eaSopenharmony_ci          break;
71275793eaSopenharmony_ci        }
72275793eaSopenharmony_ci
73275793eaSopenharmony_ci        /* Filename */
74275793eaSopenharmony_ci        if (fnsize > 0) {
75275793eaSopenharmony_ci          if (fnsize < sizeof(filename)) {
76275793eaSopenharmony_ci            if (fread(filename, 1, fnsize, fpZip) == fnsize) {
77275793eaSopenharmony_ci                if (fwrite(filename, 1, fnsize, fpOut) == fnsize) {
78275793eaSopenharmony_ci                offset += fnsize;
79275793eaSopenharmony_ci              } else {
80275793eaSopenharmony_ci                err = Z_ERRNO;
81275793eaSopenharmony_ci                break;
82275793eaSopenharmony_ci              }
83275793eaSopenharmony_ci            } else {
84275793eaSopenharmony_ci              err = Z_ERRNO;
85275793eaSopenharmony_ci              break;
86275793eaSopenharmony_ci            }
87275793eaSopenharmony_ci          } else {
88275793eaSopenharmony_ci            err = Z_ERRNO;
89275793eaSopenharmony_ci            break;
90275793eaSopenharmony_ci          }
91275793eaSopenharmony_ci        } else {
92275793eaSopenharmony_ci          err = Z_STREAM_ERROR;
93275793eaSopenharmony_ci          break;
94275793eaSopenharmony_ci        }
95275793eaSopenharmony_ci
96275793eaSopenharmony_ci        /* Extra field */
97275793eaSopenharmony_ci        if (extsize > 0) {
98275793eaSopenharmony_ci          if (extsize < sizeof(extra)) {
99275793eaSopenharmony_ci            if (fread(extra, 1, extsize, fpZip) == extsize) {
100275793eaSopenharmony_ci              if (fwrite(extra, 1, extsize, fpOut) == extsize) {
101275793eaSopenharmony_ci                offset += extsize;
102275793eaSopenharmony_ci                } else {
103275793eaSopenharmony_ci                err = Z_ERRNO;
104275793eaSopenharmony_ci                break;
105275793eaSopenharmony_ci              }
106275793eaSopenharmony_ci            } else {
107275793eaSopenharmony_ci              err = Z_ERRNO;
108275793eaSopenharmony_ci              break;
109275793eaSopenharmony_ci            }
110275793eaSopenharmony_ci          } else {
111275793eaSopenharmony_ci            err = Z_ERRNO;
112275793eaSopenharmony_ci            break;
113275793eaSopenharmony_ci          }
114275793eaSopenharmony_ci        }
115275793eaSopenharmony_ci
116275793eaSopenharmony_ci        /* Data */
117275793eaSopenharmony_ci        {
118275793eaSopenharmony_ci          int dataSize = cpsize;
119275793eaSopenharmony_ci          if (dataSize == 0) {
120275793eaSopenharmony_ci            dataSize = uncpsize;
121275793eaSopenharmony_ci          }
122275793eaSopenharmony_ci          if (dataSize > 0) {
123275793eaSopenharmony_ci            char* data = malloc(dataSize);
124275793eaSopenharmony_ci            if (data != NULL) {
125275793eaSopenharmony_ci              if ((int)fread(data, 1, dataSize, fpZip) == dataSize) {
126275793eaSopenharmony_ci                if ((int)fwrite(data, 1, dataSize, fpOut) == dataSize) {
127275793eaSopenharmony_ci                  offset += dataSize;
128275793eaSopenharmony_ci                  totalBytes += dataSize;
129275793eaSopenharmony_ci                } else {
130275793eaSopenharmony_ci                  err = Z_ERRNO;
131275793eaSopenharmony_ci                }
132275793eaSopenharmony_ci              } else {
133275793eaSopenharmony_ci                err = Z_ERRNO;
134275793eaSopenharmony_ci              }
135275793eaSopenharmony_ci              free(data);
136275793eaSopenharmony_ci              if (err != Z_OK) {
137275793eaSopenharmony_ci                break;
138275793eaSopenharmony_ci              }
139275793eaSopenharmony_ci            } else {
140275793eaSopenharmony_ci              err = Z_MEM_ERROR;
141275793eaSopenharmony_ci              break;
142275793eaSopenharmony_ci            }
143275793eaSopenharmony_ci          }
144275793eaSopenharmony_ci        }
145275793eaSopenharmony_ci
146275793eaSopenharmony_ci        /* Central directory entry */
147275793eaSopenharmony_ci        {
148275793eaSopenharmony_ci          char header[46];
149275793eaSopenharmony_ci          char* comment = "";
150275793eaSopenharmony_ci          int comsize = (int) strlen(comment);
151275793eaSopenharmony_ci          WRITE_32(header, 0x02014b50);
152275793eaSopenharmony_ci          WRITE_16(header + 4, version);
153275793eaSopenharmony_ci          WRITE_16(header + 6, version);
154275793eaSopenharmony_ci          WRITE_16(header + 8, gpflag);
155275793eaSopenharmony_ci          WRITE_16(header + 10, method);
156275793eaSopenharmony_ci          WRITE_16(header + 12, filetime);
157275793eaSopenharmony_ci          WRITE_16(header + 14, filedate);
158275793eaSopenharmony_ci          WRITE_32(header + 16, crc);
159275793eaSopenharmony_ci          WRITE_32(header + 20, cpsize);
160275793eaSopenharmony_ci          WRITE_32(header + 24, uncpsize);
161275793eaSopenharmony_ci          WRITE_16(header + 28, fnsize);
162275793eaSopenharmony_ci          WRITE_16(header + 30, extsize);
163275793eaSopenharmony_ci          WRITE_16(header + 32, comsize);
164275793eaSopenharmony_ci          WRITE_16(header + 34, 0);     /* disk # */
165275793eaSopenharmony_ci          WRITE_16(header + 36, 0);     /* int attrb */
166275793eaSopenharmony_ci          WRITE_32(header + 38, 0);     /* ext attrb */
167275793eaSopenharmony_ci          WRITE_32(header + 42, currentOffset);
168275793eaSopenharmony_ci          /* Header */
169275793eaSopenharmony_ci          if (fwrite(header, 1, 46, fpOutCD) == 46) {
170275793eaSopenharmony_ci            offsetCD += 46;
171275793eaSopenharmony_ci
172275793eaSopenharmony_ci            /* Filename */
173275793eaSopenharmony_ci            if (fnsize > 0) {
174275793eaSopenharmony_ci              if (fwrite(filename, 1, fnsize, fpOutCD) == fnsize) {
175275793eaSopenharmony_ci                offsetCD += fnsize;
176275793eaSopenharmony_ci              } else {
177275793eaSopenharmony_ci                err = Z_ERRNO;
178275793eaSopenharmony_ci                break;
179275793eaSopenharmony_ci              }
180275793eaSopenharmony_ci            } else {
181275793eaSopenharmony_ci              err = Z_STREAM_ERROR;
182275793eaSopenharmony_ci              break;
183275793eaSopenharmony_ci            }
184275793eaSopenharmony_ci
185275793eaSopenharmony_ci            /* Extra field */
186275793eaSopenharmony_ci            if (extsize > 0) {
187275793eaSopenharmony_ci              if (fwrite(extra, 1, extsize, fpOutCD) == extsize) {
188275793eaSopenharmony_ci                offsetCD += extsize;
189275793eaSopenharmony_ci              } else {
190275793eaSopenharmony_ci                err = Z_ERRNO;
191275793eaSopenharmony_ci                break;
192275793eaSopenharmony_ci              }
193275793eaSopenharmony_ci            }
194275793eaSopenharmony_ci
195275793eaSopenharmony_ci            /* Comment field */
196275793eaSopenharmony_ci            if (comsize > 0) {
197275793eaSopenharmony_ci              if ((int)fwrite(comment, 1, comsize, fpOutCD) == comsize) {
198275793eaSopenharmony_ci                offsetCD += comsize;
199275793eaSopenharmony_ci              } else {
200275793eaSopenharmony_ci                err = Z_ERRNO;
201275793eaSopenharmony_ci                break;
202275793eaSopenharmony_ci              }
203275793eaSopenharmony_ci            }
204275793eaSopenharmony_ci
205275793eaSopenharmony_ci
206275793eaSopenharmony_ci          } else {
207275793eaSopenharmony_ci            err = Z_ERRNO;
208275793eaSopenharmony_ci            break;
209275793eaSopenharmony_ci          }
210275793eaSopenharmony_ci        }
211275793eaSopenharmony_ci
212275793eaSopenharmony_ci        /* Success */
213275793eaSopenharmony_ci        entries++;
214275793eaSopenharmony_ci
215275793eaSopenharmony_ci      } else {
216275793eaSopenharmony_ci        break;
217275793eaSopenharmony_ci      }
218275793eaSopenharmony_ci    }
219275793eaSopenharmony_ci
220275793eaSopenharmony_ci    /* Final central directory  */
221275793eaSopenharmony_ci    {
222275793eaSopenharmony_ci      int entriesZip = entries;
223275793eaSopenharmony_ci      char header[22];
224275793eaSopenharmony_ci      char* comment = ""; // "ZIP File recovered by zlib/minizip/mztools";
225275793eaSopenharmony_ci      int comsize = (int) strlen(comment);
226275793eaSopenharmony_ci      if (entriesZip > 0xffff) {
227275793eaSopenharmony_ci        entriesZip = 0xffff;
228275793eaSopenharmony_ci      }
229275793eaSopenharmony_ci      WRITE_32(header, 0x06054b50);
230275793eaSopenharmony_ci      WRITE_16(header + 4, 0);    /* disk # */
231275793eaSopenharmony_ci      WRITE_16(header + 6, 0);    /* disk # */
232275793eaSopenharmony_ci      WRITE_16(header + 8, entriesZip);   /* hack */
233275793eaSopenharmony_ci      WRITE_16(header + 10, entriesZip);  /* hack */
234275793eaSopenharmony_ci      WRITE_32(header + 12, offsetCD);    /* size of CD */
235275793eaSopenharmony_ci      WRITE_32(header + 16, offset);      /* offset to CD */
236275793eaSopenharmony_ci      WRITE_16(header + 20, comsize);     /* comment */
237275793eaSopenharmony_ci
238275793eaSopenharmony_ci      /* Header */
239275793eaSopenharmony_ci      if (fwrite(header, 1, 22, fpOutCD) == 22) {
240275793eaSopenharmony_ci
241275793eaSopenharmony_ci        /* Comment field */
242275793eaSopenharmony_ci        if (comsize > 0) {
243275793eaSopenharmony_ci          if ((int)fwrite(comment, 1, comsize, fpOutCD) != comsize) {
244275793eaSopenharmony_ci            err = Z_ERRNO;
245275793eaSopenharmony_ci          }
246275793eaSopenharmony_ci        }
247275793eaSopenharmony_ci
248275793eaSopenharmony_ci      } else {
249275793eaSopenharmony_ci        err = Z_ERRNO;
250275793eaSopenharmony_ci      }
251275793eaSopenharmony_ci    }
252275793eaSopenharmony_ci
253275793eaSopenharmony_ci    /* Final merge (file + central directory) */
254275793eaSopenharmony_ci    fclose(fpOutCD);
255275793eaSopenharmony_ci    if (err == Z_OK) {
256275793eaSopenharmony_ci      fpOutCD = fopen(fileOutTmp, "rb");
257275793eaSopenharmony_ci      if (fpOutCD != NULL) {
258275793eaSopenharmony_ci        int nRead;
259275793eaSopenharmony_ci        char buffer[8192];
260275793eaSopenharmony_ci        while ( (nRead = (int)fread(buffer, 1, sizeof(buffer), fpOutCD)) > 0) {
261275793eaSopenharmony_ci          if ((int)fwrite(buffer, 1, nRead, fpOut) != nRead) {
262275793eaSopenharmony_ci            err = Z_ERRNO;
263275793eaSopenharmony_ci            break;
264275793eaSopenharmony_ci          }
265275793eaSopenharmony_ci        }
266275793eaSopenharmony_ci        fclose(fpOutCD);
267275793eaSopenharmony_ci      }
268275793eaSopenharmony_ci    }
269275793eaSopenharmony_ci
270275793eaSopenharmony_ci    /* Close */
271275793eaSopenharmony_ci    fclose(fpZip);
272275793eaSopenharmony_ci    fclose(fpOut);
273275793eaSopenharmony_ci
274275793eaSopenharmony_ci    /* Wipe temporary file */
275275793eaSopenharmony_ci    (void)remove(fileOutTmp);
276275793eaSopenharmony_ci
277275793eaSopenharmony_ci    /* Number of recovered entries */
278275793eaSopenharmony_ci    if (err == Z_OK) {
279275793eaSopenharmony_ci      if (nRecovered != NULL) {
280275793eaSopenharmony_ci        *nRecovered = entries;
281275793eaSopenharmony_ci      }
282275793eaSopenharmony_ci      if (bytesRecovered != NULL) {
283275793eaSopenharmony_ci        *bytesRecovered = totalBytes;
284275793eaSopenharmony_ci      }
285275793eaSopenharmony_ci    }
286275793eaSopenharmony_ci  } else {
287275793eaSopenharmony_ci    err = Z_STREAM_ERROR;
288275793eaSopenharmony_ci  }
289275793eaSopenharmony_ci  return err;
290275793eaSopenharmony_ci}
291