📄 zipfile.cpp
字号:
/*
* Copyright (c) 2001,2002,2003 Mike Matsnev. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice immediately at the beginning of the file, without modification,
* this list of conditions, and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Absolutely no warranty of function or purpose is made by the author
* Mike Matsnev.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $Id: ZipFile.cpp,v 1.17.2.8 2004/07/07 12:04:47 mike Exp $
*
*/
#include <afx.h>
#include <afxwin.h>
#include "ptr.h"
#include "ZipFile.h"
#include "BufFile.h"
#include "TextViewNG.h"
#include "Unicode.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
ZipFile::ZipFile(const CString& fn) :
RFile(fn), m_zs(NULL), m_curfile(NULL), m_curpos(NULL)
{
m_rootdir.off=-1;
m_rootdir.children=new CMapStringToPtr;
m_rootdir.parent=NULL;
m_curdir=&m_rootdir;
Reopen();
}
ZipFile::~ZipFile() {
if (m_zs) {
inflateEnd(m_zs);
delete m_zs;
}
}
ZipFile::ZFile::~ZFile() {
if (isdir()) { // directory
CString key;
for (POSITION p=children->GetStartPosition();p!=NULL;) {
void *ptr;
children->GetNextAssoc(p,key,ptr);
delete (ZFile*)ptr;
}
delete children;
}
}
void ZipFile::rewind() {
if (!m_curfile)
return;
if (m_ptr==0)
return;
RFile::seek(m_curfile->off);
if (m_curfile->iscmp()) {
inflateReset(m_zs);
m_zs->avail_in=0;
}
m_ptr=0;
}
void ZipFile::seek(DWORD pos) {
if (!m_curfile)
return;
pos&=BMASK;
if (pos>=(DWORD)m_curfile->size) // special case for eof
m_ptr=m_curfile->size;
else if (pos==0) // start of file
rewind();
else if (m_curfile->iscmp()) { // compressed!
if (pos!=m_ptr) {// do something
if (pos<m_ptr) // before current position
rewind();
Buffer<char> tmp(BSZ);
while (pos>m_ptr && read(tmp)==(unsigned)BSZ) ;
}
} else {
m_ptr=pos;
RFile::seek(m_curfile->off+m_ptr);
}
}
DWORD ZipFile::read(void *buf) {
if (!m_curfile || m_ptr>=(DWORD)m_curfile->size) // invalid file or at eof
return 0;
if (!m_curfile->iscmp()) {// uncompressed
DWORD nr=RFile::read2(buf,min(BSZ,m_curfile->size));
m_ptr+=nr;
return nr;
}
m_zs->avail_out=BSZ;
m_zs->next_out=(Bytef*)buf;
int r;
while (m_zs->avail_out>0) {
if (m_zs->avail_in==0) { // fill in next buffer
m_zs->avail_in=RFile::read2(m_in,BSZ);
m_zs->next_in=m_in;
}
r=inflate(m_zs,Z_SYNC_FLUSH);
if (r!=Z_OK)
break;
}
m_ptr+=BSZ-m_zs->avail_out;
return BSZ-m_zs->avail_out;
}
DWORD ZipFile::size() {
if (!m_curfile)
return 0;
return m_curfile->size;
}
// compression
CString ZipFile::CompressionInfo() {
if (!m_curfile)
return _T("No open file");
if (m_curfile->iscmp() && m_curfile->size>0) {
CString ret;
ret.Format(_T("ZIP, deflated (%.2f)"),
(double)m_curfile->csize/(double)m_curfile->size);
return ret;
}
return _T("ZIP, stored");
}
// zip file specific methods
bool ZipFile::SetDir(const CString& dir) {
if (!dir.GetLength())
return false;
if (dir==_T("..")) { // up one level
if (m_curdir->parent) // good
m_curdir=m_curdir->parent;
else
return false;
return true;
}
int idx=dir[0]==_T('\\');
ZFile *cur=idx?&m_rootdir:m_curdir;
while (idx<dir.GetLength()) {
// find next element
int spos=dir.Find(_T('\\'),idx);
CString elem;
if (spos<0) { // last element
elem=dir.Mid(idx);
idx=dir.GetLength();
} else {
elem=dir.Mid(idx,spos-idx);
idx=spos+1;
}
void *ptr;
if (cur->children->Lookup(elem,ptr)) { // ok, found it
cur=(ZFile*)ptr;
if (cur->off>=0) // not a directory
return false;
} else // dir not found
return false;;
}
m_curdir=cur;
m_curpos=NULL;
return true;
}
bool ZipFile::GetNextFileInfo(CString& fname,bool& isdir,__int64& size) {
if (m_curpos==NULL)
return false;
void *ptr;
m_curdir->children->GetNextAssoc(m_curpos,fname,ptr);
isdir=((ZFile*)ptr)->isdir();
size=((ZFile*)ptr)->size;
return true;
}
void ZipFile::Reset() {
m_curpos=m_curdir->children->GetStartPosition();
}
#define LOCALSIG 0x04034b50
#define DIRSIG 0x02014b50
#pragma pack(push)
#pragma pack(1)
// zip file local header
struct LocalZipHdr {
DWORD sig; // signature
WORD extver; // version needed to extract
WORD flags; // general purpose flag
WORD compmeth; // compression method
WORD modtime; // last modification time
WORD moddate; // last modification date
DWORD crc32;
DWORD compsize; // compressed size
DWORD usize; // uncompressed size
WORD namelength; // filename length
WORD extralength; // extra field length
// filename (variable size)
// extra field (variable size)
};
#pragma pack(pop)
static DWORD dword(const DWORD& dw) { // convert from unaligned le-dword
const BYTE *b=(const BYTE *)&dw;
return (DWORD)b[0]|((DWORD)b[1]<<8)|((DWORD)b[2]<<16)|((DWORD)b[3]<<24);
}
static WORD word(const WORD& w) { // convert from unaligned word
const BYTE *b=(const BYTE *)&w;
return (WORD)b[0]|((WORD)b[1]<<8);
}
// copy the string without _any_ conversion
static CString rawconv(const Buffer<char>& buf) {
CString ret;
TCHAR *ptr=ret.GetBuffer(buf.size());
for (int i=0;i<buf.size();++i)
ptr[i]=(unsigned char)buf[i];
ret.ReleaseBuffer(buf.size());
return ret;
}
bool ZipFile::ReadZip() {
int cp=-1;
int acp=Unicode::GetIntCodePage(::GetACP());
int oemcp=Unicode::GetIntCodePage(::GetOEMCP());
// iterate over zip file until we find a central dir
for (;;) {
LocalZipHdr hdr;
if (RFile::read2(&hdr,sizeof(hdr))!=sizeof(hdr)) // invalid file
return false;
if (dword(hdr.sig)==DIRSIG) { // found central directory
// version here will be used to select codepage
CString rcp(CTVApp::GetStr(_T("ZipEncoding")));
if (rcp.GetLength()>0)
cp=Unicode::FindCodePage(rcp);
if (cp<0) {
BYTE os=word(hdr.extver)>>8;
if (os==11) // ntfs
cp=acp;
else if (os==5 && acp==1251) // unix
cp=Unicode::FindCodePage(_T("koi8-r"));
else
cp=oemcp;
}
if (cp<0)
cp=oemcp;
break;
}
if (dword(hdr.sig)!=LOCALSIG) // invalid zip
return false;
// check validity
if (word(hdr.flags)&0x0004) // unsupported format
return false;
if (word(hdr.namelength)==0) // invalid zip
return false;
if (word(hdr.compmeth)==0 || word(hdr.compmeth)==8) { // storing and deflating only supported
Buffer<char> name(word(hdr.namelength));
if (RFile::read2(name,word(hdr.namelength))!=word(hdr.namelength)) // invalid zip
return false;
if (name[name.size()-1]=='/') { // this is a directory, skip it
RFile::seek2(word(hdr.extralength),FILE_CURRENT);
continue;
}
// skip over extra field
RFile::seek2(word(hdr.extralength),FILE_CURRENT);
// add file to in-memory directory
// what a PITA. we don't know the codepage yet
CString uname(rawconv(name));
int idx=0;
int end=uname.ReverseFind(_T('/')); // see if we have a path
ZFile *cur=&m_rootdir;
while (idx<end) { // locate directories, creating if needed
int spos=uname.Find(_T('/'),idx);
CString elem;
if (spos<0 || spos>=end) { // this is the last component
elem=uname.Mid(idx,end-idx);
idx=end;
} else {
elem=uname.Mid(idx,spos-idx);
idx=spos+1;
}
void *ptr;
if (!cur->children->Lookup(elem,ptr)) { // no such dir, create it
ZFile *dir=new ZFile;
dir->parent=cur;
dir->off=-1;
dir->children=new CMapStringToPtr;
cur->children->SetAt(elem,dir);
ptr=dir;
}
cur=(ZFile*)ptr;
if (cur->off>=0) // invalid zip, we already have a file with this name
return false;
}
void *ptr;
uname.Delete(0,end+1);
if (cur->children->Lookup(uname,ptr)) // invalid zip, we already have this file
return false;
ZFile *file=new ZFile;
file->off=(int)RFile::pos();
file->size=dword(hdr.usize);
if (word(hdr.compmeth)==0)
file->csize=-1;
else
file->csize=dword(hdr.compsize);
file->parent=cur;
cur->children->SetAt(uname,file);
// skip over compressed data
RFile::seek2(dword(hdr.compsize),FILE_CURRENT);
} else // skip this file
RFile::seek2(dword(hdr.compsize)+word(hdr.namelength)+word(hdr.extralength),FILE_CURRENT);
}
if (cp<0)
cp=Unicode::DefaultCodePage();
m_rootdir.fixup_encoding(cp);
return true;
}
bool ZipFile::Open(const CString& filename) {
void *ptr;
if (!m_curdir->children->Lookup(filename,ptr))
return false;
m_curfile=(ZFile*)ptr;
if (m_curfile->iscmp()) {
if (m_zs)
inflateReset(m_zs);
else {
m_in=Buffer<Bytef>(BSZ);
m_zs=new z_stream;
memset(m_zs,0,sizeof(*m_zs));
inflateInit2(m_zs,-MAX_WBITS);
}
}
m_ptr=0;
RFile::seek(m_curfile->off);
return true;
}
bool ZipFile::IsSingleFile(CString *s) {
if (m_rootdir.off<0 && m_rootdir.children &&
m_rootdir.children->GetCount()==1)
{
POSITION p=m_rootdir.children->GetStartPosition();
if (p!=NULL) {
CString tmp;
void *q;
m_rootdir.children->GetNextAssoc(p,tmp,q);
ZFile *f=(ZFile*)q;
if (f->off>=0) {
if (s)
*s=tmp;
return true;
}
}
}
return false;
}
void ZipFile::ZFile::fixup_encoding(int enc) {
if (!isdir())
return;
CString fn,rc;
void *ptr;
CMapStringToPtr *map=new CMapStringToPtr;
for (POSITION pos=children->GetStartPosition();pos;) {
children->GetNextAssoc(pos,fn,ptr);
// contrive a char buffer, copy data and convert
Buffer<char> tmp(fn.GetLength());
for (int i=0;i<fn.GetLength();++i)
tmp[i]=(char)fn[i];
rc=Unicode::ToCS(enc,tmp,tmp.size());
map->SetAt(rc,ptr);
if (((ZFile*)ptr)->isdir())
((ZFile*)ptr)->fixup_encoding(enc);
}
delete children;
children=map;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -