|
|
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: /**
192: * Returns a list of files from the directory (dir) in a zip file list (zip)
193: * sets entries to the number of entries and returns a dirent structure, or
194: * NULL on failure. NOTE: only f_name is set in the dirent structures.
195: */
196: struct dirent **ZIP_GetFilesDir(const zip_dir *zip, const char *dir, int *entries)
1.1 root 197: {
1.1.1.5 root 198: int i,j;
199: zip_dir *files;
200: char *temp;
1.1.1.7 root 201: bool flag;
1.1.1.5 root 202: int slash;
203: struct dirent **fentries;
204:
205: files = (zip_dir *)malloc(sizeof(zip_dir));
206: if (!files)
207: {
208: perror("ZIP_GetFilesDir");
209: return NULL;
210: }
211:
212: files->names = (char **)malloc((zip->nfiles + 1) * sizeof(char *));
213: if (!files->names)
214: {
215: perror("ZIP_GetFilesDir");
216: free(files);
217: return NULL;
218: }
219:
220: /* add ".." directory */
221: files->nfiles = 1;
222: temp = (char *)malloc(4);
223: if (!temp)
224: return NULL;
225: temp[0] = temp[1] = '.';
226: temp[2] = '/';
227: temp[3] = '\0';
228: files->names[0] = temp;
229:
230: for (i = 0; i < zip->nfiles; i++)
231: {
232: if (strlen(zip->names[i]) > strlen(dir))
1.1 root 233: {
1.1.1.5 root 234: if (strncasecmp(zip->names[i], dir, strlen(dir)) == 0)
1.1 root 235: {
1.1.1.5 root 236: temp = zip->names[i];
237: temp = (char *)(temp + strlen(dir));
238: if (temp[0] != '\0')
239: {
240: if ((slash=Zip_FileNameHasSlash(temp)) > 0)
241: {
242: /* file is in a subdirectory, add this subdirectory if it doesn't exist in the list */
1.1.1.9 ! root 243: flag = false;
1.1.1.5 root 244: for (j = files->nfiles-1; j > 0; j--)
245: {
246: if (strncasecmp(temp, files->names[j], slash+1) == 0)
1.1.1.9 ! root 247: flag = true;
1.1.1.5 root 248: }
1.1.1.9 ! root 249: if (flag == false)
1.1.1.5 root 250: {
1.1.1.6 root 251: files->names[files->nfiles] = (char *)malloc(slash+2);
1.1.1.5 root 252: if (!files->names[files->nfiles])
253: {
254: perror("ZIP_GetFilesDir");
255: return NULL;
256: }
257: strncpy(files->names[files->nfiles], temp, slash+1);
258: ((char *)files->names[files->nfiles])[slash+1] = '\0';
259: files->nfiles++;
260: }
261: }
262: else
263: {
264: /* add a filename */
265: files->names[files->nfiles] = (char *)malloc(strlen(temp)+1);
266: if (!files->names[files->nfiles])
267: {
268: perror("ZIP_GetFilesDir");
269: return NULL;
270: }
271: strncpy(files->names[files->nfiles], temp, strlen(temp));
272: ((char *)files->names[files->nfiles])[strlen(temp)] = '\0';
273: files->nfiles++;
274: }
275: }
1.1 root 276: }
277: }
278: }
279:
1.1.1.5 root 280: /* copy to a dirent structure */
281: *entries = files->nfiles;
282: fentries = (struct dirent **)malloc(sizeof(struct dirent *)*files->nfiles);
283: if (!fentries)
284: {
285: perror("ZIP_GetFilesDir");
286: ZIP_FreeZipDir(files);
287: return NULL;
288: }
289: for (i = 0; i < files->nfiles; i++)
290: {
291: fentries[i] = (struct dirent *)malloc(sizeof(struct dirent));
292: if (!fentries[i])
293: {
294: perror("ZIP_GetFilesDir");
295: return NULL;
296: }
297: strcpy(fentries[i]->d_name, files->names[i]);
298: }
1.1.1.3 root 299:
1.1.1.5 root 300: ZIP_FreeZipDir(files);
1.1.1.3 root 301:
1.1.1.5 root 302: return fentries;
1.1 root 303: }
304:
1.1.1.3 root 305:
1.1 root 306: /*-----------------------------------------------------------------------*/
1.1.1.6 root 307: /**
308: * Check an image file in the archive, return the uncompressed length
309: */
310: static long ZIP_CheckImageFile(unzFile uf, char *filename, int namelen, int *pDiskType)
1.1 root 311: {
1.1.1.5 root 312: unz_file_info file_info;
313:
314: if (unzLocateFile(uf,filename, 0) != UNZ_OK)
315: {
1.1.1.6 root 316: Log_Printf(LOG_ERROR, "Error: File \"%s\" not found in the archive!\n", filename);
1.1.1.5 root 317: return -1;
318: }
319:
1.1.1.6 root 320: if (unzGetCurrentFileInfo(uf, &file_info, filename, namelen, NULL, 0, NULL, 0) != UNZ_OK)
1.1.1.5 root 321: {
1.1.1.6 root 322: Log_Printf(LOG_ERROR, "Error with zipfile in unzGetCurrentFileInfo\n");
1.1.1.5 root 323: return -1;
324: }
325:
326: /* check for a .msa or .st extention */
1.1.1.9 ! root 327: if (MSA_FileNameIsMSA(filename, false))
1.1.1.5 root 328: {
329: *pDiskType = ZIP_FILE_MSA;
330: return file_info.uncompressed_size;
331: }
332:
1.1.1.9 ! root 333: if (ST_FileNameIsST(filename, false))
1.1.1.5 root 334: {
335: *pDiskType = ZIP_FILE_ST;
336: return file_info.uncompressed_size;
337: }
338:
1.1.1.9 ! root 339: if (DIM_FileNameIsDIM(filename, false))
1.1.1.5 root 340: {
341: *pDiskType = ZIP_FILE_DIM;
342: return file_info.uncompressed_size;
343: }
344:
345: Log_Printf(LOG_ERROR, "Not an .ST, .MSA or .DIM file.\n");
346: return 0;
347: }
348:
349: /*-----------------------------------------------------------------------*/
1.1.1.6 root 350: /**
351: * Return the first matching file in a zip, or NULL on failure.
352: * String buffer size is ZIP_PATH_MAX
353: */
354: static char *ZIP_FirstFile(const char *filename, const char * const ppsExts[])
1.1.1.5 root 355: {
356: zip_dir *files;
357: int i, j;
358: char *name;
359:
360: files = ZIP_GetFiles(filename);
361: if (files == NULL)
362: return NULL;
363:
364: name = malloc(ZIP_PATH_MAX);
365: if (!name)
366: {
367: perror("ZIP_FirstFile");
368: return NULL;
369: }
370:
371: /* Do we have to scan for a certain extension? */
372: if (ppsExts)
373: {
374: name[0] = '\0';
375: for(i = files->nfiles-1; i >= 0; i--)
376: {
377: for (j = 0; ppsExts[j] != NULL; j++)
378: {
379: if (File_DoesFileExtensionMatch(files->names[i], ppsExts[j]))
380: {
381: strncpy(name, files->names[i], ZIP_PATH_MAX);
382: break;
383: }
384: }
385: }
386: }
387: else
388: {
389: /* There was no extension given -> use the very first name */
390: strncpy(name, files->names[0], ZIP_PATH_MAX);
391: }
1.1 root 392:
1.1.1.5 root 393: /* free the files */
394: ZIP_FreeZipDir(files);
395:
396: if (name[0] == '\0')
397: return NULL;
398: return name;
1.1 root 399: }
400:
401:
402: /*-----------------------------------------------------------------------*/
1.1.1.6 root 403: /**
404: * Extract a file (filename) from a ZIP-file (uf), the number of
405: * bytes to uncompress is size. Returns a pointer to a buffer containing
406: * the uncompressed data, or NULL.
407: */
408: static void *ZIP_ExtractFile(unzFile uf, const char *filename, uLong size)
1.1 root 409: {
1.1.1.5 root 410: int err = UNZ_OK;
411: char filename_inzip[ZIP_PATH_MAX];
412: void* buf;
413: uInt size_buf;
414: unz_file_info file_info;
415:
416:
417: if (unzLocateFile(uf,filename, 0) != UNZ_OK)
418: {
419: Log_Printf(LOG_ERROR, "ZIP_ExtractFile: could not find file in archive\n");
420: return NULL;
421: }
422:
423: err = unzGetCurrentFileInfo(uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
424:
425: if (err != UNZ_OK)
426: {
427: Log_Printf(LOG_ERROR, "ZIP_ExtractFile: could not get file info\n");
428: return NULL;
429: }
430:
431: size_buf = size;
432: buf = malloc(size_buf);
433: if (!buf)
434: {
435: perror("ZIP_ExtractFile");
436: return NULL;
437: }
438:
439: err = unzOpenCurrentFile(uf);
440: if (err != UNZ_OK)
441: {
442: Log_Printf(LOG_ERROR, "ZIP_ExtractFile: could not open file\n");
443: return NULL;
444: }
445:
446: do
447: {
448: err = unzReadCurrentFile(uf,buf,size_buf);
449: if (err < 0)
450: {
451: Log_Printf(LOG_ERROR, "ZIP_ExtractFile: could not read file\n");
452: return NULL;
453: }
454: }
455: while (err > 0);
456:
457: return buf;
1.1 root 458: }
459:
1.1.1.5 root 460:
1.1 root 461: /*-----------------------------------------------------------------------*/
1.1.1.6 root 462: /**
463: * Load disk image from a .ZIP archive into memory, set the number
464: * of bytes loaded into pImageSize and return the data or NULL on error.
465: */
466: Uint8 *ZIP_ReadDisk(const char *pszFileName, const char *pszZipPath, long *pImageSize)
1.1 root 467: {
1.1.1.5 root 468: uLong ImageSize=0;
469: unzFile uf=NULL;
470: Uint8 *buf;
1.1.1.6 root 471: char *path;
1.1.1.5 root 472: int nDiskType = -1;
473: Uint8 *pDiskBuffer = NULL;
474:
475: *pImageSize = 0;
476:
477: uf = unzOpen(pszFileName);
478: if (uf == NULL)
479: {
480: Log_Printf(LOG_ERROR, "Cannot open %s\n", pszFileName);
481: return NULL;
482: }
1.1 root 483:
1.1.1.5 root 484: if (pszZipPath == NULL || pszZipPath[0] == 0)
485: {
1.1.1.6 root 486: path = ZIP_FirstFile(pszFileName, pszDiskNameExts);
487: if (path == NULL)
1.1.1.5 root 488: {
489: Log_Printf(LOG_ERROR, "Cannot open %s\n", pszFileName);
490: unzClose(uf);
491: return NULL;
492: }
1.1.1.6 root 493: }
494: else
495: {
496: path = malloc(ZIP_PATH_MAX);
497: if (path == NULL)
498: {
499: perror("ZIP_ReadDisk");
500: unzClose(uf);
501: return NULL;
502: }
503: strncpy(path, pszZipPath, ZIP_PATH_MAX);
504: path[ZIP_PATH_MAX-1] = '\0';
1.1.1.5 root 505: }
1.1.1.2 root 506:
1.1.1.6 root 507: ImageSize = ZIP_CheckImageFile(uf, path, ZIP_PATH_MAX, &nDiskType);
1.1.1.5 root 508: if (ImageSize <= 0)
509: {
510: unzClose(uf);
1.1.1.6 root 511: free(path);
1.1.1.5 root 512: return NULL;
513: }
514:
515: /* extract to buf */
1.1.1.6 root 516: buf = ZIP_ExtractFile(uf, path, ImageSize);
517:
1.1.1.5 root 518: unzCloseCurrentFile(uf);
519: unzClose(uf);
1.1.1.6 root 520: free(path);
521: path = NULL;
522:
1.1.1.5 root 523: if (buf == NULL)
524: {
525: return NULL; /* failed extraction, return error */
526: }
527:
1.1.1.6 root 528: switch(nDiskType) {
529: case ZIP_FILE_MSA:
1.1.1.5 root 530: /* uncompress the MSA file */
1.1.1.6 root 531: pDiskBuffer = MSA_UnCompress(buf, (long *)&ImageSize);
532: free(buf);
533: buf = NULL;
534: break;
535: case ZIP_FILE_DIM:
1.1.1.5 root 536: /* Skip DIM header */
537: ImageSize -= 32;
1.1.1.6 root 538: memmove(buf, buf+32, ImageSize);
539: /* ...and passthrough */
540: case ZIP_FILE_ST:
541: /* ST image => return buffer directly */
542: pDiskBuffer = buf;
543: break;
1.1.1.5 root 544: }
1.1.1.6 root 545:
546: if (pDiskBuffer)
547: {
1.1.1.5 root 548: *pImageSize = ImageSize;
1.1.1.6 root 549: }
1.1.1.5 root 550: return pDiskBuffer;
1.1 root 551: }
552:
553:
554: /*-----------------------------------------------------------------------*/
1.1.1.6 root 555: /**
1.1.1.9 ! root 556: * Save .ZIP file from memory buffer. Returns true if all is OK.
1.1.1.6 root 557: *
558: * Not yet implemented.
559: */
1.1.1.7 root 560: bool ZIP_WriteDisk(const char *pszFileName,unsigned char *pBuffer,int ImageSize)
1.1 root 561: {
1.1.1.9 ! root 562: return false;
1.1 root 563: }
564:
1.1.1.5 root 565:
566: /*-----------------------------------------------------------------------*/
1.1.1.6 root 567: /**
568: * Load first file from a .ZIP archive into memory, and return the number
569: * of bytes loaded.
570: */
571: Uint8 *ZIP_ReadFirstFile(const char *pszFileName, long *pImageSize, const char * const ppszExts[])
1.1.1.5 root 572: {
573: unzFile uf=NULL;
574: Uint8 *pBuffer;
575: char *pszZipPath;
576: unz_file_info file_info;
577:
578: *pImageSize = 0;
579:
580: /* Open the ZIP file */
581: uf = unzOpen(pszFileName);
582: if (uf == NULL)
583: {
584: Log_Printf(LOG_ERROR, "Cannot open '%s'\n", pszFileName);
585: return NULL;
586: }
587:
588: /* Locate the first file in the ZIP archive */
589: pszZipPath = ZIP_FirstFile(pszFileName, ppszExts);
590: if (pszZipPath == NULL)
591: {
592: Log_Printf(LOG_ERROR, "Failed to locate first file in '%s'\n", pszFileName);
593: unzClose(uf);
594: return NULL;
595: }
596:
597: if (unzLocateFile(uf, pszZipPath, 0) != UNZ_OK)
598: {
1.1.1.6 root 599: Log_Printf(LOG_ERROR, "Error: Can not locate '%s' in the archive!\n", pszZipPath);
600: free(pszZipPath);
1.1.1.5 root 601: return NULL;
602: }
603:
604: /* Get file information (file size!) */
605: if (unzGetCurrentFileInfo(uf, &file_info, pszZipPath, ZIP_PATH_MAX, NULL, 0, NULL, 0) != UNZ_OK)
606: {
607: Log_Printf(LOG_ERROR, "Error with zipfile in unzGetCurrentFileInfo.\n");
1.1.1.6 root 608: free(pszZipPath);
1.1.1.5 root 609: return NULL;
610: }
611:
612: /* Extract to buffer */
613: pBuffer = ZIP_ExtractFile(uf, pszZipPath, file_info.uncompressed_size);
614:
615: /* And close the file */
616: unzCloseCurrentFile(uf);
617: unzClose(uf);
618:
619: free(pszZipPath);
620:
621: if (pBuffer)
622: *pImageSize = file_info.uncompressed_size;
623:
624: return pBuffer;
625: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.