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