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