Humble Framework for SkyOS


Main Page | Modules | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

HZipFile.h

Go to the documentation of this file.
00001 /****************************************************************************
00002 **
00003 **  $Header: /SkyOS.root/pig/Humble/HZipFile.h 4     2/04/05 10:45a Neusel $
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     HZipFile class -- no implementation file
00022     ----------------------------------------------------------------------  */
00034 class HZipFile : public HFile
00035     {
00036     typedef HFile       base_class;
00037     static StringPtr    ClassName(void) { return "HZipFile"; }
00038 
00039 protected:
00040     //
00041     // ZIP file structures: these have to be packed at WORD (2) boundaries.
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;      // COMP_xxxx
00055         uint16      modTime;
00056         uint16      modDate;
00057         uint32      crc32;
00058         uint32      cSize;
00059         uint32      ucSize;
00060         uint16      fnameLen;         // Filename string follows header.
00061         uint16      xtraLen;          // Extra field follows filename.
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;      // COMP_xxxx
00089         uint16      modTime;
00090         uint16      modDate;
00091         uint32      crc32;
00092         uint32      cSize;            // Compressed size
00093         uint32      ucSize;           // Uncompressed size
00094         uint16      fnameLen;         // Filename string follows header.
00095         uint16      xtraLen;          // Extra field follows filename.
00096         uint16      cmntLen;          // Comment field follows extra field.
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 **  Variables
00116 */
00117 protected:
00118     int32               m_nEntries;     
00119     HMemory             m_directory;    
00120 /*
00121 **  Methods
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             //  Use MEM_CMP() because the directory filename is NOT 
00152             //  zero-terminated.
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             //  Allocate a buffer to hold the uncompressed data
00191             //      
00192             if ((ec = bufData.SetSize(header.ucSize)) != NO_ERROR)
00193                 throw HError::GetLastError();
00194             //
00195             //  Allocate a buffer to hold the compressed data, and then read it in
00196             //  from the file.  (This assumes that the file position is already 
00197             //  positioned at the first byte of compressed data.)
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             // Perform inflation. wbits < 0 indicates no zlib header inside the data.
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();   // nuke any partially uncompressed data
00237             }
00238             
00239         return ec;
00240         }
00253     ErrCode
00254     unstoreData(ZipLocalHeader const & header, HMemory & bufData)
00255         {
00256         ErrCode     ec = HError::NoError();
00257         //
00258         //  Allocate a buffer to hold the uncompressed data, then read it
00259         //  in from the file (assumes the file is already positioned at
00260         //  the start of the uncompressed data stream.
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             //  bufData now contains the uncompressed data, so write it out to 
00322             //  a file.  If the caller didn't specify a different destination 
00323             //  name, use the source name for the file.
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))   // make sure the O_BINARY flag is set
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             //  Assuming no extra comment at the end, read the whole end record
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));   // sanity check
00394             //
00395             //  Go to the beginning of the directory, allocate a buffer and 
00396             //  read in the entire directory.
00397             //
00398             Seek(nDirOffset - dh.dirSize, SEEK_SET);
00399             if ((ec = m_directory.SetSize(dh.dirSize)) != NO_ERROR)
00400                 throw HError::GetLastError();
00401             //
00402             //  Now read in and verify each directory entry.
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));  // sanity check
00459             ASSERT(pDir->ucSize <= MB(8U)); // sanity check
00460             //
00461             //  Go to the actual file and read the local header.
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             //  Allocate the buffer to receive the data
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             //  Now check the CRC.  Under certain circumstances, it is 
00494             //  left as zero in the directory, in which case it is ignored.
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             //  Everything worked as expected...life is good.
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 //  CTOR / DTOR
00529 //
00530     HZipFile(void) :    HFile(), 
00531                         m_nEntries(0)
00532         { /* EMPTY CTOR */ }
00533 
00534     virtual
00535     ~HZipFile(void)
00536         { (void) Close(); }
00537     };
00538 #endif  // HZIPFILE_H
00539 /****************************************************************************
00540 **
00541 **  $History: HZipFile.h $
00542  * 
00543  * *****************  Version 4  *****************
00544  * User: Neusel       Date: 2/04/05    Time: 10:45a
00545  * Updated in $/SkyOS.root/pig/Humble
00546  * 
00547  * *****************  Version 3  *****************
00548  * User: Neusel       Date: 12/08/04   Time: 5:06p
00549  * Updated in $/SkyOS.root/pig/Humble
00550  * 20041208
00551  * 
00552  * *****************  Version 2  *****************
00553  * User: Neusel       Date: 11/30/04   Time: 1:01p
00554  * Updated in $/SkyOS.root/pig/Humble
00555  * Released as HUMBLE_VER 20041130.
00556  * 
00557  * *****************  Version 1  *****************
00558  * User: Neusel       Date: 11/23/04   Time: 8:24a
00559  * Created in $/SkyOS.root/pig/Humble
00560 **
00561 **  -------------------------------------------------------------------------
00562 **
00563 **  End of HZipFile.h
00564 **
00565 ****************************************************************************/