zipArchive.cpp
Engine/source/core/util/zip/zipArchive.cpp
Namespaces:
Detailed Description
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "platform/platform.h" 25#include "core/util/zip/zipArchive.h" 26 27#include "core/stream/stream.h" 28#include "core/stream/fileStream.h" 29#include "core/filterStream.h" 30#include "core/util/zip/zipCryptStream.h" 31#include "core/crc.h" 32//#include "core/resManager.h" 33 34#include "console/console.h" 35 36#include "core/util/zip/compressor.h" 37#include "core/util/zip/zipTempStream.h" 38#include "core/util/zip/zipStatFilter.h" 39 40#ifdef TORQUE_ZIP_AES 41#include "core/zipAESCryptStream.h" 42#include "core/zip/crypto/aes.h" 43#endif 44 45#include "core/util/safeDelete.h" 46 47#include "app/version.h" 48 49namespace Zip 50{ 51 52//----------------------------------------------------------------------------- 53// Constructor/Destructor 54//----------------------------------------------------------------------------- 55 56ZipArchive::ZipArchive() : 57 mStream(NULL), 58 mDiskStream(NULL), 59 mMode(Read), 60 mRoot(NULL), 61 mFilename(NULL) 62{ 63} 64 65ZipArchive::~ZipArchive() 66{ 67 closeArchive(); 68} 69 70//----------------------------------------------------------------------------- 71// Protected Methods 72//----------------------------------------------------------------------------- 73 74bool ZipArchive::readCentralDirectory() 75{ 76 mEntries.clear(); 77 SAFE_DELETE(mRoot); 78 mRoot = new ZipEntry; 79 mRoot->mName = ""; 80 mRoot->mIsDirectory = true; 81 mRoot->mCD.setFilename(""); 82 83 if(! mEOCD.findInStream(mStream)) 84 return false; 85 86 if(! mEOCD.read(mStream)) 87 return false; 88 89 if(mEOCD.mDiskNum != mEOCD.mStartCDDiskNum || 90 mEOCD.mNumEntriesInThisCD != mEOCD.mTotalEntriesInCD) 91 { 92 if(isVerbose()) 93 Con::errorf("ZipArchive::readCentralDirectory - %s: Zips that span multiple disks are not supported.", mFilename ? mFilename : "<no filename>"); 94 return false; 95 } 96 97 if(! mStream->setPosition(mEOCD.mCDOffset)) 98 return false; 99 100 for(S32 i = 0;i < mEOCD.mNumEntriesInThisCD;++i) 101 { 102 ZipEntry *ze = new ZipEntry; 103 if(! ze->mCD.read(mStream)) 104 { 105 delete ze; 106 107 if(isVerbose()) 108 Con::errorf("ZipArchive::readCentralDirectory - %s: Error reading central directory.", mFilename ? mFilename : "<no filename>"); 109 return false; 110 } 111 112 insertEntry(ze); 113 } 114 115 return true; 116} 117 118void ZipArchive::dumpCentralDirectory(ZipEntry* entry, String* indent) 119{ 120 // if entry is null, use root 121 if (entry == NULL) 122 entry = mRoot; 123 124 if (!entry) 125 return; 126 127 String emptyIndent; 128 if (indent == NULL) 129 indent = &emptyIndent; 130 131 Con::printf("%s%s%s", indent->c_str(), entry->mIsDirectory ? "/" : "", entry->mName.c_str()); 132 for (Map<String,ZipEntry*>::Iterator iter = entry->mChildren.begin(); 133 iter != entry->mChildren.end(); 134 ++iter) 135 { 136 String newIdent = *indent + " "; 137 dumpCentralDirectory((*iter).value, &(newIdent)); 138 } 139} 140 141//----------------------------------------------------------------------------- 142 143void ZipArchive::insertEntry(ZipEntry *ze) 144{ 145 char path[1024]; 146 dStrncpy(path, ze->mCD.mFilename.c_str(), sizeof(path)); 147 path[sizeof(path) - 1] = 0; 148 149 for(S32 i = 0;i < dStrlen(path);++i) 150 { 151 if(path[i] == '\\') 152 path[i] = '/'; 153 } 154 155 ZipEntry *root = mRoot; 156 157 char *ptr = path, *slash = NULL; 158 do 159 { 160 slash = dStrchr(ptr, '/'); 161 if(slash) 162 { 163 // Add the directory 164 *slash = 0; 165 166 // try to get root, create if not found 167 ZipEntry *newEntry = NULL; 168 if (!root->mChildren.tryGetValue(ptr, newEntry)) 169 { 170 newEntry = new ZipEntry; 171 newEntry->mParent = root; 172 newEntry->mName = String(ptr); 173 newEntry->mIsDirectory = true; 174 newEntry->mCD.setFilename(path); 175 176 root->mChildren[ptr] = newEntry; 177 } 178 179 root = newEntry; 180 181 *slash = '/'; 182 ptr = slash + 1; 183 } 184 else 185 { 186 // Add the file. 187 if(*ptr) 188 { 189 ze->mIsDirectory = false; 190 ze->mName = ptr; 191 ze->mParent = root; 192 root->mChildren[ptr] = ze; 193 mEntries.push_back(ze); 194 } 195 else 196 { 197 // [tom, 2/6/2007] If ptr is empty, this was a directory entry. Since 198 // we created a new entry for it above, we need to delete the old 199 // pointer otherwise it will leak as it won't have got inserted. 200 201 delete ze; 202 } 203 } 204 } while(slash); 205} 206 207void ZipArchive::removeEntry(ZipEntry *ze) 208{ 209 if(ze == mRoot) 210 { 211 // [tom, 2/1/2007] We don't want to remove the root as it should always 212 // be removed through closeArchive() 213 AssertFatal(0, "ZipArchive::removeEntry - Attempting to remove the root"); 214 return; 215 } 216 217 // Can't iterate the hash table, so we can't do this safely 218 AssertFatal(!ze->mIsDirectory, "ZipArchive::removeEntry - Cannot remove a directory"); 219 220 // See if we have a temporary file for this entry 221 Vector<ZipTempStream *>::iterator i; 222 for(i = mTempFiles.begin();i != mTempFiles.end();++i) 223 { 224 if((*i)->getCentralDir() == &ze->mCD) 225 { 226 SAFE_DELETE(*i); 227 mTempFiles.erase(i); 228 229 break; 230 } 231 } 232 233 // Remove from the tree 234 Vector<ZipEntry *>::iterator j; 235 for(j = mEntries.begin();j != mEntries.end();++j) 236 { 237 if(*j == ze) 238 { 239 mEntries.erase(j); 240 break; 241 } 242 } 243 244 // [tom, 2/2/2007] This must be last, as ze is no longer valid once it's 245 // removed from the parent. 246 ZipEntry *z = ze->mParent->mChildren[ze->mName]; 247 ze->mParent->mChildren.erase(ze->mName); 248 delete z; 249} 250 251//----------------------------------------------------------------------------- 252 253CentralDir *ZipArchive::findFileInfo(const char *filename) 254{ 255 ZipEntry *ze = findZipEntry(filename); 256 return ze ? &ze->mCD : NULL; 257} 258 259ZipArchive::ZipEntry *ZipArchive::findZipEntry(const char *filename) 260{ 261 char path[1024]; 262 dStrncpy(path, filename, sizeof(path)); 263 path[sizeof(path) - 1] = 0; 264 265 for(S32 i = 0;i < dStrlen(path);++i) 266 { 267 if(path[i] == '\\') 268 path[i] = '/'; 269 } 270 271 ZipEntry *root = mRoot; 272 273 char *ptr = path, *slash = NULL; 274 do 275 { 276 slash = dStrchr(ptr, '/'); 277 if(slash) 278 { 279 // Find the directory 280 *slash = 0; 281 282 // check child dict for new root 283 ZipEntry *newRoot = NULL; 284 if (!root->mChildren.tryGetValue(ptr, newRoot)) 285 return NULL; 286 287 root = newRoot; 288 289 ptr = slash + 1; 290 } 291 else 292 { 293 // Find the file 294 ZipEntry* entry = NULL; 295 if (root->mChildren.tryGetValue(ptr, entry)) 296 return entry; 297 } 298 } while(slash); 299 300 return NULL; 301} 302 303//----------------------------------------------------------------------------- 304 305Stream *ZipArchive::createNewFile(const char *filename, Compressor *method) 306{ 307 ZipEntry *ze = new ZipEntry; 308 ze->mIsDirectory = false; 309 ze->mCD.setFilename(filename); 310 insertEntry(ze); 311 312 ZipTempStream *stream = new ZipTempStream(&ze->mCD); 313 if(stream->open()) 314 { 315 Stream *retStream = method->createWriteStream(&ze->mCD, stream); 316 if(retStream == NULL) 317 { 318 delete stream; 319 return NULL; 320 } 321 322 ZipStatFilter *filter = new ZipStatFilter(&ze->mCD); 323 if(! filter->attachStream(retStream)) 324 { 325 delete filter; 326 delete retStream; 327 delete stream; 328 return NULL; 329 } 330 331 ze->mCD.mCompressMethod = method->getMethod(); 332 ze->mCD.mInternalFlags |= CDFileOpen; 333 334 return filter; 335 } 336 337 return NULL; 338} 339 340void ZipArchive::updateFile(ZipTempStream *stream) 341{ 342 CentralDir *cd = stream->getCentralDir(); 343 344 // [tom, 1/23/2007] Uncompressed size and CRC32 are updated by ZipStatFilter 345 cd->mCompressedSize = stream->getStreamSize(); 346 cd->mInternalFlags |= CDFileDirty; 347 cd->mInternalFlags &= ~<a href="/coding/namespace/namespacezip/#namespacezip_1ad771ec68ae52065ffc275a917e05b7b3a20443c9b2b3971b1d0ecbb81874b0055">CDFileOpen</a>; 348 349 // Upper byte should be zero, lower is version as major * 10 + minor 350 cd->mVersionMadeBy = (getVersionNumber() / 100) & 0xff; 351 cd->mExtractVer = 20; 352 353 U32 dosTime = currentTimeToDOSTime(); 354 cd->mModTime = dosTime & 0x0000ffff; 355 cd->mModDate = (dosTime & 0xffff0000) >> 16; 356 357 mTempFiles.push_back(stream); 358} 359 360//----------------------------------------------------------------------------- 361 362U32 ZipArchive::localTimeToDOSTime(const Torque::Time::DateTime &dt) 363{ 364 // DOS time format 365 // http://msdn.microsoft.com/en-us/library/ms724274(VS.85).aspx 366 return TimeToDOSTime(Torque::Time(dt)); 367} 368 369U32 ZipArchive::TimeToDOSTime(const Torque::Time& t) 370{ 371 S32 year,month,day,hour,minute,second,microsecond; 372 t.get(&year, &month, &day, &hour, &minute, &second, µsecond); 373 374 if(year > 1980) // De Do Do Do, De Da Da Da 375 year -= 1980; 376 377 return (((day) + (32 * (month)) + (512 * year)) << 16) | ((second/2) + (32* minute) + (2048 * (U32)hour)); 378} 379 380Torque::Time ZipArchive::DOSTimeToTime(U16 time, U16 date) 381{ 382 Torque::Time::DateTime dt; 383 dt.microsecond = 0; 384 dt.hour = (time & 0xF800) >> 11; 385 dt.minute = (time & 0x07E0) >> 5; 386 dt.second = (time & 0x001F)*2; 387 388 dt.year = ((date & 0xFE00) >> 9) + 1980; 389 dt.month = (date & 0x01E0) >> 5; 390 dt.day = (date & 0x001F); 391 392 return Torque::Time(dt); 393} 394 395Torque::Time ZipArchive::DOSTimeToTime(U32 dosTime) 396{ 397 U16 time = dosTime & 0x0000ffff; 398 U16 date = (dosTime & 0xffff0000) >> 16; 399 400 return ZipArchive::DOSTimeToTime(time, date); 401} 402 403U32 ZipArchive::currentTimeToDOSTime() 404{ 405 Torque::Time::DateTime dt; 406 Torque::Time::getCurrentDateTime(dt); 407 408 return localTimeToDOSTime(dt); 409} 410 411//----------------------------------------------------------------------------- 412 413// [tom, 1/24/2007] The general idea here is we want to create a new file, 414// copy any data from the old zip file and add the new stuff. Once the new 415// zip is created, delete the old one and rename the new one. 416 417bool ZipArchive::rebuildZip() 418{ 419 String newZipName; 420 FileStream tempFile; 421 Stream *zipFile = mStream; 422 423 // FIXME [tom, 1/24/2007] Temporary for expediting testing 424 if(mFilename == NULL) 425 return false; 426 427 if(mMode == ReadWrite) 428 { 429 newZipName = String(mFilename) + ".new"; 430 431 if(! tempFile.open(newZipName, mMode == Write ? Torque::FS::File::Write : Torque::FS::File::ReadWrite)) 432 return false; 433 434 zipFile = &tempFile; 435 } 436 437 // Copy any unmodified files 438 for(S32 i = 0;i < mEntries.size();++i) 439 { 440 ZipEntry *entry = mEntries[i]; 441 442 // Directories are internal only for lookup purposes 443 if(entry->mIsDirectory || (entry->mCD.mInternalFlags & (CDFileDirty | CDFileDeleted))) 444 continue; 445 446 copyFileToNewZip(&entry->mCD, zipFile); 447 } 448 449 // Copy any dirty files 450 for(S32 i = 0;i < mTempFiles.size();++i) 451 { 452 ZipTempStream *zts = mTempFiles[i]; 453 454 writeDirtyFileToNewZip(zts, zipFile); 455 zts->close(); 456 delete zts; 457 mTempFiles[i] = NULL; 458 } 459 mTempFiles.clear(); 460 461 // Write central directory 462 mEOCD.mCDOffset = zipFile->getPosition(); 463 mEOCD.mNumEntriesInThisCD = 0; 464 465 for(S32 i = 0;i < mEntries.size();++i) 466 { 467 ZipEntry *entry = mEntries[i]; 468 469 // [tom, 1/24/2007] Directories are internal only for lookup purposes 470 if(entry->mIsDirectory || (entry->mCD.mInternalFlags & CDFileDeleted) != 0) 471 continue; 472 473 ++mEOCD.mNumEntriesInThisCD; 474 if(! entry->mCD.write(zipFile)) 475 break; 476 } 477 478 mEOCD.mCDSize = zipFile->getPosition() - mEOCD.mCDOffset; 479 mEOCD.mTotalEntriesInCD = mEOCD.mNumEntriesInThisCD; 480 481 mEOCD.mDiskNum = 0; 482 mEOCD.mStartCDDiskNum = 0; 483 484 mEOCD.write(zipFile); 485 486 if(mMode == ReadWrite) 487 { 488 // Close file, replace old zip with it 489 tempFile.close(); 490 491 // [tom, 2/1/2007] The disk stream must be closed else we can't rename 492 // the file. Since rebuildZip() is only called from closeArchive() this 493 // should be safe. 494 if(mDiskStream) 495 { 496 mDiskStream->close(); 497 498 delete mDiskStream; 499 mDiskStream = NULL; 500 } 501 502 String oldRename; 503 oldRename = String(mFilename) + ".old"; 504 505 if(! Torque::FS::Rename(mFilename, oldRename)) 506 return false; 507 508 if(! Torque::FS::Rename(newZipName, mFilename)) 509 return false; 510 511 Torque::FS::Remove(oldRename); 512 } 513 return true; 514} 515 516bool ZipArchive::writeDirtyFileToNewZip(ZipTempStream *fileStream, Stream *zipStream) 517{ 518 CentralDir *cdir = fileStream->getCentralDir(); 519 FileHeader fh(*cdir); 520 fh.setFilename(cdir->mFilename); 521 522 cdir->mLocalHeadOffset = zipStream->getPosition(); 523 524 // Write header and file 525 if(! fh.write(zipStream)) 526 return false; 527 528 if(! fileStream->rewind()) 529 return false; 530 531 return zipStream->copyFrom(fileStream); 532} 533 534bool ZipArchive::copyFileToNewZip(CentralDir *cdir, Stream *newZipStream) 535{ 536 // [tom, 1/24/2007] Using the stored compressor allows us to copy the raw 537 // data regardless of compression method without having to re-compress it. 538 Compressor *comp = Compressor::findCompressor(Stored); 539 if(comp == NULL) 540 return false; 541 542 if(! mStream->setPosition(cdir->mLocalHeadOffset)) 543 return false; 544 545 // Copy file header 546 // FIXME [tom, 1/24/2007] This will currently not copy the extra fields 547 FileHeader fh; 548 if(! fh.read(mStream)) 549 return false; 550 551 cdir->mLocalHeadOffset = newZipStream->getPosition(); 552 553 if(! fh.write(newZipStream)) 554 return false; 555 556 // Copy file data 557 Stream *readS = comp->createReadStream(cdir, mStream); 558 if(readS == NULL) 559 return false; 560 561 bool ret = newZipStream->copyFrom(readS); 562 563 // [tom, 1/24/2007] closeFile() just frees the relevant filters and 564 // thus it is safe to call from here. 565 closeFile(readS); 566 567 return ret; 568} 569 570//----------------------------------------------------------------------------- 571// Public Methods 572//----------------------------------------------------------------------------- 573 574void ZipArchive::setFilename(const char *filename) 575{ 576 SAFE_FREE(mFilename); 577 if(filename) 578 mFilename = dStrdup(filename); 579} 580 581//----------------------------------------------------------------------------- 582 583bool ZipArchive::openArchive(const char *filename, AccessMode mode /* = Read */) 584{ 585 if(mode != Read && mode != Write && mode != ReadWrite) 586 return false; 587 588 closeArchive(); 589 590 mDiskStream = new FileStream; 591 if(mDiskStream->open(filename, (Torque::FS::File::AccessMode)mode)) 592 { 593 setFilename(filename); 594 595 if(openArchive(mDiskStream, mode)) 596 return true; 597 } 598 599 // Cleanup just in case openArchive() failed 600 closeArchive(); 601 602 return false; 603} 604 605bool ZipArchive::openArchive(Stream *stream, AccessMode mode /* = Read */) 606{ 607 if(mode != Read && mode != Write && mode != ReadWrite) 608 return false; 609 610 mStream = stream; 611 mMode = mode; 612 613 if(mode == Read || mode == ReadWrite) 614 { 615 bool ret = readCentralDirectory(); 616 if(mode == Read) 617 return ret; 618 619 return true; 620 } 621 else 622 { 623 mEntries.clear(); 624 SAFE_DELETE(mRoot); 625 mRoot = new ZipEntry; 626 mRoot->mName = ""; 627 mRoot->mIsDirectory = true; 628 mRoot->mCD.setFilename(""); 629 } 630 631 return true; 632} 633 634void ZipArchive::closeArchive() 635{ 636 if(mMode == Write || mMode == ReadWrite) 637 rebuildZip(); 638 639 // Free any remaining temporary files 640 for(S32 i = 0;i < mTempFiles.size();++i) 641 { 642 SAFE_DELETE(mTempFiles[i]); 643 } 644 mTempFiles.clear(); 645 646 // Close the zip file stream and clean up 647 if(mDiskStream) 648 { 649 mDiskStream->close(); 650 651 delete mDiskStream; 652 mDiskStream = NULL; 653 } 654 655 mStream = NULL; 656 657 SAFE_FREE(mFilename); 658 SAFE_DELETE(mRoot); 659 mEntries.clear(); 660} 661 662//----------------------------------------------------------------------------- 663Stream * ZipArchive::openFile(const char *filename, AccessMode mode /* = Read */) 664{ 665 ZipEntry* ze = findZipEntry(filename); 666 return openFile(filename, ze, mode); 667} 668 669Stream * ZipArchive::openFile(const char *filename, ZipEntry* ze, AccessMode mode /* = Read */) 670{ 671 if(mode == Read) 672 { 673 if(ze == NULL) 674 return NULL; 675 676 return openFileForRead(&ze->mCD); 677 } 678 679 if(mode == Write) 680 { 681 if(ze) 682 { 683 if(ze->mCD.mInternalFlags & CDFileOpen) 684 { 685 if(isVerbose()) 686 Con::errorf("ZipArchive::openFile - File %s is already open", filename); 687 return NULL; 688 } 689 690 // Remove the old entry so we can create a new one 691 removeEntry(ze); 692 ze = NULL; 693 } 694 695 return createNewFile(filename, Compressor::findCompressor(Deflated)); 696 } 697 698 if(isVerbose()) 699 Con::errorf("ZipArchive::openFile - Files within zips can only be opened as read or write, but not both at the same time."); 700 701 return NULL; 702} 703 704void ZipArchive::closeFile(Stream *stream) 705{ 706 FilterStream *currentStream, *nextStream; 707 708 nextStream = dynamic_cast<FilterStream*>(stream); 709 while (nextStream) 710 { 711 currentStream = nextStream; 712 stream = currentStream->getStream(); 713 714 currentStream->detachStream(); 715 716 nextStream = dynamic_cast<FilterStream*>(stream); 717 718 delete currentStream; 719 } 720 721 ZipTempStream *tempStream = dynamic_cast<ZipTempStream *>(stream); 722 if(tempStream && (tempStream->getCentralDir()->mInternalFlags & CDFileOpen)) 723 { 724 // [tom, 1/23/2007] This is a temporary file we are writing to 725 // so we need to update the relevant information in the header. 726 updateFile(tempStream); 727 } 728} 729 730//----------------------------------------------------------------------------- 731 732Stream *ZipArchive::openFileForRead(const CentralDir *fileCD) 733{ 734 if(mMode != Read && mMode != ReadWrite) 735 return NULL; 736 737 if((fileCD->mInternalFlags & (CDFileDeleted | CDFileOpen)) != 0) 738 return NULL; 739 740 Stream *stream = mStream; 741 742 if(fileCD->mInternalFlags & CDFileDirty) 743 { 744 // File is dirty, we need to read from the temporary file 745 for(S32 i = 0;i < mTempFiles.size();++i) 746 { 747 if(mTempFiles[i]->getCentralDir() == fileCD) 748 { 749 // Found the temporary file 750 if(! mTempFiles[i]->rewind()) 751 { 752 if(isVerbose()) 753 Con::errorf("ZipArchive::openFile - %s: %s is dirty, but could not rewind temporary file?", mFilename ? mFilename : "<no filename>", fileCD->mFilename.c_str()); 754 return NULL; 755 } 756 757 stream = mTempFiles[i]; 758 break; 759 } 760 } 761 762 if(stream == mStream) 763 { 764 if(isVerbose()) 765 Con::errorf("ZipArchive::openFile - %s: %s is dirty, but no temporary file found?", mFilename ? mFilename : "<no filename>", fileCD->mFilename.c_str()); 766 return NULL; 767 } 768 } 769 else 770 { 771 // Read from the zip file directly 772 if(! mStream->setPosition(fileCD->mLocalHeadOffset)) 773 { 774 if(isVerbose()) 775 Con::errorf("ZipArchive::openFile - %s: Could not locate local header for file %s", mFilename ? mFilename : "<no filename>", fileCD->mFilename.c_str()); 776 return NULL; 777 } 778 779 FileHeader fh; 780 if(! fh.read(mStream)) 781 { 782 if(isVerbose()) 783 Con::errorf("ZipArchive::openFile - %s: Could not read local header for file %s", mFilename ? mFilename : "<no filename>", fileCD->mFilename.c_str()); 784 return NULL; 785 } 786 } 787 788 Stream *attachTo = stream; 789 U16 compMethod = fileCD->mCompressMethod; 790 791 if(fileCD->mFlags & Encrypted) 792 { 793 if(fileCD->mCompressMethod == AESEncrypted) 794 { 795 // [tom, 1/19/2007] Whilst AES support does exist, I'm not including it in TGB 796 // to avoid having to deal with crypto export legal issues. 797 Con::errorf("ZipArchive::openFile - %s: File %s is AES encrypted, but AES is not supported in this version.", mFilename ? mFilename : "<no filename>", fileCD->mFilename.c_str()); 798 } 799 else 800 { 801 ZipCryptRStream *cryptStream = new ZipCryptRStream; 802 cryptStream->setPassword(DEFAULT_ZIP_PASSWORD); 803 cryptStream->setFileEndPos(stream->getPosition() + fileCD->mCompressedSize); 804 if(! cryptStream->attachStream(stream)) 805 { 806 delete cryptStream; 807 return NULL; 808 } 809 810 attachTo = cryptStream; 811 } 812 } 813 814 Compressor *comp = Compressor::findCompressor(compMethod); 815 if(comp == NULL) 816 { 817 if(isVerbose()) 818 Con::errorf("ZipArchive::openFile - %s: Unsupported compression method (%d) for file %s", mFilename ? mFilename : "<no filename>", fileCD->mCompressMethod, fileCD->mFilename.c_str()); 819 return NULL; 820 } 821 822 return comp->createReadStream(fileCD, attachTo); 823} 824 825//----------------------------------------------------------------------------- 826 827bool ZipArchive::addFile(const char *filename, const char *pathInZip, bool replace /* = true */) 828{ 829 FileStream f; 830 if (!f.open(filename, Torque::FS::File::Read)) 831 return false; 832 833 const CentralDir *cd = findFileInfo(pathInZip); 834 if(! replace && cd && (cd->mInternalFlags & CDFileDeleted) == 0) 835 return false; 836 837 Stream *dest = openFile(pathInZip, Write); 838 if(dest == NULL) 839 { 840 f.close(); 841 return false; 842 } 843 844 bool ret = dest->copyFrom(&f); 845 846 closeFile(dest); 847 f.close(); 848 849 return ret; 850} 851 852bool ZipArchive::extractFile(const char *pathInZip, const char *filename, bool *crcFail /* = NULL */) 853{ 854 if(crcFail) 855 *crcFail = false; 856 857 const CentralDir *realCD = findFileInfo(pathInZip); 858 if(realCD == NULL) 859 return false; 860 861 FileStream dest; 862 if(! dest.open(filename, Torque::FS::File::Write)) 863 return false; 864 865 Stream *source = openFile(pathInZip, Read); 866 if(source == NULL) 867 { 868 dest.close(); 869 return false; 870 } 871 872 // [tom, 2/7/2007] CRC checking the lazy man's way 873 // ZipStatFilter only fails if it doesn't have a central directory, so this is safe 874 CentralDir fakeCD; 875 ZipStatFilter zsf(&fakeCD); 876 zsf.attachStream(source); 877 878 bool ret = dest.copyFrom(&zsf); 879 880 zsf.detachStream(); 881 882 if(ret && fakeCD.mCRC32 != realCD->mCRC32) 883 { 884 if(crcFail) 885 *crcFail = true; 886 887 if(isVerbose()) 888 Con::errorf("ZipArchive::extractFile - CRC failure extracting file %s", pathInZip); 889 ret = false; 890 } 891 892 closeFile(source); 893 dest.close(); 894 895 return ret; 896} 897 898bool ZipArchive::deleteFile(const char *filename) 899{ 900 if(mMode != Write && mMode != ReadWrite) 901 return false; 902 903 CentralDir *cd = findFileInfo(filename); 904 if(cd == NULL) 905 return false; 906 907 cd->mInternalFlags |= CDFileDeleted; 908 909 // CodeReview [tom, 2/9/2007] If this is a file we have a temporary file for, 910 // we should probably delete it here rather then waiting til the archive is closed. 911 912 return true; 913} 914 915//----------------------------------------------------------------------------- 916 917bool ZipArchive::isVerbose() 918{ 919 return Con::getBoolVariable("$pref::Zip::Verbose"); 920} 921 922void ZipArchive::setVerbose(bool verbose) 923{ 924 Con::setBoolVariable("$pref::Zip::Verbose", verbose); 925} 926 927} // end namespace Zip 928