zipArchive.cc 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/platform.h"
  23. #include "io/stream.h"
  24. #include "io/fileStream.h"
  25. #include "io/filterStream.h"
  26. #include "io/zip/zipCryptStream.h"
  27. #include "algorithm/crc.h"
  28. #include "io/resource/resourceManager.h"
  29. #include "console/console.h"
  30. #include "io/zip/zipArchive.h"
  31. #include "io/zip/compressor.h"
  32. #include "io/zip/zipTempStream.h"
  33. #include "io/zip/zipStatFilter.h"
  34. #ifdef TORQUE_ZIP_AES
  35. #include "core/zipAESCryptStream.h"
  36. #include "io/zip/crypto/aes.h"
  37. #endif
  38. #include "memory/safeDelete.h"
  39. namespace Zip
  40. {
  41. //////////////////////////////////////////////////////////////////////////
  42. // Constructor/Destructor
  43. //////////////////////////////////////////////////////////////////////////
  44. ZipArchive::ZipArchive()
  45. {
  46. mStream = NULL;
  47. mMode = Read;
  48. mDiskStream = NULL;
  49. mFilename = NULL;
  50. mRoot = NULL;
  51. }
  52. ZipArchive::~ZipArchive()
  53. {
  54. closeArchive();
  55. }
  56. //////////////////////////////////////////////////////////////////////////
  57. // Protected Methods
  58. //////////////////////////////////////////////////////////////////////////
  59. bool ZipArchive::readCentralDirectory()
  60. {
  61. mEntries.clear();
  62. SAFE_DELETE(mRoot);
  63. mRoot = new ZipEntry;
  64. mRoot->mName = StringTable->EmptyString;
  65. mRoot->mIsDirectory = true;
  66. mRoot->mCD.setFilename("");
  67. if(! mEOCD.findInStream(mStream))
  68. return false;
  69. if(! mEOCD.read(mStream))
  70. return false;
  71. if(mEOCD.mDiskNum != mEOCD.mStartCDDiskNum ||
  72. mEOCD.mNumEntriesInThisCD != mEOCD.mTotalEntriesInCD)
  73. {
  74. if(isVerbose())
  75. Con::errorf("ZipArchive::readCentralDirectory - %s: Zips that span multiple disks are not supported.", mFilename ? mFilename : "<no filename>");
  76. return false;
  77. }
  78. if(! mStream->setPosition(mEOCD.mCDOffset))
  79. return false;
  80. for(S32 i = 0;i < mEOCD.mNumEntriesInThisCD;++i)
  81. {
  82. ZipEntry *ze = new ZipEntry;
  83. if(! ze->mCD.read(mStream))
  84. {
  85. delete ze;
  86. if(isVerbose())
  87. Con::errorf("ZipArchive::readCentralDirectory - %s: Error reading central directory.", mFilename ? mFilename : "<no filename>");
  88. return false;
  89. }
  90. insertEntry(ze);
  91. }
  92. return true;
  93. }
  94. //////////////////////////////////////////////////////////////////////////
  95. void ZipArchive::insertEntry(ZipEntry *ze)
  96. {
  97. char path[1024];
  98. dStrncpy(path, ze->mCD.mFilename, sizeof(path));
  99. path[sizeof(path) - 1] = 0;
  100. for(U32 i = 0;i < dStrlen(path);++i)
  101. {
  102. if(path[i] == '\\')
  103. path[i] = '/';
  104. }
  105. ZipEntry *root = mRoot;
  106. char *ptr = path, *slash = NULL;
  107. do
  108. {
  109. slash = dStrchr(ptr, '/');
  110. if(slash)
  111. {
  112. // Add the directory
  113. *slash = 0;
  114. ZipEntry *newEntry;
  115. if((newEntry = root->mChildren.retrieve(ptr)) == NULL)
  116. {
  117. newEntry = new ZipEntry;
  118. newEntry->mParent = root;
  119. newEntry->mName = StringTable->insert(ptr, true);
  120. newEntry->mIsDirectory = true;
  121. newEntry->mCD.setFilename(path);
  122. root->mChildren.insert(newEntry, ptr);
  123. }
  124. root = newEntry;
  125. *slash = '/';
  126. ptr = slash + 1;
  127. }
  128. else
  129. {
  130. // Add the file.
  131. if(*ptr)
  132. {
  133. ze->mIsDirectory = false;
  134. ze->mName = StringTable->insert(ptr, true);
  135. ze->mParent = root;
  136. root->mChildren.insert(ze, ptr);
  137. mEntries.push_back(ze);
  138. }
  139. else
  140. {
  141. // [tom, 2/6/2007] If ptr is empty, this was a directory entry. Since
  142. // we created a new entry for it above, we need to delete the old
  143. // pointer otherwise it will leak as it won't have got inserted.
  144. delete ze;
  145. }
  146. }
  147. } while(slash);
  148. }
  149. void ZipArchive::removeEntry(ZipEntry *ze)
  150. {
  151. if(ze == mRoot)
  152. {
  153. // [tom, 2/1/2007] We don't want to remove the root as it should always
  154. // be removed through closeArchive()
  155. AssertFatal(0, "ZipArchive::removeEntry - Attempting to remove the root");
  156. return;
  157. }
  158. // Can't iterate the hash table, so we can't do this safely
  159. AssertFatal(!ze->mIsDirectory, "ZipArchive::removeEntry - Cannot remove a directory");
  160. // See if we have a temporary file for this entry
  161. VectorPtr<ZipTempStream *>::iterator i;
  162. for(i = mTempFiles.begin();i != mTempFiles.end();++i)
  163. {
  164. if((*i)->getCentralDir() == &ze->mCD)
  165. {
  166. SAFE_DELETE(*i);
  167. mTempFiles.erase(i);
  168. break;
  169. }
  170. }
  171. // Remove from the tree
  172. VectorPtr<ZipEntry *>::iterator j;
  173. for(j = mEntries.begin();j != mEntries.end();++j)
  174. {
  175. if(*j == ze)
  176. {
  177. mEntries.erase(j);
  178. break;
  179. }
  180. }
  181. // [tom, 2/2/2007] This must be last, as ze is no longer valid once it's
  182. // removed from the parent.
  183. ZipEntry *z = ze->mParent->mChildren.remove(ze->mName);
  184. delete z;
  185. }
  186. //////////////////////////////////////////////////////////////////////////
  187. CentralDir *ZipArchive::findFileInfo(const char *filename)
  188. {
  189. ZipEntry *ze = findZipEntry(filename);
  190. return ze ? &ze->mCD : NULL;
  191. }
  192. ZipArchive::ZipEntry *ZipArchive::findZipEntry(const char *filename)
  193. {
  194. char path[1024];
  195. dStrncpy(path, filename, sizeof(path));
  196. path[sizeof(path) - 1] = 0;
  197. for(U32 i = 0;i < dStrlen(path);++i)
  198. {
  199. if(path[i] == '\\')
  200. path[i] = '/';
  201. }
  202. ZipEntry *root = mRoot;
  203. char *ptr = path, *slash = NULL;
  204. do
  205. {
  206. slash = dStrchr(ptr, '/');
  207. if(slash)
  208. {
  209. // Find the directory
  210. *slash = 0;
  211. ZipEntry *newEntry;
  212. if((newEntry = root->mChildren.retrieve(ptr)) == NULL)
  213. return NULL;
  214. root = newEntry;
  215. ptr = slash + 1;
  216. }
  217. else
  218. {
  219. // Find the file
  220. ZipEntry *ze;
  221. if((ze = root->mChildren.retrieve(ptr)) != NULL)
  222. return ze;
  223. }
  224. } while(slash);
  225. return NULL;
  226. }
  227. //////////////////////////////////////////////////////////////////////////
  228. Stream *ZipArchive::createNewFile(const char *filename, Compressor *method)
  229. {
  230. ZipEntry *ze = new ZipEntry;
  231. ze->mIsDirectory = false;
  232. ze->mCD.setFilename(filename);
  233. insertEntry(ze);
  234. ZipTempStream *stream = new ZipTempStream(&ze->mCD);
  235. if(stream->open())
  236. {
  237. Stream *retStream = method->createWriteStream(&ze->mCD, stream);
  238. if(retStream == NULL)
  239. {
  240. delete stream;
  241. return NULL;
  242. }
  243. ZipStatFilter *filter = new ZipStatFilter(&ze->mCD);
  244. if(! filter->attachStream(retStream))
  245. {
  246. delete filter;
  247. delete retStream;
  248. delete stream;
  249. return NULL;
  250. }
  251. ze->mCD.mCompressMethod = method->getMethod();
  252. ze->mCD.mInternalFlags |= CDFileOpen;
  253. return filter;
  254. }
  255. return NULL;
  256. }
  257. void ZipArchive::updateFile(ZipTempStream *stream)
  258. {
  259. CentralDir *cd = stream->getCentralDir();
  260. // [tom, 1/23/2007] Uncompressed size and CRC32 are updated by ZipStatFilter
  261. cd->mCompressedSize = stream->getStreamSize();
  262. cd->mInternalFlags |= CDFileDirty;
  263. cd->mInternalFlags &= ~CDFileOpen;
  264. // Upper byte should be zero, lower is version as major * 10 + minor
  265. cd->mVersionMadeBy = (TORQUE_GAME_ENGINE / 100) & 0xff;
  266. cd->mExtractVer = 20;
  267. U32 dosTime = currentTimeToDOSTime();
  268. cd->mModTime = dosTime & 0x0000ffff;
  269. cd->mModDate = (dosTime & 0xffff0000) >> 16;
  270. mTempFiles.push_back(stream);
  271. }
  272. //////////////////////////////////////////////////////////////////////////
  273. U32 ZipArchive::localTimeToDOSTime(const Platform::LocalTime &t)
  274. {
  275. U16 year = t.year;
  276. if(year > 80)
  277. year -= 80;
  278. return (((t.monthday) + (32 * (t.month+1)) + (512 * year)) << 16) | ((t.sec/2) + (32* t.min) + (2048 * (U32)t.hour));
  279. }
  280. U32 ZipArchive::currentTimeToDOSTime()
  281. {
  282. Platform::LocalTime t;
  283. Platform::getLocalTime(t);
  284. return localTimeToDOSTime(t);
  285. }
  286. //////////////////////////////////////////////////////////////////////////
  287. // [tom, 1/24/2007] The general idea here is we want to create a new file,
  288. // copy any data from the old zip file and add the new stuff. Once the new
  289. // zip is created, delete the old one and rename the new one.
  290. bool ZipArchive::rebuildZip()
  291. {
  292. char newZipName[1024];
  293. FileStream tempFile;
  294. Stream *zipFile = mStream;
  295. // FIXME [tom, 1/24/2007] Temporary for expediting testing
  296. if(mFilename == NULL)
  297. return false;
  298. if(mMode == ReadWrite)
  299. {
  300. dSprintf(newZipName, sizeof(newZipName), "%s.new", mFilename);
  301. if(! tempFile.open(newZipName, mMode == Write ? FileStream::Write : FileStream::ReadWrite))
  302. return false;
  303. zipFile = &tempFile;
  304. }
  305. // Copy any unmodified files
  306. for(S32 i = 0;i < mEntries.size();++i)
  307. {
  308. ZipEntry *entry = mEntries[i];
  309. // [tom, 1/24/2007] Directories are internal only for lookup purposes
  310. if( entry->mIsDirectory || ((entry->mCD.mInternalFlags & (CDFileDirty | CDFileDeleted)) != 0) )
  311. continue;
  312. copyFileToNewZip(&entry->mCD, zipFile);
  313. }
  314. // Copy any dirty files
  315. for(S32 i = 0;i < mTempFiles.size();++i)
  316. {
  317. ZipTempStream *zts = mTempFiles[i];
  318. writeDirtyFileToNewZip(zts, zipFile);
  319. zts->close();
  320. delete zts;
  321. mTempFiles[i] = NULL;
  322. }
  323. mTempFiles.clear();
  324. // Write central directory
  325. mEOCD.mCDOffset = zipFile->getPosition();
  326. mEOCD.mNumEntriesInThisCD = 0;
  327. for(S32 i = 0;i < mEntries.size();++i)
  328. {
  329. ZipEntry *entry = mEntries[i];
  330. // [tom, 1/24/2007] Directories are internal only for lookup purposes
  331. if(entry->mIsDirectory || (entry->mCD.mInternalFlags & CDFileDeleted) != 0)
  332. continue;
  333. ++mEOCD.mNumEntriesInThisCD;
  334. if(! entry->mCD.write(zipFile))
  335. break;
  336. }
  337. mEOCD.mCDSize = zipFile->getPosition() - mEOCD.mCDOffset;
  338. mEOCD.mTotalEntriesInCD = mEOCD.mNumEntriesInThisCD;
  339. mEOCD.mDiskNum = 0;
  340. mEOCD.mStartCDDiskNum = 0;
  341. mEOCD.write(zipFile);
  342. if(mMode == ReadWrite)
  343. {
  344. // Close file, replace old zip with it
  345. tempFile.close();
  346. // [tom, 2/1/2007] The disk stream must be closed else we can't rename
  347. // the file. Since rebuildZip() is only called from closeArchive() this
  348. // should be safe.
  349. if(mDiskStream)
  350. {
  351. mDiskStream->close();
  352. delete mDiskStream;
  353. mDiskStream = NULL;
  354. }
  355. char oldRename[512];
  356. dSprintf(oldRename, sizeof(oldRename), "%s.old", mFilename);
  357. if(! Platform::fileRename(mFilename, oldRename))
  358. return false;
  359. if(! Platform::fileRename(newZipName, mFilename))
  360. return false;
  361. Platform::fileDelete(oldRename);
  362. }
  363. return true;
  364. }
  365. bool ZipArchive::writeDirtyFileToNewZip(ZipTempStream *fileStream, Stream *zipStream)
  366. {
  367. CentralDir *cdir = fileStream->getCentralDir();
  368. FileHeader fh(*cdir);
  369. fh.mFilename = NULL;
  370. fh.setFilename(cdir->mFilename);
  371. cdir->mLocalHeadOffset = zipStream->getPosition();
  372. // Write header and file
  373. if(! fh.write(zipStream))
  374. return false;
  375. if(! fileStream->rewind())
  376. return false;
  377. return zipStream->copyFrom(fileStream);
  378. }
  379. bool ZipArchive::copyFileToNewZip(CentralDir *cdir, Stream *newZipStream)
  380. {
  381. // [tom, 1/24/2007] Using the stored compressor allows us to copy the raw
  382. // data regardless of compression method without having to re-compress it.
  383. Compressor *comp = Compressor::findCompressor(Stored);
  384. if(comp == NULL)
  385. return false;
  386. if(! mStream->setPosition(cdir->mLocalHeadOffset))
  387. return false;
  388. // Copy file header
  389. // FIXME [tom, 1/24/2007] This will currently not copy the extra fields
  390. FileHeader fh;
  391. if(! fh.read(mStream))
  392. return false;
  393. cdir->mLocalHeadOffset = newZipStream->getPosition();
  394. if(! fh.write(newZipStream))
  395. return false;
  396. // Copy file data
  397. Stream *readS = comp->createReadStream(cdir, mStream);
  398. if(readS == NULL)
  399. return false;
  400. bool ret = newZipStream->copyFrom(readS);
  401. // [tom, 1/24/2007] closeFile() just frees the relevant filters and
  402. // thus it is safe to call from here.
  403. closeFile(readS);
  404. return ret;
  405. }
  406. //////////////////////////////////////////////////////////////////////////
  407. // Public Methods
  408. //////////////////////////////////////////////////////////////////////////
  409. void ZipArchive::setFilename(const char *filename)
  410. {
  411. SAFE_FREE(mFilename);
  412. if(filename)
  413. mFilename = dStrdup(filename);
  414. }
  415. //////////////////////////////////////////////////////////////////////////
  416. bool ZipArchive::openArchive(const char *filename, AccessMode mode /* = Read */)
  417. {
  418. if(mode != Read && mode != Write && mode != ReadWrite)
  419. return false;
  420. closeArchive();
  421. mDiskStream = new FileStream;
  422. if(mDiskStream->open(filename, (FileStream::AccessMode)mode))
  423. {
  424. setFilename(filename);
  425. if(openArchive(mDiskStream, mode))
  426. return true;
  427. }
  428. // Cleanup just in case openArchive() failed
  429. closeArchive();
  430. return false;
  431. }
  432. bool ZipArchive::openArchive(Stream *stream, AccessMode mode /* = Read */)
  433. {
  434. if(mode != Read && mode != Write && mode != ReadWrite)
  435. return false;
  436. mStream = stream;
  437. mMode = mode;
  438. if(mode == Read || mode == ReadWrite)
  439. {
  440. bool ret = readCentralDirectory();
  441. if(mode == Read)
  442. return ret;
  443. return true;
  444. }
  445. else
  446. {
  447. mEntries.clear();
  448. SAFE_DELETE(mRoot);
  449. mRoot = new ZipEntry;
  450. mRoot->mName = StringTable->EmptyString;
  451. mRoot->mIsDirectory = true;
  452. mRoot->mCD.setFilename("");
  453. }
  454. return true;
  455. }
  456. void ZipArchive::closeArchive()
  457. {
  458. if(mMode == Write || mMode == ReadWrite)
  459. rebuildZip();
  460. // Free any remaining temporary files
  461. for(S32 i = 0;i < mTempFiles.size();++i)
  462. {
  463. SAFE_DELETE(mTempFiles[i]);
  464. }
  465. mTempFiles.clear();
  466. // Close the zip file stream and clean up
  467. if(mDiskStream)
  468. {
  469. mDiskStream->close();
  470. delete mDiskStream;
  471. mDiskStream = NULL;
  472. }
  473. mStream = NULL;
  474. SAFE_FREE(mFilename);
  475. SAFE_DELETE(mRoot);
  476. mEntries.clear();
  477. }
  478. //////////////////////////////////////////////////////////////////////////
  479. Stream * ZipArchive::openFile(const char *filename, AccessMode mode /* = Read */)
  480. {
  481. ZipEntry *ze = findZipEntry(filename);
  482. if(mode == Read)
  483. {
  484. if(ze == NULL)
  485. return NULL;
  486. return openFileForRead(&ze->mCD);
  487. }
  488. if(mode == Write)
  489. {
  490. if(ze)
  491. {
  492. if(ze->mCD.mInternalFlags & CDFileOpen)
  493. {
  494. if(isVerbose())
  495. Con::errorf("ZipArchive::openFile - File %s is already open", filename);
  496. return NULL;
  497. }
  498. // Remove the old entry so we can create a new one
  499. removeEntry(ze);
  500. ze = NULL;
  501. }
  502. return createNewFile(filename, Compressor::findCompressor(Deflated));
  503. }
  504. if(isVerbose())
  505. Con::errorf("ZipArchive::openFile - Files within zips can only be opened as read or write, but not both at the same time.");
  506. return NULL;
  507. }
  508. void ZipArchive::closeFile(Stream *stream)
  509. {
  510. FilterStream *currentStream, *nextStream;
  511. nextStream = dynamic_cast<FilterStream*>(stream);
  512. while (nextStream)
  513. {
  514. currentStream = nextStream;
  515. stream = currentStream->getStream();
  516. currentStream->detachStream();
  517. nextStream = dynamic_cast<FilterStream*>(stream);
  518. delete currentStream;
  519. }
  520. ZipTempStream *tempStream = dynamic_cast<ZipTempStream *>(stream);
  521. if(tempStream && (tempStream->getCentralDir()->mInternalFlags & CDFileOpen))
  522. {
  523. // [tom, 1/23/2007] This is a temporary file we are writing to
  524. // so we need to update the relevant information in the header.
  525. updateFile(tempStream);
  526. }
  527. }
  528. //////////////////////////////////////////////////////////////////////////
  529. Stream *ZipArchive::openFileForRead(const CentralDir *fileCD)
  530. {
  531. if(mMode != Read && mMode != ReadWrite)
  532. return NULL;
  533. if((fileCD->mInternalFlags & (CDFileDeleted | CDFileOpen)) != 0)
  534. return NULL;
  535. Stream *stream = mStream;
  536. if(fileCD->mInternalFlags & CDFileDirty)
  537. {
  538. // File is dirty, we need to read from the temporary file
  539. for(S32 i = 0;i < mTempFiles.size();++i)
  540. {
  541. if(mTempFiles[i]->getCentralDir() == fileCD)
  542. {
  543. // Found the temporary file
  544. if(! mTempFiles[i]->rewind())
  545. {
  546. if(isVerbose())
  547. Con::errorf("ZipArchive::openFile - %s: %s is dirty, but could not rewind temporary file?", mFilename ? mFilename : "<no filename>", fileCD->mFilename);
  548. return NULL;
  549. }
  550. stream = mTempFiles[i];
  551. break;
  552. }
  553. }
  554. if(stream == mStream)
  555. {
  556. if(isVerbose())
  557. Con::errorf("ZipArchive::openFile - %s: %s is dirty, but no temporary file found?", mFilename ? mFilename : "<no filename>", fileCD->mFilename);
  558. return NULL;
  559. }
  560. }
  561. else
  562. {
  563. // Read from the zip file directly
  564. if(! mStream->setPosition(fileCD->mLocalHeadOffset))
  565. {
  566. if(isVerbose())
  567. Con::errorf("ZipArchive::openFile - %s: Could not locate local header for file %s", mFilename ? mFilename : "<no filename>", fileCD->mFilename);
  568. return NULL;
  569. }
  570. FileHeader fh;
  571. if(! fh.read(mStream))
  572. {
  573. if(isVerbose())
  574. Con::errorf("ZipArchive::openFile - %s: Could not read local header for file %s", mFilename ? mFilename : "<no filename>", fileCD->mFilename);
  575. return NULL;
  576. }
  577. }
  578. Stream *attachTo = stream;
  579. U16 compMethod = fileCD->mCompressMethod;
  580. if(fileCD->mFlags & Encrypted)
  581. {
  582. if(fileCD->mCompressMethod == AESEncrypted)
  583. {
  584. // [tom, 1/19/2007] Whilst AES support does exist, I'm not including it in TGB
  585. // to avoid having to deal with crypto export legal issues.
  586. Con::errorf("ZipArchive::openFile - %s: File %s is AES encrypted, but AES is not supported in this version.", mFilename ? mFilename : "<no filename>", fileCD->mFilename);
  587. }
  588. else
  589. {
  590. ZipCryptRStream *cryptStream = new ZipCryptRStream;
  591. cryptStream->setPassword(DEFAULT_ZIP_PASSWORD);
  592. cryptStream->setFileEndPos(stream->getPosition() + fileCD->mCompressedSize);
  593. if(! cryptStream->attachStream(stream))
  594. {
  595. delete cryptStream;
  596. return NULL;
  597. }
  598. attachTo = cryptStream;
  599. }
  600. }
  601. Compressor *comp = Compressor::findCompressor(compMethod);
  602. if(comp == NULL)
  603. {
  604. if(isVerbose())
  605. Con::errorf("ZipArchive::openFile - %s: Unsupported compression method (%d) for file %s", mFilename ? mFilename : "<no filename>", fileCD->mCompressMethod, fileCD->mFilename);
  606. return NULL;
  607. }
  608. return comp->createReadStream(fileCD, attachTo);
  609. }
  610. //////////////////////////////////////////////////////////////////////////
  611. bool ZipArchive::addFile(const char *filename, const char *pathInZip, bool replace /* = true */)
  612. {
  613. Stream *source = ResourceManager->openStream(filename);
  614. if(source == NULL)
  615. return false;
  616. const CentralDir *cd = findFileInfo(pathInZip);
  617. if(! replace && cd && (cd->mInternalFlags & CDFileDeleted) == 0)
  618. return false;
  619. Stream *dest = openFile(pathInZip, Write);
  620. if(dest == NULL)
  621. {
  622. ResourceManager->closeStream(source);
  623. return false;
  624. }
  625. bool ret = dest->copyFrom(source);
  626. closeFile(dest);
  627. ResourceManager->closeStream(source);
  628. return ret;
  629. }
  630. bool ZipArchive::extractFile(const char *pathInZip, const char *filename, bool *crcFail /* = NULL */)
  631. {
  632. if(crcFail)
  633. *crcFail = false;
  634. const CentralDir *realCD = findFileInfo(pathInZip);
  635. if(realCD == NULL)
  636. return false;
  637. FileStream dest;
  638. if(! ResourceManager->openFileForWrite(dest, filename))
  639. return false;
  640. Stream *source = openFile(pathInZip, Read);
  641. if(source == NULL)
  642. {
  643. dest.close();
  644. return false;
  645. }
  646. // [tom, 2/7/2007] CRC checking the lazy man's way
  647. // ZipStatFilter only fails if it doesn't have a central directory, so this is safe
  648. CentralDir fakeCD;
  649. ZipStatFilter zsf(&fakeCD);
  650. zsf.attachStream(source);
  651. bool ret = dest.copyFrom(&zsf);
  652. zsf.detachStream();
  653. if(ret && fakeCD.mCRC32 != realCD->mCRC32)
  654. {
  655. if(crcFail)
  656. *crcFail = true;
  657. if(isVerbose())
  658. Con::errorf("ZipArchive::extractFile - CRC failure extracting file %s", pathInZip);
  659. ret = false;
  660. }
  661. closeFile(source);
  662. dest.close();
  663. return ret;
  664. }
  665. bool ZipArchive::deleteFile(const char *filename)
  666. {
  667. if(mMode != Write && mMode != ReadWrite)
  668. return false;
  669. CentralDir *cd = findFileInfo(filename);
  670. if(cd == NULL)
  671. return false;
  672. cd->mInternalFlags |= CDFileDeleted;
  673. // CodeReview [tom, 2/9/2007] If this is a file we have a temporary file for,
  674. // we should probably delete it here rather then waiting til the archive is closed.
  675. return true;
  676. }
  677. //////////////////////////////////////////////////////////////////////////
  678. bool ZipArchive::isVerbose()
  679. {
  680. return Con::getBoolVariable("$Pref::Zip::Verbose");
  681. }
  682. void ZipArchive::setVerbose(bool verbose)
  683. {
  684. Con::setBoolVariable("$Pref::Zip::Verbose", verbose);
  685. }
  686. } // end namespace Zip