00001
00002
00003
00004
00005
00013 #ifndef HZIPFILE_H
00014 # define HZIPFILE_H
00015
00016 #include "HCRC.h"
00017 #include "HFile.h"
00018
00019 #include <zlib/zlib.h>
00020
00021
00022
00034 class HZipFile : public HFile
00035 {
00036 typedef HFile base_class;
00037 static StringPtr ClassName(void) { return "HZipFile"; }
00038
00039 protected:
00040
00041
00042
00043 struct ZipLocalHeader
00044 {
00045 enum {
00046 SIGNATURE = 0x04034b50,
00047 COMP_STORE = 0,
00048 COMP_DEFLAT = 8,
00049 };
00050
00051 uint32 sig;
00052 uint16 version;
00053 uint16 flag;
00054 uint16 compression;
00055 uint16 modTime;
00056 uint16 modDate;
00057 uint32 crc32;
00058 uint32 cSize;
00059 uint32 ucSize;
00060 uint16 fnameLen;
00061 uint16 xtraLen;
00062 } GCC_ONLY(__attribute__((aligned(2),packed)));
00063
00064 struct ZipDirHeader
00065 {
00066 enum { SIGNATURE = 0x06054b50 };
00067 uint32 sig;
00068 uint16 nDisk;
00069 uint16 nStartDisk;
00070 uint16 nDirEntries;
00071 uint16 totalDirEntries;
00072 uint32 dirSize;
00073 uint32 dirOffset;
00074 uint16 cmntLen;
00075 } GCC_ONLY(__attribute__((aligned(2),packed)));
00076
00077 struct ZipDirFileHeader
00078 {
00079 enum {
00080 SIGNATURE = 0x02014b50,
00081 COMP_STORE = 0,
00082 COMP_DEFLAT = 8,
00083 };
00084 uint32 sig;
00085 uint16 verMade;
00086 uint16 verNeeded;
00087 uint16 flag;
00088 uint16 compression;
00089 uint16 modTime;
00090 uint16 modDate;
00091 uint32 crc32;
00092 uint32 cSize;
00093 uint32 ucSize;
00094 uint16 fnameLen;
00095 uint16 xtraLen;
00096 uint16 cmntLen;
00097 uint16 diskStart;
00098 uint16 intAttr;
00099 uint32 extAttr;
00100 uint32 hdrOffset;
00101
00102 StringPtr GetName(void) const { return StringPtr(this + 1); }
00103 StringPtr GetExtra(void) const { return GetName() + fnameLen; }
00104 StringPtr GetComment(void) const { return GetExtra() + xtraLen; }
00105
00106 ZipDirFileHeader *
00107 GetNext(void) const
00108 {
00109 return MAKE_PTR(ZipDirFileHeader *,
00110 this,
00111 sizeof(ZipDirFileHeader) + fnameLen + xtraLen + cmntLen);
00112 }
00113 } GCC_ONLY(__attribute__((aligned(2),packed)));
00114
00115
00116
00117 protected:
00118 int32 m_nEntries;
00119 HMemory m_directory;
00120
00121
00122
00123 protected:
00133 ZipDirFileHeader *
00134 findDir(StringPtr pstrFile) const
00135 {
00136 ZipDirFileHeader * pDir;
00137
00138 if (STR_EMPTY(pstrFile))
00139 {
00140 DEBUG_LOG("HZipFile::findDir() => invalid filename\n");
00141 return NULL;
00142 }
00143
00144 pDir = static_cast<ZipDirFileHeader *>( m_directory.GetPtr() );
00145 ASSERT_PTR(pDir);
00146
00147 for (int32 ndx = 0; GOOD_PTR(pDir) && ndx < m_nEntries; ++ndx)
00148 {
00149 ASSERT(pDir->sig == ZipDirFileHeader::SIGNATURE);
00150
00151
00152
00153
00154 if (MEM_CMP(pstrFile, pDir->GetName(), pDir->fnameLen) == 0)
00155 {
00156 DEBUG_LOG( "HZipFile::findDir(%s) => directory #%ld\n",
00157 pstrFile, ndx);
00158
00159 return pDir;
00160 }
00161
00162 pDir = pDir->GetNext();
00163 }
00164
00165 DEBUG_LOG("HZipFile::findDir() => failed to find \"%s\"\n", pstrFile);
00166 return NULL;
00167 }
00178 ErrCode
00179 inflateData(const ZipLocalHeader & header, HMemory & bufData)
00180 {
00181 ErrCode ec = HError::NoError();
00182 int32 nRC;
00183 uint32 uRead;
00184 HMemory bufRaw;
00185 z_stream stream = { 0x00 };
00186
00187 try
00188 {
00189
00190
00191
00192 if ((ec = bufData.SetSize(header.ucSize)) != NO_ERROR)
00193 throw HError::GetLastError();
00194
00195
00196
00197
00198
00199 if ((ec = bufRaw.SetSize(header.cSize)) != NO_ERROR)
00200 throw HError::GetLastError();
00201
00202 if ((uRead = Read(bufRaw)) != header.cSize)
00203 HError::Throw(ERR_FILE_READ, __FILE__, __LINE__);
00204
00205
00206
00207 stream.next_in = reinterpret_cast<Bytef *>( bufRaw.GetPtr() );
00208 stream.avail_in = bufRaw.GetSize();
00209 stream.next_out = reinterpret_cast<Bytef *>( bufData.GetPtr() );
00210 stream.avail_out = bufData.GetSize();
00211 stream.zalloc = NULL;
00212 stream.zfree = NULL;
00213
00214 ASSERT_PTR(stream.next_in);
00215 ASSERT_PTR(stream.next_out);
00216
00217 if ((nRC = inflateInit2(&stream, -MAX_WBITS)) != Z_OK)
00218 {
00219 DEBUG_LOG("HZipFile::Unzip() => inflateInit2() returned %ld\n", nRC);
00220 HError::Throw(ERR_DATA_INVALID, __FILE__, __LINE__);
00221 }
00222
00223 nRC = inflate(&stream, Z_FINISH);
00224 inflateEnd(&stream);
00225 if (nRC == Z_STREAM_END)
00226 nRC = Z_OK;
00227 inflateEnd(&stream);
00228 }
00229 catch (const std::exception & e)
00230 {
00231 DEBUG_LOG("HZipFile::inflateData() => caught %s\n", e.what());
00232
00233 if ((ec = ErrCode( HError::GetLastError() )) == NO_ERROR)
00234 ec = HError::Set(ERR_STL_EXCEPTION, __FILE__, __LINE__);
00235
00236 (void) bufData.Destroy();
00237 }
00238
00239 return ec;
00240 }
00253 ErrCode
00254 unstoreData(ZipLocalHeader const & header, HMemory & bufData)
00255 {
00256 ErrCode ec = HError::NoError();
00257
00258
00259
00260
00261
00262 if ((ec = bufData.SetSize(header.ucSize)) == NO_ERROR)
00263 {
00264 uint32 uRead = Read(bufData);
00265
00266 if (uRead != header.ucSize)
00267 ec = HError::Set(ERR_FILE_READ, __FILE__, __LINE__);
00268 }
00269
00270 return ec;
00271 }
00272 public:
00278 virtual ErrCode
00279 Close(void)
00280 {
00281 ErrCode ec = HError::NoError();
00282
00283 m_nEntries = 0;
00284 if ((ec = m_directory.Destroy()) == NO_ERROR)
00285 ec = base_class::Close();
00286
00287 return ec;
00288 }
00301 virtual ErrCode
00302 Extract(StringPtr pstrSrc, StringPtr pstrDst = NULL)
00303 {
00304 ErrCode ec = HError::NoError();
00305 HFile fOut;
00306 HMemory bufData;
00307
00308 DEBUG_LOG("HZipFile::Extract(%s)\n", pstrSrc);
00309
00310 try
00311 {
00312 if (STR_EMPTY(pstrSrc))
00313 HError::Throw(ERR_BAD_PARAMETER, __FILE__, __LINE__);
00314
00315 if (STR_EMPTY(pstrDst))
00316 pstrDst = pstrSrc;
00317
00318 if ((ec = UnZip(pstrSrc, bufData)) != NO_ERROR)
00319 throw HError::GetLastError();
00320
00321
00322
00323
00324
00325 if ((ec = fOut.Open(pstrDst, O_BINARY|O_CREAT|O_WRONLY)) != NO_ERROR)
00326 throw HError::GetLastError();
00327
00328 if (fOut.Write(bufData) != bufData.GetSize())
00329 HError::Throw(ERR_FILE_WRITE, __FILE__, __LINE__);
00330
00331 if ((ec = fOut.Close()) != NO_ERROR)
00332 throw HError::GetLastError();
00333
00334 DEBUG_LOG( "HZipFile::Extract() => extracted %s to %s.\n",
00335 pstrSrc, pstrDst);
00336 }
00337 catch (const std::exception & e)
00338 {
00339 DEBUG_LOG("HZipFile::Extract() => caught %s\n", e.what());
00340
00341 if ((ec = ErrCode( HError::GetLastError() )) != NO_ERROR)
00342 ec = HError::Set(ERR_STL_EXCEPTION, __FILE__, __LINE__);
00343 }
00344
00345 return ec;
00346 }
00354 ErrCode
00355 Open(const HFilename & fn, uint32 uFlags)
00356 {
00357 ErrCode ec = HError::NoError();
00358
00359 try
00360 {
00361 if (!fn.IsValid())
00362 HError::Throw(ERR_BAD_PARAMETER, __FILE__, __LINE__);
00366 if (!(uFlags & O_BINARY))
00367 {
00368 DEBUG_LOG( "HZipFile::Open(\"%s\") => forcing O_BINARY flag.\n",
00369 fn.GetPath());
00370 uFlags |= O_BINARY;
00371 }
00372
00373 if ((ec = base_class::Open(fn, uFlags)) != NO_ERROR)
00374 throw HError::GetLastError();
00375
00376
00377
00378 uint32 uRead;
00379 int32 nDirOffset;
00380 ZipDirFileHeader * pDir = NULL;
00381 ZipDirHeader dh = { 0x00 };
00382
00383 nDirOffset = Seek(0, SEEK_END) - sizeof(ZipDirHeader);
00384 nDirOffset = Seek(nDirOffset, SEEK_SET);
00385
00386 uRead = Read(&dh, sizeof(ZipDirHeader));
00387 if (uRead != sizeof(ZipDirHeader))
00388 HError::Throw(ERR_FILE_READ, __FILE__, __LINE__);
00389
00390 if (dh.sig != ZipDirHeader::SIGNATURE)
00391 HError::Throw(ERR_DATA_INVALID, __FILE__, __LINE__);
00392
00393 ASSERT(dh.dirSize <= MB(4U));
00394
00395
00396
00397
00398 Seek(nDirOffset - dh.dirSize, SEEK_SET);
00399 if ((ec = m_directory.SetSize(dh.dirSize)) != NO_ERROR)
00400 throw HError::GetLastError();
00401
00402
00403
00404 uRead = Read(m_directory);
00405 if (uRead != m_directory.GetSize())
00406 HError::Throw(ERR_FILE_READ, __FILE__, __LINE__);
00407
00408 pDir = reinterpret_cast<ZipDirFileHeader *>( m_directory.GetPtr() );
00409 for (m_nEntries = 0; m_nEntries < dh.nDirEntries; ++m_nEntries)
00410 {
00411 if (GOOD_PTR(pDir) && pDir->sig == ZipDirFileHeader::SIGNATURE)
00412 pDir = pDir->GetNext();
00413 else
00414 {
00415 DEBUG_LOG( "HZipFile::Open(%s) => directory #%ld is bad.\n",
00416 StringPtr(fn), m_nEntries);
00417 HError::Throw(ERR_DATA_INVALID, __FILE__, __LINE__);
00418 }
00419 }
00420
00421 DEBUG_LOG( "HZipFile::Open(%s) => contains %ld entr%s.\n",
00422 StringPtr(fn), m_nEntries, ((m_nEntries == 1) ? "y" : "ies"));
00423 }
00424 catch (const std::exception & e)
00425 {
00426 DEBUG_LOG( "HZipFile::Open(%s) => caught %s\n",
00427 StringPtr(fn), e.what());
00428 if ((ec = ErrCode( HError::GetLastError() )) == NO_ERROR)
00429 ec = HError::Set(ERR_STL_EXCEPTION, __FILE__, __LINE__);
00430 }
00431
00432 return ec;
00433 }
00441 ErrCode
00442 UnZip(StringPtr pstrSrc, HMemory & bufOut)
00443 {
00444 ErrCode ec = HError::NoError();
00445 uint32 uRead;
00446 ZipDirFileHeader * pDir = NULL;
00447 ZipLocalHeader header = { 0x00 };
00448
00449 DEBUG_LOG("HZipFile::Unzip(%s)\n", pstrSrc);
00450
00451 try
00452 {
00453 pDir = findDir(pstrSrc);
00454 if (NULL_PTR(pDir))
00455 HError::Throw(ERR_NOT_FOUND, __FILE__, __LINE__);
00456
00457 ASSERT(pDir->cSize <= pDir->ucSize);
00458 ASSERT(pDir->cSize <= MB(8U));
00459 ASSERT(pDir->ucSize <= MB(8U));
00460
00461
00462
00463 (void) Seek(pDir->hdrOffset, SEEK_SET);
00464 uRead = Read(&header, sizeof(ZipLocalHeader));
00465 if (uRead != sizeof(ZipLocalHeader))
00466 HError::Throw(ERR_FILE_READ, __FILE__, __LINE__);
00467 if (header.sig != ZipLocalHeader::SIGNATURE || header.ucSize != pDir->ucSize)
00468 HError::Throw(ERR_DATA_INVALID, __FILE__, __LINE__);
00469
00470 (void) Seek(header.fnameLen + header.xtraLen, SEEK_CUR);
00471
00472 DEBUG_LOG( "HZipFile::UnZip(%s) => cSize=%lu ucSize=%lu.\n",
00473 pstrSrc, header.cSize, header.ucSize);
00474
00475
00476
00477 switch (header.compression)
00478 {
00479 case ZipLocalHeader::COMP_STORE:
00480 if ((ec = unstoreData(header, bufOut)) != NO_ERROR)
00481 throw HError::GetLastError();
00482 break;
00483
00484 case ZipLocalHeader::COMP_DEFLAT:
00485 if ((ec = inflateData(header, bufOut)) != NO_ERROR)
00486 throw HError::GetLastError();
00487 break;
00488
00489 default:
00490 HError::Throw(ERR_NOT_IMPLEMENTED, __FILE__, __LINE__);
00491 }
00492
00493
00494
00495
00496 if (pDir->crc32)
00497 {
00498 uint32 uCRC;
00499
00500 if ((ec = HCRC::Compute(bufOut, uCRC)) != NO_ERROR)
00501 throw HError::GetLastError();
00502
00503 if (uCRC != pDir->crc32)
00504 {
00505 DEBUG_LOG( "HZipFile::UnZip(%s) => got CRC 0x%08lX expected 0x%08lX\n",
00506 pstrSrc, uCRC, pDir->crc32);
00507 HError::Throw(ERR_CRC_FAILURE, __FILE__, __LINE__);
00508 }
00509 }
00510
00511
00512
00513 DEBUG_LOG("HZipFile::UnZip(%s) => ok\n", pstrSrc);
00514 }
00515 catch (const std::exception & e)
00516 {
00517 DEBUG_LOG("HZipFile::UnZip(%s) => caught %s.\n", pstrSrc, e.what());
00518
00519 if ((ec = ErrCode( HError::GetLastError() )) == NO_ERROR)
00520 ec = HError::Set(ERR_STL_EXCEPTION, __FILE__, __LINE__);
00521
00522 (void) bufOut.Destroy();
00523 }
00524
00525 return ec;
00526 }
00527
00528
00529
00530 HZipFile(void) : HFile(),
00531 m_nEntries(0)
00532 { }
00533
00534 virtual
00535 ~HZipFile(void)
00536 { (void) Close(); }
00537 };
00538 #endif // HZIPFILE_H
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548
00549
00550
00551
00552
00553
00554
00555
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565