|
|
1.1 root 1: /*
2: Hatari - zip.c
3:
4: This file is distributed under the GNU Public License, version 2 or at
5: your option any later version. Read the file gpl.txt for details.
6:
7: Zipped disk support, uses zlib
8: */
9: const char ZIP_fileid[] = "Hatari zip.c : " __DATE__ " " __TIME__;
10:
11: #include <stdio.h>
12: #include <stdlib.h>
13: #include <unistd.h>
14: #include <dirent.h>
15: #include <sys/types.h>
16: #include <zlib.h>
17:
18: #include "main.h"
19: #include "file.h"
20: #include "log.h"
21: #include "str.h"
22: #include "unzip.h"
23: #include "zip.h"
24:
25: #ifdef QNX
26: #include <sys/dir.h>
27: #define dirent direct
28: #endif
29:
30: /* #define SAVE_TO_ZIP_IMAGES */
31:
32: #define ZIP_PATH_MAX 256
33:
34: #define ZIP_FILE_ST 1
35: #define ZIP_FILE_MSA 2
36: #define ZIP_FILE_DIM 3
37:
38:
39: /* Possible disk image extensions to scan for */
40: static const char * const pszDiskNameExts[] =
41: {
42: ".msa",
43: ".st",
44: ".dim",
45: NULL
46: };
47:
48:
49: /*-----------------------------------------------------------------------*/
50: /**
51: * Does filename end with a .ZIP extension? If so, return true.
52: */
53: bool ZIP_FileNameIsZIP(const char *pszFileName)
54: {
55: return File_DoesFileExtensionMatch(pszFileName,".zip");
56: }
57:
58:
59: /*-----------------------------------------------------------------------*/
60: /**
61: * Check if a file name contains a slash or backslash and return its position.
62: */
63: static int Zip_FileNameHasSlash(const char *fn)
64: {
65: int i=0;
66:
67: while (fn[i] != '\0')
68: {
69: if (fn[i] == '\\' || fn[i] == '/')
70: return i;
71: i++;
72: }
73: return -1;
74: }
75:
76:
77: /*-----------------------------------------------------------------------*/
78: /**
79: * Returns a list of files from a zip file. returns NULL on failure,
80: * returns a pointer to an array of strings if successful. Sets nfiles
81: * to the number of files.
82: */
83: zip_dir *ZIP_GetFiles(const char *pszFileName)
84: {
85: int nfiles;
86: unsigned int i;
87: unz_global_info gi;
88: int err;
89: unzFile uf;
90: char **filelist;
91: unz_file_info file_info;
92: char filename_inzip[ZIP_PATH_MAX];
93: zip_dir *zd;
94:
95: uf = unzOpen(pszFileName);
96: if (uf == NULL)
97: {
98: Log_Printf(LOG_ERROR, "ZIP_GetFiles: Cannot open %s\n", pszFileName);
99: return NULL;
100: }
101:
102: err = unzGetGlobalInfo(uf,&gi);
103: if (err != UNZ_OK)
104: {
105: Log_Printf(LOG_ERROR, "Error %d with zipfile in unzGetGlobalInfo \n",err);
106: return NULL;
107: }
108:
109: /* allocate a file list */
110: filelist = (char **)malloc(gi.number_entry*sizeof(char *));
111: if (!filelist)
112: {
113: perror("ZIP_GetFiles");
114: return NULL;
115: }
116:
117: nfiles = gi.number_entry; /* set the number of files */
118:
119: for (i = 0; i < gi.number_entry; i++)
120: {
121: err = unzGetCurrentFileInfo(uf, &file_info, filename_inzip, ZIP_PATH_MAX, NULL, 0, NULL, 0);
122: if (err != UNZ_OK)
123: {
124: free(filelist);
125: return NULL;
126: }
127:
128: filelist[i] = (char *)malloc(strlen(filename_inzip) + 1);
129: if (!filelist[i])
130: {
131: perror("ZIP_GetFiles");
132: free(filelist);
133: return NULL;
134: }
135:
136: strcpy(filelist[i], filename_inzip);
137: if ((i+1) < gi.number_entry)
138: {
139: err = unzGoToNextFile(uf);
140: if (err != UNZ_OK)
141: {
142: Log_Printf(LOG_ERROR, "ZIP_GetFiles: Error in ZIP-file\n");
143: /* deallocate memory */
144: for (; i > 0; i--)
145: free(filelist[i]);
146: free(filelist);
147: return NULL;
148: }
149: }
150: }
151:
152: unzClose(uf);
153:
154: zd = (zip_dir *)malloc(sizeof(zip_dir));
155: if (!zd)
156: {
157: perror("ZIP_GetFiles");
158: free(filelist);
159: return NULL;
160: }
161: zd->names = filelist;
162: zd->nfiles = nfiles;
163:
164: return zd;
165: }
166:
167:
168: /*-----------------------------------------------------------------------*/
169: /**
170: * Free the memory that has been allocated for a zip_dir.
171: */
172: void ZIP_FreeZipDir(zip_dir *f_zd)
173: {
174: while (f_zd->nfiles > 0)
175: {
176: f_zd->nfiles--;
177: free(f_zd->names[f_zd->nfiles]);
178: f_zd->names[f_zd->nfiles] = NULL;
179: }
180: free(f_zd->names);
181: f_zd->names = NULL;
182: free(f_zd);
183: }
184:
185:
186: /*-----------------------------------------------------------------------*/
187: /**
188: * Free the memory that has been allocated for fentries.
189: */
190: static void ZIP_FreeFentries(struct dirent **fentries, int entries)
191: {
192: while (entries > 0)
193: {
194: entries--;
195: free(fentries[entries]);
196: }
197: free(fentries);
198: }
199:
200:
201: /*-----------------------------------------------------------------------*/
202: /**
203: * Returns a list of files from the directory (dir) in a zip file list (zip)
204: * sets entries to the number of entries and returns a dirent structure, or
205: * NULL on failure. NOTE: only f_name is set in the dirent structures.
206: */
207: struct dirent **ZIP_GetFilesDir(const zip_dir *zip, const char *dir, int *entries)
208: {
209: int i,j;
210: zip_dir *files;
211: char *temp;
212: bool flag;
213: int slash;
214: struct dirent **fentries;
215:
216: files = (zip_dir *)malloc(sizeof(zip_dir));
217: if (!files)
218: {
219: perror("ZIP_GetFilesDir");
220: return NULL;
221: }
222:
223: files->names = (char **)malloc((zip->nfiles + 1) * sizeof(char *));
224: if (!files->names)
225: {
226: perror("ZIP_GetFilesDir");
227: free(files);
228: return NULL;
229: }
230:
231: /* add ".." directory */
232: files->nfiles = 1;
233: temp = (char *)malloc(4);
234: if (!temp)
235: {
236: ZIP_FreeZipDir(files);
237: return NULL;
238: }
239: temp[0] = temp[1] = '.';
240: temp[2] = '/';
241: temp[3] = '\0';
242: files->names[0] = temp;
243:
244: for (i = 0; i < zip->nfiles; i++)
245: {
246: if (strlen(zip->names[i]) > strlen(dir))
247: {
248: if (strncasecmp(zip->names[i], dir, strlen(dir)) == 0)
249: {
250: temp = zip->names[i];
251: temp = (char *)(temp + strlen(dir));
252: if (temp[0] != '\0')
253: {
254: if ((slash=Zip_FileNameHasSlash(temp)) > 0)
255: {
256: /* file is in a subdirectory, add this subdirectory if it doesn't exist in the list */
257: flag = false;
258: for (j = files->nfiles-1; j > 0; j--)
259: {
260: if (strncasecmp(temp, files->names[j], slash+1) == 0)
261: flag = true;
262: }
263: if (flag == false)
264: {
265: files->names[files->nfiles] = (char *)malloc(slash+2);
266: if (!files->names[files->nfiles])
267: {
268: perror("ZIP_GetFilesDir");
269: ZIP_FreeZipDir(files);
270: return NULL;
271: }
272: strncpy(files->names[files->nfiles], temp, slash+1);
273: ((char *)files->names[files->nfiles])[slash+1] = '\0';
274: files->nfiles++;
275: }
276: }
277: else
278: {
279: /* add a filename */
280: files->names[files->nfiles] = (char *)malloc(strlen(temp)+1);
281: if (!files->names[files->nfiles])
282: {
283: perror("ZIP_GetFilesDir");
284: ZIP_FreeZipDir(files);
285: return NULL;
286: }
287: strncpy(files->names[files->nfiles], temp, strlen(temp));
288: ((char *)files->names[files->nfiles])[strlen(temp)] = '\0';
289: files->nfiles++;
290: }
291: }
292: }
293: }
294: }
295:
296: /* copy to a dirent structure */
297: *entries = files->nfiles;
298: fentries = (struct dirent **)malloc(sizeof(struct dirent *)*files->nfiles);
299: if (!fentries)
300: {
301: perror("ZIP_GetFilesDir");
302: ZIP_FreeZipDir(files);
303: return NULL;
304: }
305: for (i = 0; i < files->nfiles; i++)
306: {
307: fentries[i] = (struct dirent *)malloc(sizeof(struct dirent));
308: if (!fentries[i])
309: {
310: perror("ZIP_GetFilesDir");
311: ZIP_FreeFentries(fentries, i+1);
312: return NULL;
313: }
314: strcpy(fentries[i]->d_name, files->names[i]);
315: }
316:
317: ZIP_FreeZipDir(files);
318:
319: return fentries;
320: }
321:
322:
323: /*-----------------------------------------------------------------------*/
324: /**
325: * Check an image file in the archive, return the uncompressed length
326: */
327: static long ZIP_CheckImageFile(unzFile uf, char *filename, int namelen, int *pDiskType)
328: {
329: unz_file_info file_info;
330:
331: if (unzLocateFile(uf,filename, 0) != UNZ_OK)
332: {
333: Log_Printf(LOG_ERROR, "Error: File \"%s\" not found in the archive!\n", filename);
334: return -1;
335: }
336:
337: if (unzGetCurrentFileInfo(uf, &file_info, filename, namelen, NULL, 0, NULL, 0) != UNZ_OK)
338: {
339: Log_Printf(LOG_ERROR, "Error with zipfile in unzGetCurrentFileInfo\n");
340: return -1;
341: }
342:
343: Log_Printf(LOG_ERROR, "Not an .ST, .MSA or .DIM file.\n");
344: return 0;
345: }
346:
347: /*-----------------------------------------------------------------------*/
348: /**
349: * Return the first matching file in a zip, or NULL on failure.
350: * String buffer size is ZIP_PATH_MAX
351: */
352: static char *ZIP_FirstFile(const char *filename, const char * const ppsExts[])
353: {
354: zip_dir *files;
355: int i, j;
356: char *name;
357:
358: files = ZIP_GetFiles(filename);
359: if (files == NULL)
360: return NULL;
361:
362: name = malloc(ZIP_PATH_MAX);
363: if (!name)
364: {
365: perror("ZIP_FirstFile");
366: return NULL;
367: }
368:
369: /* Do we have to scan for a certain extension? */
370: if (ppsExts)
371: {
372: name[0] = '\0';
373: for(i = files->nfiles-1; i >= 0; i--)
374: {
375: for (j = 0; ppsExts[j] != NULL; j++)
376: {
377: if (File_DoesFileExtensionMatch(files->names[i], ppsExts[j]))
378: {
379: strncpy(name, files->names[i], ZIP_PATH_MAX);
380: break;
381: }
382: }
383: }
384: }
385: else
386: {
387: /* There was no extension given -> use the very first name */
388: strncpy(name, files->names[0], ZIP_PATH_MAX);
389: }
390:
391: /* free the files */
392: ZIP_FreeZipDir(files);
393:
394: if (name[0] == '\0')
395: return NULL;
396: return name;
397: }
398:
399:
400: /*-----------------------------------------------------------------------*/
401: /**
402: * Extract a file (filename) from a ZIP-file (uf), the number of
403: * bytes to uncompress is size. Returns a pointer to a buffer containing
404: * the uncompressed data, or NULL.
405: */
406: static void *ZIP_ExtractFile(unzFile uf, const char *filename, uLong size)
407: {
408: int err = UNZ_OK;
409: char filename_inzip[ZIP_PATH_MAX];
410: void* buf;
411: uInt size_buf;
412: unz_file_info file_info;
413:
414:
415: if (unzLocateFile(uf,filename, 0) != UNZ_OK)
416: {
417: Log_Printf(LOG_ERROR, "ZIP_ExtractFile: could not find file in archive\n");
418: return NULL;
419: }
420:
421: err = unzGetCurrentFileInfo(uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
422:
423: if (err != UNZ_OK)
424: {
425: Log_Printf(LOG_ERROR, "ZIP_ExtractFile: could not get file info\n");
426: return NULL;
427: }
428:
429: size_buf = size;
430: buf = malloc(size_buf);
431: if (!buf)
432: {
433: perror("ZIP_ExtractFile");
434: return NULL;
435: }
436:
437: err = unzOpenCurrentFile(uf);
438: if (err != UNZ_OK)
439: {
440: Log_Printf(LOG_ERROR, "ZIP_ExtractFile: could not open file\n");
441: free(buf);
442: return NULL;
443: }
444:
445: do
446: {
447: err = unzReadCurrentFile(uf,buf,size_buf);
448: if (err < 0)
449: {
450: Log_Printf(LOG_ERROR, "ZIP_ExtractFile: could not read file\n");
451: return NULL;
452: }
453: }
454: while (err > 0);
455:
456: return buf;
457: }
458:
459:
460: /*-----------------------------------------------------------------------*/
461: /**
462: * Load disk image from a .ZIP archive into memory, set the number
463: * of bytes loaded into pImageSize and return the data or NULL on error.
464: */
465: Uint8 *ZIP_ReadDisk(const char *pszFileName, const char *pszZipPath, long *pImageSize)
466: {
467: uLong ImageSize=0;
468: unzFile uf=NULL;
469: Uint8 *buf;
470: char *path;
471: int nDiskType = -1;
472: Uint8 *pDiskBuffer = NULL;
473:
474: *pImageSize = 0;
475:
476: uf = unzOpen(pszFileName);
477: if (uf == NULL)
478: {
479: Log_Printf(LOG_ERROR, "Cannot open %s\n", pszFileName);
480: return NULL;
481: }
482:
483: if (pszZipPath == NULL || pszZipPath[0] == 0)
484: {
485: path = ZIP_FirstFile(pszFileName, pszDiskNameExts);
486: if (path == NULL)
487: {
488: Log_Printf(LOG_ERROR, "Cannot open %s\n", pszFileName);
489: unzClose(uf);
490: return NULL;
491: }
492: }
493: else
494: {
495: path = malloc(ZIP_PATH_MAX);
496: if (path == NULL)
497: {
498: perror("ZIP_ReadDisk");
499: unzClose(uf);
500: return NULL;
501: }
502: strncpy(path, pszZipPath, ZIP_PATH_MAX);
503: path[ZIP_PATH_MAX-1] = '\0';
504: }
505:
506: ImageSize = ZIP_CheckImageFile(uf, path, ZIP_PATH_MAX, &nDiskType);
507: if (ImageSize <= 0)
508: {
509: unzClose(uf);
510: free(path);
511: return NULL;
512: }
513:
514: /* extract to buf */
515: buf = ZIP_ExtractFile(uf, path, ImageSize);
516:
517: unzCloseCurrentFile(uf);
518: unzClose(uf);
519: free(path);
520: path = NULL;
521:
522: if (buf == NULL)
523: {
524: return NULL; /* failed extraction, return error */
525: }
526:
527: switch(nDiskType) {
528: case ZIP_FILE_MSA:
529: /* uncompress the MSA file */
530: // pDiskBuffer = MSA_UnCompress(buf, (long *)&ImageSize);
531: free(buf);
532: buf = NULL;
533: break;
534: case ZIP_FILE_DIM:
535: /* Skip DIM header */
536: ImageSize -= 32;
537: memmove(buf, buf+32, ImageSize);
538: /* ...and passthrough */
539: case ZIP_FILE_ST:
540: /* ST image => return buffer directly */
541: pDiskBuffer = buf;
542: break;
543: }
544:
545: if (pDiskBuffer)
546: {
547: *pImageSize = ImageSize;
548: }
549: return pDiskBuffer;
550: }
551:
552:
553: /*-----------------------------------------------------------------------*/
554: /**
555: * Save .ZIP file from memory buffer. Returns true if all is OK.
556: *
557: * Not yet implemented.
558: */
559: bool ZIP_WriteDisk(const char *pszFileName,unsigned char *pBuffer,int ImageSize)
560: {
561: return false;
562: }
563:
564:
565: /*-----------------------------------------------------------------------*/
566: /**
567: * Load first file from a .ZIP archive into memory, and return the number
568: * of bytes loaded.
569: */
570: Uint8 *ZIP_ReadFirstFile(const char *pszFileName, long *pImageSize, const char * const ppszExts[])
571: {
572: unzFile uf=NULL;
573: Uint8 *pBuffer;
574: char *pszZipPath;
575: unz_file_info file_info;
576:
577: *pImageSize = 0;
578:
579: /* Open the ZIP file */
580: uf = unzOpen(pszFileName);
581: if (uf == NULL)
582: {
583: Log_Printf(LOG_ERROR, "Cannot open '%s'\n", pszFileName);
584: return NULL;
585: }
586:
587: /* Locate the first file in the ZIP archive */
588: pszZipPath = ZIP_FirstFile(pszFileName, ppszExts);
589: if (pszZipPath == NULL)
590: {
591: Log_Printf(LOG_ERROR, "Failed to locate first file in '%s'\n", pszFileName);
592: unzClose(uf);
593: return NULL;
594: }
595:
596: if (unzLocateFile(uf, pszZipPath, 0) != UNZ_OK)
597: {
598: Log_Printf(LOG_ERROR, "Error: Can not locate '%s' in the archive!\n", pszZipPath);
599: free(pszZipPath);
600: return NULL;
601: }
602:
603: /* Get file information (file size!) */
604: if (unzGetCurrentFileInfo(uf, &file_info, pszZipPath, ZIP_PATH_MAX, NULL, 0, NULL, 0) != UNZ_OK)
605: {
606: Log_Printf(LOG_ERROR, "Error with zipfile in unzGetCurrentFileInfo.\n");
607: free(pszZipPath);
608: return NULL;
609: }
610:
611: /* Extract to buffer */
612: pBuffer = ZIP_ExtractFile(uf, pszZipPath, file_info.uncompressed_size);
613:
614: /* And close the file */
615: unzCloseCurrentFile(uf);
616: unzClose(uf);
617:
618: free(pszZipPath);
619:
620: if (pBuffer)
621: *pImageSize = file_info.uncompressed_size;
622:
623: return pBuffer;
624: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.