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