|
|
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.