1 /*
2 * unzip.c
3 * Basic unzip interface
4 *
5 * Created on: 1 janv. 2010
6 * Author: Mathieu Peponas
7 */
8
9 #ifdef HAVE_CONFIG_H
10 #include "config.h"
11 #endif
12
13 #if defined(HAVE_LIBZ) && defined (HAVE_MMAP)
14 #include <zlib.h>
15 //#define ZLIB_IN_CHUNK 128*1024
16 #include <unistd.h>
17 #include <sys/mman.h>
18 #include <assert.h>
19 #else
20 #include "stb_zlib.h"
21 #endif
22
23 #include <stdarg.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27 #include "unzip.h"
28
fget8(FILE * f)29 static int fget8(FILE *f) {
30 if (f) {
31 int c = fgetc(f);
32 return c == EOF ? 0 : c;
33 }
34 return 0;
35 }
fget8u(FILE * f)36 static uint8_t fget8u(FILE *f) {
37 return (uint8_t) fget8(f);
38 }
fskip(FILE * f,int n)39 static void fskip(FILE *f, int n) {
40 uint8_t a, i;
41 size_t tr;
42 if (!f)
43 return;//fseek(f,n,SEEK_CUR);
44 for (i = 0; i < n; i++)
45 tr=fread(&a, 1, 1, f);
46 }
fget16(FILE * f)47 static uint16_t fget16(FILE *f) {
48 int z = fget8(f);
49 return (z << 8) + fget8(f);
50 }
51
fget32(FILE * f)52 static uint32_t fget32(FILE *f) {
53 uint32_t z = fget16(f);
54 return (z << 16) + fget16(f);
55 }
56
fget16le(FILE * f)57 static uint16_t fget16le(FILE *f) {
58 int z = fget8(f);
59 return z + (fget8(f) << 8);
60 }
61
fget32le(FILE * f)62 static uint32_t fget32le(FILE *f) {
63 uint32_t z = fget16le(f);
64 return z + (fget16le(f) << 16);
65 }
66
freadvle(FILE * f,char * fmt,...)67 static void freadvle(FILE *f, char *fmt, ...) {
68 va_list v;
69 va_start(v,fmt);
70 //printf("%d\n",sizeof(uint16_t));
71 while (*fmt) {
72 switch (*fmt++) {
73 case ' ':
74 break;
75 case '1': {
76 uint8_t *x = (uint8_t*) va_arg(v, int*);
77 //printf("%p\n",x);
78 if (x)
79 (*x) = fget8(f);
80 else
81 fskip(f, 1);
82 break;
83 }
84 case '2': {
85 uint16_t *x = (uint16_t*) va_arg(v, int*);
86 //printf("%p\n",x);
87 if (x)
88 (*x) = fget16le(f);
89 else
90 fskip(f, 2);
91 break;
92 }
93 case '4': {
94 uint32_t *x = (uint32_t*) va_arg(v, int*);
95 //printf("%p\n",x);
96 if (x)
97 (*x) = fget32le(f);
98 else
99 fskip(f, 4);
100 break;
101 }
102 default:
103 va_end(v);
104 return;
105 }
106 }
107 va_end(v);
108 }
109
search_sig32(PKZIP * zf,uint8_t sig[4])110 static int search_sig32(PKZIP *zf, uint8_t sig[4]) {
111 int i = 0, pos;
112 uint8_t a;
113 int t = 0;
114 size_t tr=0;
115
116 if (!zf || !zf->file)
117 return -1;
118 //pos=ftell(zf->file);
119 while (!feof(zf->file)) {
120 tr+=fread(&a, sizeof(char), 1, zf->file);
121 //printf("Search sig %d\n",a);
122 if (sig[i] == a) {
123 //printf("%02x %d\n",a,t);
124 if (i == 3) {
125 //fseek(zf->file,-4,SEEK_CUR);
126 pos = ftell(zf->file) - 4;
127 return pos;
128 break;
129 }
130 i++;
131 } else
132 i = 0;
133 t++;
134 }
135 return -1;
136 }
137
search_central_dir(PKZIP * zf)138 static int search_central_dir(PKZIP *zf) {
139 int i = 0;
140 unsigned char sig_cde[4] = { 0x50, 0x4b, 0x05, 0x06 };
141 unsigned char sig_cd[4] = { 0x50, 0x4b, 0x01, 0x02 };
142 uint16_t nbdsk;
143
144 if (!zf || !zf->file)
145 return -1;
146 zf->cde_offset = 0;
147 for (i = 0; i < 0xFFFF; i += 0x100) {
148 fseek(zf->file, -i - 22, SEEK_END);
149 /* Max comment size + size of [end of central dir record ] */
150 //printf("search ecd iteration %d\n", i);
151 zf->cde_offset = search_sig32(zf, sig_cde);
152
153 if (zf->cde_offset != -1) {
154 break;
155 }
156 }
157 if (zf->cde_offset == -1) {
158 printf("Couldn't find central dir, Corrupted zip\n");
159 return -1;
160 }
161 //printf("End of central dir here %08x\n", zf->cde_offset);
162 //freadvle(zf->file,"4 22 22 44",0,&nbdsk,0,0,&zf->nb_item,&zf->cd_size,&zf->cd_offset);
163 freadvle(zf->file, "22 22 44", &nbdsk, 0, 0, &zf->nb_item, &zf->cd_size,
164 &zf->cd_offset);
165 if (nbdsk != 0) {
166 printf("Multi disk not supported (%d)\n", nbdsk);
167 return -1;
168 }
169 /*fskip(zf->file,12);
170 zf->cd_size=fget32le(zf->file);
171 zf->cd_offset=fget32le(zf->file);*/
172 //printf("CD off=%08x CD size=%08x\n", zf->cd_offset, zf->cd_size);
173 /* check sig */
174 fseek(zf->file, zf->cd_offset, SEEK_SET);
175 if (search_sig32(zf, sig_cd) != zf->cd_offset) {
176 printf("Corrupted zip\n");
177 return -1;
178 }
179 //printf("ZIP seems ok\n");
180 return 0;
181 }
182
unzip_locate_file(PKZIP * zf,char * filename,uint32_t file_crc)183 static int unzip_locate_file(PKZIP *zf, char *filename, uint32_t file_crc) {
184 int pos;
185 uint32_t crc, offset;
186 uint16_t xf_len, fcomment_len, fname_len;
187 uint32_t sig;
188 char *fname = NULL;
189 size_t tr=0;
190
191 fseek(zf->file, zf->cd_offset, SEEK_SET);
192 pos = ftell(zf->file);
193 if (file_crc == 0)
194 file_crc = (uint32_t) -1; /* because crc=0=dir */
195 while (1) {
196 sig = fget32le(zf->file);
197 //printf("SIG=%08x\n",sig);
198 if (sig == 0x02014b50) { /* File header */
199 freadvle(zf->file, "222222 4 44 222 224 4 ", NULL, NULL, NULL,
200 NULL, NULL, NULL, &crc, NULL, NULL, &fname_len, &xf_len,
201 &fcomment_len, NULL, NULL, NULL, &offset);
202 if (fname)
203 free(fname);
204 fname = calloc(fname_len + 1, sizeof(unsigned char));
205 tr+=fread(fname, fname_len, 1, zf->file);
206 fskip(zf->file, xf_len + fcomment_len);
207 //printf("0x%08x %s=%s?\n",crc,fname,filename);
208 if ((strcmp(fname, filename) == 0 && strlen(fname) == strlen(
209 filename)) || crc == file_crc) {
210 //printf("Found 0x%08x %s\n", crc, fname);
211 free(fname);
212 fseek(zf->file, offset, SEEK_SET);
213 return 0;
214 }
215 } else
216 break;
217 }
218 if (fname)
219 free(fname);
220 return -1;
221 }
222
gn_unzip_fopen(PKZIP * zf,char * filename,uint32_t file_crc)223 ZFILE *gn_unzip_fopen(PKZIP *zf, char *filename, uint32_t file_crc) {
224 ZFILE *z;
225 uint32_t sig;
226 int cmeth, xf_len, fname_len;
227 int csize, uncsize;
228
229 if (unzip_locate_file(zf, filename, file_crc) == 0) {
230 sig = fget32le(zf->file);
231 //printf("SIG=%08x\n", sig);
232 if (sig != 0x04034b50) {
233 printf("Error\n");
234 return NULL;
235 }
236 fskip(zf->file, 2 + 2);
237 cmeth = fget16le(zf->file);
238 if (cmeth != 0 && cmeth != 8) {
239 printf("Error: Unsupported compression method\n");
240 return NULL;
241 }
242 fskip(zf->file, 2 + 2 + 4);
243 csize = fget32le(zf->file);
244 uncsize = fget32le(zf->file);
245
246 fname_len = fget16le(zf->file);
247 xf_len = fget16le(zf->file);
248
249 fskip(zf->file, xf_len + fname_len);
250
251 //printf("compressed size %d uncompressed size %d method=%d\n", csize,uncsize, cmeth);
252
253 z = malloc(sizeof(ZFILE));
254 if (!z)
255 return NULL;
256 //z->name=fname;
257 z->pos = ftell(zf->file);
258 z->csize = csize;
259 z->uncsize = uncsize;
260 if (cmeth == 8) {
261 #if defined(HAVE_LIBZ) && defined (HAVE_MMAP)
262 z->zb = malloc(sizeof(z_stream));
263 z->inbuf = zf->map+z->pos;
264 //printf("inbuf=%p %d\n",z->inbuf,fileno(zf->file));
265 //perror("ERROR:");
266 memset(z->zb, 0, sizeof(z_stream));
267
268 z->zb->avail_in=csize;
269 z->zb->next_in=z->inbuf;
270 inflateInit2(z->zb, -MAX_WBITS);
271 #else
272 z->zb=stbi_zlib_create_zbuf(NULL,zf->file,csize);
273 #endif
274 }
275 z->cmeth = cmeth;
276 z->readed = 0;
277 z->f = zf->file;
278 return z;
279 }
280
281 return NULL;
282
283 }
284
gn_unzip_fread(ZFILE * z,uint8_t * data,unsigned int size)285 int gn_unzip_fread(ZFILE *z, uint8_t *data, unsigned int size) {
286 int readed;
287 int todo;
288 int ret;
289 size_t tr=0;
290 if (!z)
291 return -1;
292 //if (z->pos!=ftell(z->f))
293 //fseek(z->f,z->pos,SEEK_SET);
294 if (z->cmeth == 8) {
295 #if defined(HAVE_LIBZ) && defined (HAVE_MMAP)
296 z->zb->next_out = data;
297 z->zb->avail_out = size;
298 ret = inflate(z->zb, Z_NO_FLUSH);
299 //printf("ret=%d\n",ret);
300 assert(ret != Z_STREAM_ERROR); /* state not clobbered */
301 switch (ret) {
302 case Z_NEED_DICT:
303 ret = Z_DATA_ERROR; /* and fall through */
304 case Z_DATA_ERROR:
305 case Z_MEM_ERROR:
306 (void) inflateEnd(z->zb);
307 return ret;
308 }
309 readed = size - z->zb->avail_out;
310 #else
311 readed=stbi_zlib_decode_noheader_stream(z->zb,data,size);
312 #endif
313 } else { /* Stored */
314 todo = z->readed - z->uncsize;
315 if (todo < size)
316 todo = size;
317
318 readed = fread(data, 1, size, z->f);
319 z->readed += readed;
320 }
321
322 z->pos = ftell(z->f);
323 return readed;
324 }
325
gn_unzip_file_malloc(PKZIP * zf,char * filename,uint32_t file_crc,unsigned int * outlen)326 uint8_t *gn_unzip_file_malloc(PKZIP *zf, char *filename, uint32_t file_crc,
327 unsigned int *outlen) {
328 ZFILE *z = gn_unzip_fopen(zf, filename, file_crc);
329 int readed;
330 if (!z)
331 return NULL;
332 uint8_t *data = malloc(z->uncsize);
333 if (!data)
334 return NULL;
335 if (z->cmeth == 8) {
336 #if !defined(HAVE_LIBZ) || !defined (HAVE_MMAP)
337 readed = stbi_zlib_decode_noheader_stream(z->zb, data, z->uncsize);
338 #else
339 readed=gn_unzip_fread(z,data,z->uncsize);
340 printf("Readed=%d \n",readed);
341 #endif
342 } else
343 readed = fread(data, 1, z->uncsize, z->f);
344 if (readed != z->uncsize)
345 printf(
346 "GNZIP: Readed data size different from uncompressed size %d!=%d \n",
347 readed, z->uncsize);
348 *outlen = z->uncsize;
349 gn_unzip_fclose(z);
350 return data;
351 }
352
gn_unzip_fclose(ZFILE * z)353 void gn_unzip_fclose(ZFILE *z) {
354 if (!z)
355 return;
356 #if defined(HAVE_LIBZ) && defined (HAVE_MMAP)
357 if (z->cmeth==8) {
358 inflateEnd(z->zb);
359 free(z->zb);
360 }
361 #else
362 if (z->cmeth==8) free(z->zb->cbuf);
363 #endif
364 free(z);
365 }
366
gn_open_zip(char * file)367 PKZIP *gn_open_zip(char *file) {
368 PKZIP *zf = malloc(sizeof(PKZIP));
369 int size;
370 int e;
371 zf->file = fopen(file, "rb");
372 if (zf->file == NULL) {
373 printf("Couldn't open %s\n", file);
374 free(zf);
375 return NULL;
376 }
377 #if defined(HAVE_LIBZ) && defined (HAVE_MMAP)
378 fseek(zf->file,0,SEEK_END);
379 size=ftell(zf->file);
380 zf->map=mmap(0,size,PROT_READ,MAP_SHARED,fileno(zf->file),0);
381
382 #endif
383 e = search_central_dir(zf);
384 if (e) {
385 printf("Strange %d %s\n", e, file);
386 fclose(zf->file);
387 free(zf);
388 return NULL;
389 }
390 return zf;
391 }
gn_close_zip(PKZIP * zf)392 void gn_close_zip(PKZIP *zf) {
393 fclose(zf->file);
394 free(zf);
395 }
396
397