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