ZipFile.cpp
上传用户:hmc_gdtv
上传日期:2013-08-04
资源大小:798k
文件大小:11k
源码类别:

Windows Mobile

开发平台:

Visual C++

  1. /*
  2.  * Copyright (c) 2001,2002,2003 Mike Matsnev.  All Rights Reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions
  6.  * are met:
  7.  *
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice immediately at the beginning of the file, without modification,
  10.  *    this list of conditions, and the following disclaimer.
  11.  * 2. Redistributions in binary form must reproduce the above copyright
  12.  *    notice, this list of conditions and the following disclaimer in the
  13.  *    documentation and/or other materials provided with the distribution.
  14.  * 3. Absolutely no warranty of function or purpose is made by the author
  15.  *    Mike Matsnev.
  16.  *
  17.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  18.  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  19.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  20.  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  21.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  22.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27.  * 
  28.  * $Id: ZipFile.cpp,v 1.17.2.8 2004/07/07 12:04:47 mike Exp $
  29.  * 
  30.  */
  31. #include <afx.h>
  32. #include <afxwin.h>
  33. #include "ptr.h"
  34. #include "ZipFile.h"
  35. #include "BufFile.h"
  36. #include "TextViewNG.h"
  37. #include "Unicode.h"
  38. //////////////////////////////////////////////////////////////////////
  39. // Construction/Destruction
  40. //////////////////////////////////////////////////////////////////////
  41. ZipFile::ZipFile(const CString& fn) :
  42.   RFile(fn), m_zs(NULL), m_curfile(NULL), m_curpos(NULL)
  43. {
  44.   m_rootdir.off=-1;
  45.   m_rootdir.children=new CMapStringToPtr;
  46.   m_rootdir.parent=NULL;
  47.   m_curdir=&m_rootdir;
  48.   Reopen();
  49. }
  50. ZipFile::~ZipFile() {
  51.   if (m_zs) {
  52.     inflateEnd(m_zs);
  53.     delete m_zs;
  54.   }
  55. }
  56. ZipFile::ZFile::~ZFile() {
  57.   if (isdir()) { // directory
  58.     CString   key;
  59.     for (POSITION p=children->GetStartPosition();p!=NULL;) {
  60.       void *ptr;
  61.       children->GetNextAssoc(p,key,ptr);
  62.       delete (ZFile*)ptr;
  63.     }
  64.     delete children;
  65.   }
  66. }
  67. void ZipFile::rewind() {
  68.   if (!m_curfile)
  69.     return;
  70.   if (m_ptr==0)
  71.     return;
  72.   RFile::seek(m_curfile->off);
  73.   if (m_curfile->iscmp()) {
  74.     inflateReset(m_zs);
  75.     m_zs->avail_in=0;
  76.   }
  77.   m_ptr=0;
  78. }
  79. void   ZipFile::seek(DWORD pos) {
  80.   if (!m_curfile)
  81.     return;
  82.   pos&=BMASK;
  83.   if (pos>=(DWORD)m_curfile->size) // special case for eof
  84.     m_ptr=m_curfile->size;
  85.   else if (pos==0) // start of file
  86.     rewind();
  87.   else if (m_curfile->iscmp()) { // compressed!
  88.     if (pos!=m_ptr) {// do something
  89.       if (pos<m_ptr) // before current position
  90. rewind();
  91.       Buffer<char>  tmp(BSZ);
  92.       while (pos>m_ptr && read(tmp)==(unsigned)BSZ) ;
  93.     }
  94.   } else {
  95.     m_ptr=pos;
  96.     RFile::seek(m_curfile->off+m_ptr);
  97.   }
  98. }
  99. DWORD   ZipFile::read(void *buf) {
  100.   if (!m_curfile || m_ptr>=(DWORD)m_curfile->size) // invalid file or at eof
  101.     return 0;
  102.   if (!m_curfile->iscmp()) {// uncompressed
  103.     DWORD nr=RFile::read2(buf,min(BSZ,m_curfile->size));
  104.     m_ptr+=nr;
  105.     return nr;
  106.   }
  107.   m_zs->avail_out=BSZ;
  108.   m_zs->next_out=(Bytef*)buf;
  109.   int r;
  110.   while (m_zs->avail_out>0) {
  111.     if (m_zs->avail_in==0) { // fill in next buffer
  112.       m_zs->avail_in=RFile::read2(m_in,BSZ);
  113.       m_zs->next_in=m_in;
  114.     }
  115.     r=inflate(m_zs,Z_SYNC_FLUSH);
  116.     if (r!=Z_OK)
  117.       break;
  118.   }
  119.   m_ptr+=BSZ-m_zs->avail_out;
  120.   return BSZ-m_zs->avail_out;
  121. }
  122. DWORD   ZipFile::size() {
  123.   if (!m_curfile)
  124.     return 0;
  125.   return m_curfile->size;
  126. }
  127. // compression
  128. CString ZipFile::CompressionInfo() {
  129.   if (!m_curfile)
  130.     return _T("No open file");
  131.   if (m_curfile->iscmp() && m_curfile->size>0) {
  132.     CString ret;
  133.     ret.Format(_T("ZIP, deflated (%.2f)"),
  134.       (double)m_curfile->csize/(double)m_curfile->size);
  135.     return ret;
  136.   }
  137.   return _T("ZIP, stored");
  138. }
  139. // zip file specific methods
  140. bool  ZipFile::SetDir(const CString& dir) {
  141.   if (!dir.GetLength())
  142.     return false;
  143.   if (dir==_T("..")) { // up one level
  144.     if (m_curdir->parent) // good
  145.       m_curdir=m_curdir->parent;
  146.     else
  147.       return false;
  148.     return true;
  149.   }
  150.   int     idx=dir[0]==_T('\');
  151.   ZFile     *cur=idx?&m_rootdir:m_curdir;
  152.   while (idx<dir.GetLength()) {
  153.     // find next element
  154.     int     spos=dir.Find(_T('\'),idx);
  155.     CString elem;
  156.     if (spos<0) { // last element
  157.       elem=dir.Mid(idx);
  158.       idx=dir.GetLength();
  159.     } else {
  160.       elem=dir.Mid(idx,spos-idx);
  161.       idx=spos+1;
  162.     }
  163.     void    *ptr;
  164.     if (cur->children->Lookup(elem,ptr)) { // ok, found it
  165.       cur=(ZFile*)ptr;
  166.       if (cur->off>=0) // not a directory
  167. return false;
  168.     } else // dir not found
  169.       return false;;
  170.   }
  171.   m_curdir=cur;
  172.   m_curpos=NULL;
  173.   return true;
  174. }
  175. bool  ZipFile::GetNextFileInfo(CString& fname,bool& isdir,__int64& size) {
  176.   if (m_curpos==NULL)
  177.     return false;
  178.   void *ptr;
  179.   m_curdir->children->GetNextAssoc(m_curpos,fname,ptr);
  180.   isdir=((ZFile*)ptr)->isdir();
  181.   size=((ZFile*)ptr)->size;
  182.   return true;
  183. }
  184. void  ZipFile::Reset() {
  185.   m_curpos=m_curdir->children->GetStartPosition();
  186. }
  187. #define LOCALSIG    0x04034b50
  188. #define DIRSIG     0x02014b50
  189. #pragma pack(push)
  190. #pragma pack(1)
  191. // zip file local header
  192. struct LocalZipHdr {
  193.   DWORD     sig;       // signature
  194.   WORD     extver;       // version needed to extract
  195.   WORD     flags;       // general purpose flag
  196.   WORD     compmeth;       // compression method
  197.   WORD     modtime;       // last modification time
  198.   WORD     moddate;       // last modification date
  199.   DWORD     crc32;
  200.   DWORD     compsize;       // compressed size
  201.   DWORD     usize;       // uncompressed size
  202.   WORD     namelength;       // filename length
  203.   WORD     extralength;      // extra field length
  204.   // filename (variable size)
  205.   // extra field (variable size)
  206. };
  207. #pragma pack(pop)
  208. static DWORD   dword(const DWORD& dw) { // convert from unaligned le-dword
  209.   const BYTE  *b=(const BYTE *)&dw;
  210.   return (DWORD)b[0]|((DWORD)b[1]<<8)|((DWORD)b[2]<<16)|((DWORD)b[3]<<24);
  211. }
  212. static WORD   word(const WORD& w) { // convert from unaligned word
  213.   const BYTE  *b=(const BYTE *)&w;
  214.   return (WORD)b[0]|((WORD)b[1]<<8);
  215. }
  216. // copy the string without _any_ conversion
  217. static CString   rawconv(const Buffer<char>& buf) {
  218.   CString   ret;
  219.   TCHAR     *ptr=ret.GetBuffer(buf.size());
  220.   for (int i=0;i<buf.size();++i)
  221.     ptr[i]=(unsigned char)buf[i];
  222.   ret.ReleaseBuffer(buf.size());
  223.   return ret;
  224. }
  225. bool  ZipFile::ReadZip() {
  226.   int cp=-1;
  227.   int acp=Unicode::GetIntCodePage(::GetACP());
  228.   int oemcp=Unicode::GetIntCodePage(::GetOEMCP());
  229.   // iterate over zip file until we find a central dir
  230.   for (;;) {
  231.     LocalZipHdr   hdr;
  232.     if (RFile::read2(&hdr,sizeof(hdr))!=sizeof(hdr)) // invalid file
  233.       return false;
  234.     if (dword(hdr.sig)==DIRSIG) { // found central directory
  235.       // version here will be used to select codepage
  236.       CString rcp(CTVApp::GetStr(_T("ZipEncoding")));
  237.       if (rcp.GetLength()>0)
  238. cp=Unicode::FindCodePage(rcp);
  239.       if (cp<0) {
  240. BYTE  os=word(hdr.extver)>>8;
  241. if (os==11) // ntfs
  242.   cp=acp;
  243. else if (os==5 && acp==1251) // unix
  244.   cp=Unicode::FindCodePage(_T("koi8-r"));
  245. else
  246.   cp=oemcp;
  247.       }
  248.       if (cp<0)
  249. cp=oemcp;
  250.       break;
  251.     }
  252.     if (dword(hdr.sig)!=LOCALSIG) // invalid zip
  253.       return false;
  254.     // check validity
  255.     if (word(hdr.flags)&0x0004) // unsupported format
  256.       return false;
  257.     if (word(hdr.namelength)==0) // invalid zip
  258.       return false;
  259.     if (word(hdr.compmeth)==0 || word(hdr.compmeth)==8) { // storing and deflating only supported
  260.       Buffer<char>    name(word(hdr.namelength));
  261.       if (RFile::read2(name,word(hdr.namelength))!=word(hdr.namelength)) // invalid zip
  262. return false;
  263.       if (name[name.size()-1]=='/') { // this is a directory, skip it
  264. RFile::seek2(word(hdr.extralength),FILE_CURRENT);
  265. continue;
  266.       }
  267.       // skip over extra field
  268.       RFile::seek2(word(hdr.extralength),FILE_CURRENT);
  269.       // add file to in-memory directory
  270.       // what a PITA. we don't know the codepage yet
  271.       CString       uname(rawconv(name));
  272.       int       idx=0;
  273.       int       end=uname.ReverseFind(_T('/')); // see if we have a path
  274.       ZFile       *cur=&m_rootdir;
  275.       while (idx<end) { // locate directories, creating if needed
  276. int spos=uname.Find(_T('/'),idx);
  277. CString elem;
  278. if (spos<0 || spos>=end) { // this is the last component
  279.   elem=uname.Mid(idx,end-idx);
  280.   idx=end;
  281. } else {
  282.   elem=uname.Mid(idx,spos-idx);
  283.   idx=spos+1;
  284. }
  285. void *ptr;
  286. if (!cur->children->Lookup(elem,ptr)) { // no such dir, create it
  287.   ZFile   *dir=new ZFile;
  288.   dir->parent=cur;
  289.   dir->off=-1;
  290.   dir->children=new CMapStringToPtr;
  291.   cur->children->SetAt(elem,dir);
  292.   ptr=dir;
  293. }
  294. cur=(ZFile*)ptr;
  295. if (cur->off>=0) // invalid zip, we already have a file with this name
  296.   return false;
  297.       }
  298.       void    *ptr;
  299.       uname.Delete(0,end+1);
  300.       if (cur->children->Lookup(uname,ptr)) // invalid zip, we already have this file
  301. return false;
  302.       ZFile   *file=new ZFile;
  303.       file->off=(int)RFile::pos();
  304.       file->size=dword(hdr.usize);
  305.       if (word(hdr.compmeth)==0)
  306. file->csize=-1;
  307.       else
  308. file->csize=dword(hdr.compsize);
  309.       file->parent=cur;
  310.       cur->children->SetAt(uname,file);
  311.       // skip over compressed data
  312.       RFile::seek2(dword(hdr.compsize),FILE_CURRENT);
  313.     } else // skip this file
  314.       RFile::seek2(dword(hdr.compsize)+word(hdr.namelength)+word(hdr.extralength),FILE_CURRENT);
  315.   }
  316.   if (cp<0)
  317.     cp=Unicode::DefaultCodePage();
  318.   m_rootdir.fixup_encoding(cp);
  319.   return true;
  320. }
  321. bool  ZipFile::Open(const CString& filename) {
  322.   void   *ptr;
  323.   if (!m_curdir->children->Lookup(filename,ptr))
  324.     return false;
  325.   m_curfile=(ZFile*)ptr;
  326.   if (m_curfile->iscmp()) {
  327.     if (m_zs)
  328.       inflateReset(m_zs);
  329.     else {
  330.       m_in=Buffer<Bytef>(BSZ);
  331.       m_zs=new z_stream;
  332.       memset(m_zs,0,sizeof(*m_zs));
  333.       inflateInit2(m_zs,-MAX_WBITS);
  334.     }
  335.   }
  336.   m_ptr=0;
  337.   RFile::seek(m_curfile->off);
  338.   return true;
  339. }
  340. bool  ZipFile::IsSingleFile(CString *s) {
  341.   if (m_rootdir.off<0 && m_rootdir.children &&
  342.       m_rootdir.children->GetCount()==1)
  343.   {
  344.     POSITION p=m_rootdir.children->GetStartPosition();
  345.     if (p!=NULL) {
  346.       CString tmp;
  347.       void    *q;
  348.       m_rootdir.children->GetNextAssoc(p,tmp,q);
  349.       ZFile   *f=(ZFile*)q;
  350.       if (f->off>=0) {
  351. if (s)
  352.   *s=tmp;
  353. return true;
  354.       }
  355.     }
  356.   }
  357.   return false;
  358. }
  359. void  ZipFile::ZFile::fixup_encoding(int enc) {
  360.   if (!isdir())
  361.     return;
  362.   CString   fn,rc;
  363.   void   *ptr;
  364.   CMapStringToPtr *map=new CMapStringToPtr;
  365.   for (POSITION pos=children->GetStartPosition();pos;) {
  366.     children->GetNextAssoc(pos,fn,ptr);
  367.     // contrive a char buffer, copy data and convert
  368.     Buffer<char>    tmp(fn.GetLength());
  369.     for (int i=0;i<fn.GetLength();++i)
  370.       tmp[i]=(char)fn[i];
  371.     rc=Unicode::ToCS(enc,tmp,tmp.size());
  372.     map->SetAt(rc,ptr);
  373.     if (((ZFile*)ptr)->isdir())
  374.       ((ZFile*)ptr)->fixup_encoding(enc);
  375.   }
  376.   delete children;
  377.   children=map;
  378. }