2011-03-20 18:05:19 +00:00
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Name: src/common/zipstrm.cpp
|
|
|
|
// Purpose: Streams for Zip files
|
|
|
|
// Author: Mike Wetherell
|
2012-03-17 18:12:27 -07:00
|
|
|
// RCS-ID: $Id: zipstrm.cpp 67681 2011-05-03 16:29:04Z DS $
|
2011-03-20 18:05:19 +00:00
|
|
|
// Copyright: (c) Mike Wetherell
|
|
|
|
// Licence: wxWindows licence
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// For compilers that support precompilation, includes "wx.h".
|
|
|
|
#include "wx/wxprec.h"
|
|
|
|
|
|
|
|
#ifdef __BORLANDC__
|
|
|
|
#pragma hdrstop
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if wxUSE_ZIPSTREAM
|
|
|
|
|
|
|
|
#include "wx/zipstrm.h"
|
|
|
|
|
|
|
|
#ifndef WX_PRECOMP
|
|
|
|
#include "wx/hashmap.h"
|
|
|
|
#include "wx/intl.h"
|
|
|
|
#include "wx/log.h"
|
|
|
|
#include "wx/utils.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "wx/datstrm.h"
|
|
|
|
#include "wx/zstream.h"
|
|
|
|
#include "wx/mstream.h"
|
|
|
|
#include "wx/scopedptr.h"
|
|
|
|
#include "wx/wfstream.h"
|
|
|
|
#include "zlib.h"
|
|
|
|
|
|
|
|
// value for the 'version needed to extract' field (20 means 2.0)
|
|
|
|
enum {
|
|
|
|
VERSION_NEEDED_TO_EXTRACT = 20
|
|
|
|
};
|
|
|
|
|
|
|
|
// signatures for the various records (PKxx)
|
|
|
|
enum {
|
|
|
|
CENTRAL_MAGIC = 0x02014b50, // central directory record
|
|
|
|
LOCAL_MAGIC = 0x04034b50, // local header
|
|
|
|
END_MAGIC = 0x06054b50, // end of central directory record
|
|
|
|
SUMS_MAGIC = 0x08074b50 // data descriptor (info-zip)
|
|
|
|
};
|
|
|
|
|
|
|
|
// unix file attributes. zip stores them in the high 16 bits of the
|
|
|
|
// 'external attributes' field, hence the extra zeros.
|
|
|
|
enum {
|
|
|
|
wxZIP_S_IFMT = 0xF0000000,
|
|
|
|
wxZIP_S_IFDIR = 0x40000000,
|
|
|
|
wxZIP_S_IFREG = 0x80000000
|
|
|
|
};
|
|
|
|
|
|
|
|
// minimum sizes for the various records
|
|
|
|
enum {
|
|
|
|
CENTRAL_SIZE = 46,
|
|
|
|
LOCAL_SIZE = 30,
|
|
|
|
END_SIZE = 22,
|
|
|
|
SUMS_SIZE = 12
|
|
|
|
};
|
|
|
|
|
|
|
|
// The number of bytes that must be written to an wxZipOutputStream before
|
|
|
|
// a zip entry is created. The purpose of this latency is so that
|
|
|
|
// OpenCompressor() can see a little data before deciding which compressor
|
|
|
|
// it should use.
|
|
|
|
enum {
|
|
|
|
OUTPUT_LATENCY = 4096
|
|
|
|
};
|
|
|
|
|
|
|
|
// Some offsets into the local header
|
|
|
|
enum {
|
|
|
|
SUMS_OFFSET = 14
|
|
|
|
};
|
|
|
|
|
|
|
|
IMPLEMENT_DYNAMIC_CLASS(wxZipEntry, wxArchiveEntry)
|
|
|
|
IMPLEMENT_DYNAMIC_CLASS(wxZipClassFactory, wxArchiveClassFactory)
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Helpers
|
|
|
|
|
|
|
|
// read a string of a given length
|
|
|
|
//
|
|
|
|
static wxString ReadString(wxInputStream& stream, wxUint16 len, wxMBConv& conv)
|
|
|
|
{
|
|
|
|
if (len == 0)
|
|
|
|
return wxEmptyString;
|
|
|
|
|
|
|
|
#if wxUSE_UNICODE
|
|
|
|
wxCharBuffer buf(len);
|
|
|
|
stream.Read(buf.data(), len);
|
|
|
|
wxString str(buf, conv);
|
|
|
|
#else
|
|
|
|
wxString str;
|
|
|
|
(void)conv;
|
|
|
|
{
|
|
|
|
wxStringBuffer buf(str, len);
|
|
|
|
stream.Read(buf, len);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Decode a little endian wxUint32 number from a character array
|
|
|
|
//
|
|
|
|
static inline wxUint32 CrackUint32(const char *m)
|
|
|
|
{
|
|
|
|
const unsigned char *n = (const unsigned char*)m;
|
|
|
|
return (n[3] << 24) | (n[2] << 16) | (n[1] << 8) | n[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Decode a little endian wxUint16 number from a character array
|
|
|
|
//
|
|
|
|
static inline wxUint16 CrackUint16(const char *m)
|
|
|
|
{
|
|
|
|
const unsigned char *n = (const unsigned char*)m;
|
|
|
|
return (n[1] << 8) | n[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Temporarily lower the logging level in debug mode to avoid a warning
|
|
|
|
// from SeekI about seeking on a stream with data written back to it.
|
|
|
|
//
|
|
|
|
static wxFileOffset QuietSeek(wxInputStream& stream, wxFileOffset pos)
|
|
|
|
{
|
|
|
|
#if wxUSE_LOG
|
|
|
|
wxLogLevel level = wxLog::GetLogLevel();
|
|
|
|
wxLog::SetLogLevel(wxLOG_Debug - 1);
|
|
|
|
wxFileOffset result = stream.SeekI(pos);
|
|
|
|
wxLog::SetLogLevel(level);
|
|
|
|
return result;
|
|
|
|
#else
|
|
|
|
return stream.SeekI(pos);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Class factory
|
|
|
|
|
|
|
|
static wxZipClassFactory g_wxZipClassFactory;
|
|
|
|
|
|
|
|
wxZipClassFactory::wxZipClassFactory()
|
|
|
|
{
|
|
|
|
if (this == &g_wxZipClassFactory)
|
|
|
|
PushFront();
|
|
|
|
}
|
|
|
|
|
|
|
|
const wxChar * const *
|
|
|
|
wxZipClassFactory::GetProtocols(wxStreamProtocolType type) const
|
|
|
|
{
|
|
|
|
static const wxChar *protocols[] = { wxT("zip"), NULL };
|
|
|
|
static const wxChar *mimetypes[] = { wxT("application/zip"), NULL };
|
|
|
|
static const wxChar *fileexts[] = { wxT(".zip"), wxT(".htb"), NULL };
|
|
|
|
static const wxChar *empty[] = { NULL };
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case wxSTREAM_PROTOCOL: return protocols;
|
|
|
|
case wxSTREAM_MIMETYPE: return mimetypes;
|
|
|
|
case wxSTREAM_FILEEXT: return fileexts;
|
|
|
|
default: return empty;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Read a zip header
|
|
|
|
|
|
|
|
class wxZipHeader
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
wxZipHeader(wxInputStream& stream, size_t size);
|
|
|
|
|
|
|
|
inline wxUint8 Read8();
|
|
|
|
inline wxUint16 Read16();
|
|
|
|
inline wxUint32 Read32();
|
|
|
|
|
|
|
|
const char *GetData() const { return m_data; }
|
|
|
|
size_t GetSize() const { return m_size; }
|
|
|
|
operator bool() const { return m_ok; }
|
|
|
|
|
|
|
|
size_t Seek(size_t pos) { m_pos = pos; return m_pos; }
|
|
|
|
size_t Skip(size_t size) { m_pos += size; return m_pos; }
|
|
|
|
|
|
|
|
wxZipHeader& operator>>(wxUint8& n) { n = Read8(); return *this; }
|
|
|
|
wxZipHeader& operator>>(wxUint16& n) { n = Read16(); return *this; }
|
|
|
|
wxZipHeader& operator>>(wxUint32& n) { n = Read32(); return *this; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
char m_data[64];
|
|
|
|
size_t m_size;
|
|
|
|
size_t m_pos;
|
|
|
|
bool m_ok;
|
|
|
|
};
|
|
|
|
|
|
|
|
wxZipHeader::wxZipHeader(wxInputStream& stream, size_t size)
|
|
|
|
: m_size(0),
|
|
|
|
m_pos(0),
|
|
|
|
m_ok(false)
|
|
|
|
{
|
|
|
|
wxCHECK_RET(size <= sizeof(m_data), wxT("buffer too small"));
|
|
|
|
m_size = stream.Read(m_data, size).LastRead();
|
|
|
|
m_ok = m_size == size;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline wxUint8 wxZipHeader::Read8()
|
|
|
|
{
|
|
|
|
wxASSERT(m_pos < m_size);
|
|
|
|
return m_data[m_pos++];
|
|
|
|
}
|
|
|
|
|
|
|
|
inline wxUint16 wxZipHeader::Read16()
|
|
|
|
{
|
|
|
|
wxASSERT(m_pos + 2 <= m_size);
|
|
|
|
wxUint16 n = CrackUint16(m_data + m_pos);
|
|
|
|
m_pos += 2;
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline wxUint32 wxZipHeader::Read32()
|
|
|
|
{
|
|
|
|
wxASSERT(m_pos + 4 <= m_size);
|
|
|
|
wxUint32 n = CrackUint32(m_data + m_pos);
|
|
|
|
m_pos += 4;
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Stored input stream
|
|
|
|
// Trival decompressor for files which are 'stored' in the zip file.
|
|
|
|
|
|
|
|
class wxStoredInputStream : public wxFilterInputStream
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
wxStoredInputStream(wxInputStream& stream);
|
|
|
|
|
|
|
|
void Open(wxFileOffset len) { Close(); m_len = len; }
|
|
|
|
void Close() { m_pos = 0; m_lasterror = wxSTREAM_NO_ERROR; }
|
|
|
|
|
|
|
|
virtual char Peek() { return wxInputStream::Peek(); }
|
|
|
|
virtual wxFileOffset GetLength() const { return m_len; }
|
|
|
|
|
|
|
|
protected:
|
|
|
|
virtual size_t OnSysRead(void *buffer, size_t size);
|
|
|
|
virtual wxFileOffset OnSysTell() const { return m_pos; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
wxFileOffset m_pos;
|
|
|
|
wxFileOffset m_len;
|
|
|
|
|
|
|
|
wxDECLARE_NO_COPY_CLASS(wxStoredInputStream);
|
|
|
|
};
|
|
|
|
|
|
|
|
wxStoredInputStream::wxStoredInputStream(wxInputStream& stream)
|
|
|
|
: wxFilterInputStream(stream),
|
|
|
|
m_pos(0),
|
|
|
|
m_len(0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t wxStoredInputStream::OnSysRead(void *buffer, size_t size)
|
|
|
|
{
|
|
|
|
size_t count = wx_truncate_cast(size_t,
|
|
|
|
wxMin(size + wxFileOffset(0), m_len - m_pos + size_t(0)));
|
|
|
|
count = m_parent_i_stream->Read(buffer, count).LastRead();
|
|
|
|
m_pos += count;
|
|
|
|
|
|
|
|
if (count < size)
|
|
|
|
m_lasterror = m_pos == m_len ? wxSTREAM_EOF : wxSTREAM_READ_ERROR;
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Stored output stream
|
|
|
|
// Trival compressor for files which are 'stored' in the zip file.
|
|
|
|
|
|
|
|
class wxStoredOutputStream : public wxFilterOutputStream
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
wxStoredOutputStream(wxOutputStream& stream) :
|
|
|
|
wxFilterOutputStream(stream), m_pos(0) { }
|
|
|
|
|
|
|
|
bool Close() {
|
|
|
|
m_pos = 0;
|
|
|
|
m_lasterror = wxSTREAM_NO_ERROR;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
virtual size_t OnSysWrite(const void *buffer, size_t size);
|
|
|
|
virtual wxFileOffset OnSysTell() const { return m_pos; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
wxFileOffset m_pos;
|
|
|
|
wxDECLARE_NO_COPY_CLASS(wxStoredOutputStream);
|
|
|
|
};
|
|
|
|
|
|
|
|
size_t wxStoredOutputStream::OnSysWrite(const void *buffer, size_t size)
|
|
|
|
{
|
|
|
|
if (!IsOk() || !size)
|
|
|
|
return 0;
|
|
|
|
size_t count = m_parent_o_stream->Write(buffer, size).LastWrite();
|
|
|
|
if (count != size)
|
|
|
|
m_lasterror = wxSTREAM_WRITE_ERROR;
|
|
|
|
m_pos += count;
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// wxRawInputStream
|
|
|
|
//
|
|
|
|
// Used to handle the unusal case of raw copying an entry of unknown
|
|
|
|
// length. This can only happen when the zip being copied from is being
|
|
|
|
// read from a non-seekable stream, and also was original written to a
|
|
|
|
// non-seekable stream.
|
|
|
|
//
|
|
|
|
// In this case there's no option but to decompress the stream to find
|
|
|
|
// it's length, but we can still write the raw compressed data to avoid the
|
|
|
|
// compression overhead (which is the greater one).
|
|
|
|
//
|
|
|
|
// Usage is like this:
|
|
|
|
// m_rawin = new wxRawInputStream(*m_parent_i_stream);
|
|
|
|
// m_decomp = m_rawin->Open(OpenDecompressor(m_rawin->GetTee()));
|
|
|
|
//
|
|
|
|
// The wxRawInputStream owns a wxTeeInputStream object, the role of which
|
|
|
|
// is something like the unix 'tee' command; it is a transparent filter, but
|
|
|
|
// allows the data read to be read a second time via an extra method 'GetData'.
|
|
|
|
//
|
|
|
|
// The wxRawInputStream then draws data through the tee using a decompressor
|
|
|
|
// then instead of returning the decompressed data, retuns the raw data
|
|
|
|
// from wxTeeInputStream::GetData().
|
|
|
|
|
|
|
|
class wxTeeInputStream : public wxFilterInputStream
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
wxTeeInputStream(wxInputStream& stream);
|
|
|
|
|
|
|
|
size_t GetCount() const { return m_end - m_start; }
|
|
|
|
size_t GetData(char *buffer, size_t size);
|
|
|
|
|
|
|
|
void Open();
|
|
|
|
bool Final();
|
|
|
|
|
|
|
|
wxInputStream& Read(void *buffer, size_t size);
|
|
|
|
|
|
|
|
protected:
|
|
|
|
virtual size_t OnSysRead(void *buffer, size_t size);
|
|
|
|
virtual wxFileOffset OnSysTell() const { return m_pos; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
wxFileOffset m_pos;
|
|
|
|
wxMemoryBuffer m_buf;
|
|
|
|
size_t m_start;
|
|
|
|
size_t m_end;
|
|
|
|
|
|
|
|
wxDECLARE_NO_COPY_CLASS(wxTeeInputStream);
|
|
|
|
};
|
|
|
|
|
|
|
|
wxTeeInputStream::wxTeeInputStream(wxInputStream& stream)
|
|
|
|
: wxFilterInputStream(stream),
|
|
|
|
m_pos(0), m_buf(8192), m_start(0), m_end(0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void wxTeeInputStream::Open()
|
|
|
|
{
|
|
|
|
m_pos = m_start = m_end = 0;
|
|
|
|
m_lasterror = wxSTREAM_NO_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool wxTeeInputStream::Final()
|
|
|
|
{
|
|
|
|
bool final = m_end == m_buf.GetDataLen();
|
|
|
|
m_end = m_buf.GetDataLen();
|
|
|
|
return final;
|
|
|
|
}
|
|
|
|
|
|
|
|
wxInputStream& wxTeeInputStream::Read(void *buffer, size_t size)
|
|
|
|
{
|
|
|
|
size_t count = wxInputStream::Read(buffer, size).LastRead();
|
|
|
|
m_end = m_buf.GetDataLen();
|
|
|
|
m_buf.AppendData(buffer, count);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t wxTeeInputStream::OnSysRead(void *buffer, size_t size)
|
|
|
|
{
|
|
|
|
size_t count = m_parent_i_stream->Read(buffer, size).LastRead();
|
|
|
|
if (count < size)
|
|
|
|
m_lasterror = m_parent_i_stream->GetLastError();
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t wxTeeInputStream::GetData(char *buffer, size_t size)
|
|
|
|
{
|
|
|
|
if (m_wbacksize) {
|
|
|
|
size_t len = m_buf.GetDataLen();
|
|
|
|
len = len > m_wbacksize ? len - m_wbacksize : 0;
|
|
|
|
m_buf.SetDataLen(len);
|
|
|
|
if (m_end > len) {
|
|
|
|
wxFAIL; // we've already returned data that's now being ungot
|
|
|
|
m_end = len;
|
|
|
|
}
|
|
|
|
m_parent_i_stream->Reset();
|
|
|
|
m_parent_i_stream->Ungetch(m_wback, m_wbacksize);
|
|
|
|
free(m_wback);
|
|
|
|
m_wback = NULL;
|
|
|
|
m_wbacksize = 0;
|
|
|
|
m_wbackcur = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (size > GetCount())
|
|
|
|
size = GetCount();
|
|
|
|
if (size) {
|
|
|
|
memcpy(buffer, m_buf + m_start, size);
|
|
|
|
m_start += size;
|
|
|
|
wxASSERT(m_start <= m_end);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_start == m_end && m_start > 0 && m_buf.GetDataLen() > 0) {
|
|
|
|
size_t len = m_buf.GetDataLen();
|
|
|
|
char *buf = (char*)m_buf.GetWriteBuf(len);
|
|
|
|
len -= m_end;
|
|
|
|
memmove(buf, buf + m_end, len);
|
|
|
|
m_buf.UngetWriteBuf(len);
|
|
|
|
m_start = m_end = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
class wxRawInputStream : public wxFilterInputStream
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
wxRawInputStream(wxInputStream& stream);
|
|
|
|
virtual ~wxRawInputStream() { delete m_tee; }
|
|
|
|
|
|
|
|
wxInputStream* Open(wxInputStream *decomp);
|
|
|
|
wxInputStream& GetTee() const { return *m_tee; }
|
|
|
|
|
|
|
|
protected:
|
|
|
|
virtual size_t OnSysRead(void *buffer, size_t size);
|
|
|
|
virtual wxFileOffset OnSysTell() const { return m_pos; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
wxFileOffset m_pos;
|
|
|
|
wxTeeInputStream *m_tee;
|
|
|
|
|
|
|
|
enum { BUFSIZE = 8192 };
|
|
|
|
wxCharBuffer m_dummy;
|
|
|
|
|
|
|
|
wxDECLARE_NO_COPY_CLASS(wxRawInputStream);
|
|
|
|
};
|
|
|
|
|
|
|
|
wxRawInputStream::wxRawInputStream(wxInputStream& stream)
|
|
|
|
: wxFilterInputStream(stream),
|
|
|
|
m_pos(0),
|
|
|
|
m_tee(new wxTeeInputStream(stream)),
|
|
|
|
m_dummy(BUFSIZE)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
wxInputStream *wxRawInputStream::Open(wxInputStream *decomp)
|
|
|
|
{
|
|
|
|
if (decomp) {
|
|
|
|
m_parent_i_stream = decomp;
|
|
|
|
m_pos = 0;
|
|
|
|
m_lasterror = wxSTREAM_NO_ERROR;
|
|
|
|
m_tee->Open();
|
|
|
|
return this;
|
|
|
|
} else {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t wxRawInputStream::OnSysRead(void *buffer, size_t size)
|
|
|
|
{
|
|
|
|
char *buf = (char*)buffer;
|
|
|
|
size_t count = 0;
|
|
|
|
|
|
|
|
while (count < size && IsOk())
|
|
|
|
{
|
|
|
|
while (m_parent_i_stream->IsOk() && m_tee->GetCount() == 0)
|
|
|
|
m_parent_i_stream->Read(m_dummy.data(), BUFSIZE);
|
|
|
|
|
|
|
|
size_t n = m_tee->GetData(buf + count, size - count);
|
|
|
|
count += n;
|
|
|
|
|
|
|
|
if (n == 0 && m_tee->Final())
|
|
|
|
m_lasterror = m_parent_i_stream->GetLastError();
|
|
|
|
}
|
|
|
|
|
|
|
|
m_pos += count;
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Zlib streams than can be reused without recreating.
|
|
|
|
|
|
|
|
class wxZlibOutputStream2 : public wxZlibOutputStream
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
wxZlibOutputStream2(wxOutputStream& stream, int level) :
|
|
|
|
wxZlibOutputStream(stream, level, wxZLIB_NO_HEADER) { }
|
|
|
|
|
|
|
|
bool Open(wxOutputStream& stream);
|
|
|
|
bool Close() { DoFlush(true); m_pos = wxInvalidOffset; return IsOk(); }
|
|
|
|
};
|
|
|
|
|
|
|
|
bool wxZlibOutputStream2::Open(wxOutputStream& stream)
|
|
|
|
{
|
|
|
|
wxCHECK(m_pos == wxInvalidOffset, false);
|
|
|
|
|
|
|
|
m_deflate->next_out = m_z_buffer;
|
|
|
|
m_deflate->avail_out = m_z_size;
|
|
|
|
m_pos = 0;
|
|
|
|
m_lasterror = wxSTREAM_NO_ERROR;
|
|
|
|
m_parent_o_stream = &stream;
|
|
|
|
|
|
|
|
if (deflateReset(m_deflate) != Z_OK) {
|
|
|
|
wxLogError(_("can't re-initialize zlib deflate stream"));
|
|
|
|
m_lasterror = wxSTREAM_WRITE_ERROR;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
class wxZlibInputStream2 : public wxZlibInputStream
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
wxZlibInputStream2(wxInputStream& stream) :
|
|
|
|
wxZlibInputStream(stream, wxZLIB_NO_HEADER) { }
|
|
|
|
|
|
|
|
bool Open(wxInputStream& stream);
|
|
|
|
};
|
|
|
|
|
|
|
|
bool wxZlibInputStream2::Open(wxInputStream& stream)
|
|
|
|
{
|
|
|
|
m_inflate->avail_in = 0;
|
|
|
|
m_pos = 0;
|
|
|
|
m_lasterror = wxSTREAM_NO_ERROR;
|
|
|
|
m_parent_i_stream = &stream;
|
|
|
|
|
|
|
|
if (inflateReset(m_inflate) != Z_OK) {
|
|
|
|
wxLogError(_("can't re-initialize zlib inflate stream"));
|
|
|
|
m_lasterror = wxSTREAM_READ_ERROR;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Class to hold wxZipEntry's Extra and LocalExtra fields
|
|
|
|
|
|
|
|
class wxZipMemory
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
wxZipMemory() : m_data(NULL), m_size(0), m_capacity(0), m_ref(1) { }
|
|
|
|
|
|
|
|
wxZipMemory *AddRef() { m_ref++; return this; }
|
|
|
|
void Release() { if (--m_ref == 0) delete this; }
|
|
|
|
|
|
|
|
char *GetData() const { return m_data; }
|
|
|
|
size_t GetSize() const { return m_size; }
|
|
|
|
size_t GetCapacity() const { return m_capacity; }
|
|
|
|
|
|
|
|
wxZipMemory *Unique(size_t size);
|
|
|
|
|
|
|
|
private:
|
|
|
|
~wxZipMemory() { delete [] m_data; }
|
|
|
|
|
|
|
|
char *m_data;
|
|
|
|
size_t m_size;
|
|
|
|
size_t m_capacity;
|
|
|
|
int m_ref;
|
|
|
|
|
|
|
|
wxSUPPRESS_GCC_PRIVATE_DTOR_WARNING(wxZipMemory)
|
|
|
|
};
|
|
|
|
|
|
|
|
wxZipMemory *wxZipMemory::Unique(size_t size)
|
|
|
|
{
|
|
|
|
wxZipMemory *zm;
|
|
|
|
|
|
|
|
if (m_ref > 1) {
|
|
|
|
--m_ref;
|
|
|
|
zm = new wxZipMemory;
|
|
|
|
} else {
|
|
|
|
zm = this;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (zm->m_capacity < size) {
|
|
|
|
delete [] zm->m_data;
|
|
|
|
zm->m_data = new char[size];
|
|
|
|
zm->m_capacity = size;
|
|
|
|
}
|
|
|
|
|
|
|
|
zm->m_size = size;
|
|
|
|
return zm;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline wxZipMemory *AddRef(wxZipMemory *zm)
|
|
|
|
{
|
|
|
|
if (zm)
|
|
|
|
zm->AddRef();
|
|
|
|
return zm;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void Release(wxZipMemory *zm)
|
|
|
|
{
|
|
|
|
if (zm)
|
|
|
|
zm->Release();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void Copy(wxZipMemory*& dest, wxZipMemory *src)
|
|
|
|
{
|
|
|
|
Release(dest);
|
|
|
|
dest = AddRef(src);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void Unique(wxZipMemory*& zm, size_t size)
|
|
|
|
{
|
|
|
|
if (!zm && size)
|
|
|
|
zm = new wxZipMemory;
|
|
|
|
if (zm)
|
|
|
|
zm = zm->Unique(size);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Collection of weak references to entries
|
|
|
|
|
|
|
|
WX_DECLARE_HASH_MAP(long, wxZipEntry*, wxIntegerHash,
|
|
|
|
wxIntegerEqual, wxOffsetZipEntryMap_);
|
|
|
|
|
|
|
|
class wxZipWeakLinks
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
wxZipWeakLinks() : m_ref(1) { }
|
|
|
|
|
|
|
|
void Release(const wxZipInputStream* WXUNUSED(x))
|
|
|
|
{ if (--m_ref == 0) delete this; }
|
|
|
|
void Release(wxFileOffset key)
|
|
|
|
{ RemoveEntry(key); if (--m_ref == 0) delete this; }
|
|
|
|
|
|
|
|
wxZipWeakLinks *AddEntry(wxZipEntry *entry, wxFileOffset key);
|
|
|
|
void RemoveEntry(wxFileOffset key)
|
|
|
|
{ m_entries.erase(wx_truncate_cast(key_type, key)); }
|
|
|
|
wxZipEntry *GetEntry(wxFileOffset key) const;
|
|
|
|
bool IsEmpty() const { return m_entries.empty(); }
|
|
|
|
|
|
|
|
private:
|
|
|
|
~wxZipWeakLinks() { wxASSERT(IsEmpty()); }
|
|
|
|
|
|
|
|
typedef wxOffsetZipEntryMap_::key_type key_type;
|
|
|
|
|
|
|
|
int m_ref;
|
|
|
|
wxOffsetZipEntryMap_ m_entries;
|
|
|
|
|
|
|
|
wxSUPPRESS_GCC_PRIVATE_DTOR_WARNING(wxZipWeakLinks)
|
|
|
|
};
|
|
|
|
|
|
|
|
wxZipWeakLinks *wxZipWeakLinks::AddEntry(wxZipEntry *entry, wxFileOffset key)
|
|
|
|
{
|
|
|
|
m_entries[wx_truncate_cast(key_type, key)] = entry;
|
|
|
|
m_ref++;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
wxZipEntry *wxZipWeakLinks::GetEntry(wxFileOffset key) const
|
|
|
|
{
|
|
|
|
wxOffsetZipEntryMap_::const_iterator it =
|
|
|
|
m_entries.find(wx_truncate_cast(key_type, key));
|
|
|
|
return it != m_entries.end() ? it->second : NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ZipEntry
|
|
|
|
|
|
|
|
wxZipEntry::wxZipEntry(
|
|
|
|
const wxString& name /*=wxEmptyString*/,
|
|
|
|
const wxDateTime& dt /*=wxDateTime::Now()*/,
|
|
|
|
wxFileOffset size /*=wxInvalidOffset*/)
|
|
|
|
:
|
|
|
|
m_SystemMadeBy(wxZIP_SYSTEM_MSDOS),
|
|
|
|
m_VersionMadeBy(wxMAJOR_VERSION * 10 + wxMINOR_VERSION),
|
|
|
|
m_VersionNeeded(VERSION_NEEDED_TO_EXTRACT),
|
|
|
|
m_Flags(0),
|
|
|
|
m_Method(wxZIP_METHOD_DEFAULT),
|
|
|
|
m_DateTime(dt),
|
|
|
|
m_Crc(0),
|
|
|
|
m_CompressedSize(wxInvalidOffset),
|
|
|
|
m_Size(size),
|
|
|
|
m_Key(wxInvalidOffset),
|
|
|
|
m_Offset(wxInvalidOffset),
|
|
|
|
m_DiskStart(0),
|
|
|
|
m_InternalAttributes(0),
|
|
|
|
m_ExternalAttributes(0),
|
|
|
|
m_Extra(NULL),
|
|
|
|
m_LocalExtra(NULL),
|
|
|
|
m_zipnotifier(NULL),
|
|
|
|
m_backlink(NULL)
|
|
|
|
{
|
|
|
|
if (!name.empty())
|
|
|
|
SetName(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
wxZipEntry::~wxZipEntry()
|
|
|
|
{
|
|
|
|
if (m_backlink)
|
|
|
|
m_backlink->Release(m_Key);
|
|
|
|
Release(m_Extra);
|
|
|
|
Release(m_LocalExtra);
|
|
|
|
}
|
|
|
|
|
|
|
|
wxZipEntry::wxZipEntry(const wxZipEntry& e)
|
|
|
|
: wxArchiveEntry(e),
|
|
|
|
m_SystemMadeBy(e.m_SystemMadeBy),
|
|
|
|
m_VersionMadeBy(e.m_VersionMadeBy),
|
|
|
|
m_VersionNeeded(e.m_VersionNeeded),
|
|
|
|
m_Flags(e.m_Flags),
|
|
|
|
m_Method(e.m_Method),
|
|
|
|
m_DateTime(e.m_DateTime),
|
|
|
|
m_Crc(e.m_Crc),
|
|
|
|
m_CompressedSize(e.m_CompressedSize),
|
|
|
|
m_Size(e.m_Size),
|
|
|
|
m_Name(e.m_Name),
|
|
|
|
m_Key(e.m_Key),
|
|
|
|
m_Offset(e.m_Offset),
|
|
|
|
m_Comment(e.m_Comment),
|
|
|
|
m_DiskStart(e.m_DiskStart),
|
|
|
|
m_InternalAttributes(e.m_InternalAttributes),
|
|
|
|
m_ExternalAttributes(e.m_ExternalAttributes),
|
|
|
|
m_Extra(AddRef(e.m_Extra)),
|
|
|
|
m_LocalExtra(AddRef(e.m_LocalExtra)),
|
|
|
|
m_zipnotifier(NULL),
|
|
|
|
m_backlink(NULL)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
wxZipEntry& wxZipEntry::operator=(const wxZipEntry& e)
|
|
|
|
{
|
|
|
|
if (&e != this) {
|
|
|
|
m_SystemMadeBy = e.m_SystemMadeBy;
|
|
|
|
m_VersionMadeBy = e.m_VersionMadeBy;
|
|
|
|
m_VersionNeeded = e.m_VersionNeeded;
|
|
|
|
m_Flags = e.m_Flags;
|
|
|
|
m_Method = e.m_Method;
|
|
|
|
m_DateTime = e.m_DateTime;
|
|
|
|
m_Crc = e.m_Crc;
|
|
|
|
m_CompressedSize = e.m_CompressedSize;
|
|
|
|
m_Size = e.m_Size;
|
|
|
|
m_Name = e.m_Name;
|
|
|
|
m_Key = e.m_Key;
|
|
|
|
m_Offset = e.m_Offset;
|
|
|
|
m_Comment = e.m_Comment;
|
|
|
|
m_DiskStart = e.m_DiskStart;
|
|
|
|
m_InternalAttributes = e.m_InternalAttributes;
|
|
|
|
m_ExternalAttributes = e.m_ExternalAttributes;
|
|
|
|
Copy(m_Extra, e.m_Extra);
|
|
|
|
Copy(m_LocalExtra, e.m_LocalExtra);
|
|
|
|
m_zipnotifier = NULL;
|
|
|
|
if (m_backlink) {
|
|
|
|
m_backlink->Release(m_Key);
|
|
|
|
m_backlink = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
wxString wxZipEntry::GetName(wxPathFormat format /*=wxPATH_NATIVE*/) const
|
|
|
|
{
|
|
|
|
bool isDir = IsDir() && !m_Name.empty();
|
|
|
|
|
|
|
|
// optimisations for common (and easy) cases
|
|
|
|
switch (wxFileName::GetFormat(format)) {
|
|
|
|
case wxPATH_DOS:
|
|
|
|
{
|
|
|
|
wxString name(isDir ? m_Name + wxT("\\") : m_Name);
|
|
|
|
for (size_t i = 0; i < name.length(); i++)
|
|
|
|
if (name[i] == wxT('/'))
|
|
|
|
name[i] = wxT('\\');
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
case wxPATH_UNIX:
|
|
|
|
return isDir ? m_Name + wxT("/") : m_Name;
|
|
|
|
|
|
|
|
default:
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
|
|
|
wxFileName fn;
|
|
|
|
|
|
|
|
if (isDir)
|
|
|
|
fn.AssignDir(m_Name, wxPATH_UNIX);
|
|
|
|
else
|
|
|
|
fn.Assign(m_Name, wxPATH_UNIX);
|
|
|
|
|
|
|
|
return fn.GetFullPath(format);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Static - Internally tars and zips use forward slashes for the path
|
|
|
|
// separator, absolute paths aren't allowed, and directory names have a
|
|
|
|
// trailing slash. This function converts a path into this internal format,
|
|
|
|
// but without a trailing slash for a directory.
|
|
|
|
//
|
|
|
|
wxString wxZipEntry::GetInternalName(const wxString& name,
|
|
|
|
wxPathFormat format /*=wxPATH_NATIVE*/,
|
|
|
|
bool *pIsDir /*=NULL*/)
|
|
|
|
{
|
|
|
|
wxString internal;
|
|
|
|
|
|
|
|
if (wxFileName::GetFormat(format) != wxPATH_UNIX)
|
|
|
|
internal = wxFileName(name, format).GetFullPath(wxPATH_UNIX);
|
|
|
|
else
|
|
|
|
internal = name;
|
|
|
|
|
|
|
|
bool isDir = !internal.empty() && internal.Last() == '/';
|
|
|
|
if (pIsDir)
|
|
|
|
*pIsDir = isDir;
|
|
|
|
if (isDir)
|
|
|
|
internal.erase(internal.length() - 1);
|
|
|
|
|
|
|
|
while (!internal.empty() && *internal.begin() == '/')
|
|
|
|
internal.erase(0, 1);
|
|
|
|
while (!internal.empty() && internal.compare(0, 2, wxT("./")) == 0)
|
|
|
|
internal.erase(0, 2);
|
|
|
|
if (internal == wxT(".") || internal == wxT(".."))
|
|
|
|
internal = wxEmptyString;
|
|
|
|
|
|
|
|
return internal;
|
|
|
|
}
|
|
|
|
|
|
|
|
void wxZipEntry::SetSystemMadeBy(int system)
|
|
|
|
{
|
|
|
|
int mode = GetMode();
|
|
|
|
bool wasUnix = IsMadeByUnix();
|
|
|
|
|
|
|
|
m_SystemMadeBy = (wxUint8)system;
|
|
|
|
|
|
|
|
if (!wasUnix && IsMadeByUnix()) {
|
|
|
|
SetIsDir(IsDir());
|
|
|
|
SetMode(mode);
|
|
|
|
} else if (wasUnix && !IsMadeByUnix()) {
|
|
|
|
m_ExternalAttributes &= 0xffff;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void wxZipEntry::SetIsDir(bool isDir /*=true*/)
|
|
|
|
{
|
|
|
|
if (isDir)
|
|
|
|
m_ExternalAttributes |= wxZIP_A_SUBDIR;
|
|
|
|
else
|
|
|
|
m_ExternalAttributes &= ~wxZIP_A_SUBDIR;
|
|
|
|
|
|
|
|
if (IsMadeByUnix()) {
|
|
|
|
m_ExternalAttributes &= ~wxZIP_S_IFMT;
|
|
|
|
if (isDir)
|
|
|
|
m_ExternalAttributes |= wxZIP_S_IFDIR;
|
|
|
|
else
|
|
|
|
m_ExternalAttributes |= wxZIP_S_IFREG;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return unix style permission bits
|
|
|
|
//
|
|
|
|
int wxZipEntry::GetMode() const
|
|
|
|
{
|
|
|
|
// return unix permissions if present
|
|
|
|
if (IsMadeByUnix())
|
|
|
|
return (m_ExternalAttributes >> 16) & 0777;
|
|
|
|
|
|
|
|
// otherwise synthesize from the dos attribs
|
|
|
|
int mode = 0644;
|
|
|
|
if (m_ExternalAttributes & wxZIP_A_RDONLY)
|
|
|
|
mode &= ~0200;
|
|
|
|
if (m_ExternalAttributes & wxZIP_A_SUBDIR)
|
|
|
|
mode |= 0111;
|
|
|
|
|
|
|
|
return mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set unix permissions
|
|
|
|
//
|
|
|
|
void wxZipEntry::SetMode(int mode)
|
|
|
|
{
|
|
|
|
// Set dos attrib bits to be compatible
|
|
|
|
if (mode & 0222)
|
|
|
|
m_ExternalAttributes &= ~wxZIP_A_RDONLY;
|
|
|
|
else
|
|
|
|
m_ExternalAttributes |= wxZIP_A_RDONLY;
|
|
|
|
|
|
|
|
// set the actual unix permission bits if the system type allows
|
|
|
|
if (IsMadeByUnix()) {
|
|
|
|
m_ExternalAttributes &= ~(0777L << 16);
|
|
|
|
m_ExternalAttributes |= (mode & 0777L) << 16;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *wxZipEntry::GetExtra() const
|
|
|
|
{
|
|
|
|
return m_Extra ? m_Extra->GetData() : NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t wxZipEntry::GetExtraLen() const
|
|
|
|
{
|
|
|
|
return m_Extra ? m_Extra->GetSize() : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void wxZipEntry::SetExtra(const char *extra, size_t len)
|
|
|
|
{
|
|
|
|
Unique(m_Extra, len);
|
|
|
|
if (len)
|
|
|
|
memcpy(m_Extra->GetData(), extra, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *wxZipEntry::GetLocalExtra() const
|
|
|
|
{
|
|
|
|
return m_LocalExtra ? m_LocalExtra->GetData() : NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t wxZipEntry::GetLocalExtraLen() const
|
|
|
|
{
|
|
|
|
return m_LocalExtra ? m_LocalExtra->GetSize() : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void wxZipEntry::SetLocalExtra(const char *extra, size_t len)
|
|
|
|
{
|
|
|
|
Unique(m_LocalExtra, len);
|
|
|
|
if (len)
|
|
|
|
memcpy(m_LocalExtra->GetData(), extra, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
void wxZipEntry::SetNotifier(wxZipNotifier& notifier)
|
|
|
|
{
|
|
|
|
wxArchiveEntry::UnsetNotifier();
|
|
|
|
m_zipnotifier = ¬ifier;
|
|
|
|
m_zipnotifier->OnEntryUpdated(*this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void wxZipEntry::Notify()
|
|
|
|
{
|
|
|
|
if (m_zipnotifier)
|
|
|
|
m_zipnotifier->OnEntryUpdated(*this);
|
|
|
|
else if (GetNotifier())
|
|
|
|
GetNotifier()->OnEntryUpdated(*this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void wxZipEntry::UnsetNotifier()
|
|
|
|
{
|
|
|
|
wxArchiveEntry::UnsetNotifier();
|
|
|
|
m_zipnotifier = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t wxZipEntry::ReadLocal(wxInputStream& stream, wxMBConv& conv)
|
|
|
|
{
|
|
|
|
wxUint16 nameLen, extraLen;
|
|
|
|
wxUint32 compressedSize, size, crc;
|
|
|
|
|
|
|
|
wxZipHeader ds(stream, LOCAL_SIZE - 4);
|
|
|
|
if (!ds)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
ds >> m_VersionNeeded >> m_Flags >> m_Method;
|
|
|
|
SetDateTime(wxDateTime().SetFromDOS(ds.Read32()));
|
|
|
|
ds >> crc >> compressedSize >> size >> nameLen >> extraLen;
|
|
|
|
|
|
|
|
bool sumsValid = (m_Flags & wxZIP_SUMS_FOLLOW) == 0;
|
|
|
|
|
|
|
|
if (sumsValid || crc)
|
|
|
|
m_Crc = crc;
|
|
|
|
if ((sumsValid || compressedSize) || m_Method == wxZIP_METHOD_STORE)
|
|
|
|
m_CompressedSize = compressedSize;
|
|
|
|
if ((sumsValid || size) || m_Method == wxZIP_METHOD_STORE)
|
|
|
|
m_Size = size;
|
|
|
|
|
|
|
|
SetName(ReadString(stream, nameLen, conv), wxPATH_UNIX);
|
|
|
|
if (stream.LastRead() != nameLen + 0u)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (extraLen || GetLocalExtraLen()) {
|
|
|
|
Unique(m_LocalExtra, extraLen);
|
|
|
|
if (extraLen) {
|
|
|
|
stream.Read(m_LocalExtra->GetData(), extraLen);
|
|
|
|
if (stream.LastRead() != extraLen + 0u)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return LOCAL_SIZE + nameLen + extraLen;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t wxZipEntry::WriteLocal(wxOutputStream& stream, wxMBConv& conv) const
|
|
|
|
{
|
|
|
|
wxString unixName = GetName(wxPATH_UNIX);
|
|
|
|
const wxWX2MBbuf name_buf = unixName.mb_str(conv);
|
|
|
|
const char *name = name_buf;
|
|
|
|
if (!name) name = "";
|
|
|
|
wxUint16 nameLen = wx_truncate_cast(wxUint16, strlen(name));
|
|
|
|
|
|
|
|
wxDataOutputStream ds(stream);
|
|
|
|
|
|
|
|
ds << m_VersionNeeded << m_Flags << m_Method;
|
|
|
|
ds.Write32(GetDateTime().GetAsDOS());
|
|
|
|
|
|
|
|
ds.Write32(m_Crc);
|
|
|
|
ds.Write32(m_CompressedSize != wxInvalidOffset ?
|
|
|
|
wx_truncate_cast(wxUint32, m_CompressedSize) : 0);
|
|
|
|
ds.Write32(m_Size != wxInvalidOffset ?
|
|
|
|
wx_truncate_cast(wxUint32, m_Size) : 0);
|
|
|
|
|
|
|
|
ds << nameLen;
|
|
|
|
wxUint16 extraLen = wx_truncate_cast(wxUint16, GetLocalExtraLen());
|
|
|
|
ds.Write16(extraLen);
|
|
|
|
|
|
|
|
stream.Write(name, nameLen);
|
|
|
|
if (extraLen)
|
|
|
|
stream.Write(m_LocalExtra->GetData(), extraLen);
|
|
|
|
|
|
|
|
return LOCAL_SIZE + nameLen + extraLen;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t wxZipEntry::ReadCentral(wxInputStream& stream, wxMBConv& conv)
|
|
|
|
{
|
|
|
|
wxUint16 nameLen, extraLen, commentLen;
|
|
|
|
|
|
|
|
wxZipHeader ds(stream, CENTRAL_SIZE - 4);
|
|
|
|
if (!ds)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
ds >> m_VersionMadeBy >> m_SystemMadeBy;
|
|
|
|
|
|
|
|
SetVersionNeeded(ds.Read16());
|
|
|
|
SetFlags(ds.Read16());
|
|
|
|
SetMethod(ds.Read16());
|
|
|
|
SetDateTime(wxDateTime().SetFromDOS(ds.Read32()));
|
|
|
|
SetCrc(ds.Read32());
|
|
|
|
SetCompressedSize(ds.Read32());
|
|
|
|
SetSize(ds.Read32());
|
|
|
|
|
|
|
|
ds >> nameLen >> extraLen >> commentLen
|
|
|
|
>> m_DiskStart >> m_InternalAttributes >> m_ExternalAttributes;
|
|
|
|
SetOffset(ds.Read32());
|
|
|
|
|
|
|
|
SetName(ReadString(stream, nameLen, conv), wxPATH_UNIX);
|
|
|
|
if (stream.LastRead() != nameLen + 0u)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (extraLen || GetExtraLen()) {
|
|
|
|
Unique(m_Extra, extraLen);
|
|
|
|
if (extraLen) {
|
|
|
|
stream.Read(m_Extra->GetData(), extraLen);
|
|
|
|
if (stream.LastRead() != extraLen + 0u)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (commentLen) {
|
|
|
|
m_Comment = ReadString(stream, commentLen, conv);
|
|
|
|
if (stream.LastRead() != commentLen + 0u)
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
m_Comment.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
return CENTRAL_SIZE + nameLen + extraLen + commentLen;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t wxZipEntry::WriteCentral(wxOutputStream& stream, wxMBConv& conv) const
|
|
|
|
{
|
|
|
|
wxString unixName = GetName(wxPATH_UNIX);
|
|
|
|
const wxWX2MBbuf name_buf = unixName.mb_str(conv);
|
|
|
|
const char *name = name_buf;
|
|
|
|
if (!name) name = "";
|
|
|
|
wxUint16 nameLen = wx_truncate_cast(wxUint16, strlen(name));
|
|
|
|
|
|
|
|
const wxWX2MBbuf comment_buf = m_Comment.mb_str(conv);
|
|
|
|
const char *comment = comment_buf;
|
|
|
|
if (!comment) comment = "";
|
|
|
|
wxUint16 commentLen = wx_truncate_cast(wxUint16, strlen(comment));
|
|
|
|
|
|
|
|
wxUint16 extraLen = wx_truncate_cast(wxUint16, GetExtraLen());
|
|
|
|
|
|
|
|
wxDataOutputStream ds(stream);
|
|
|
|
|
|
|
|
ds << CENTRAL_MAGIC << m_VersionMadeBy << m_SystemMadeBy;
|
|
|
|
|
|
|
|
ds.Write16(wx_truncate_cast(wxUint16, GetVersionNeeded()));
|
|
|
|
ds.Write16(wx_truncate_cast(wxUint16, GetFlags()));
|
|
|
|
ds.Write16(wx_truncate_cast(wxUint16, GetMethod()));
|
|
|
|
ds.Write32(GetDateTime().GetAsDOS());
|
|
|
|
ds.Write32(GetCrc());
|
|
|
|
ds.Write32(wx_truncate_cast(wxUint32, GetCompressedSize()));
|
|
|
|
ds.Write32(wx_truncate_cast(wxUint32, GetSize()));
|
|
|
|
ds.Write16(nameLen);
|
|
|
|
ds.Write16(extraLen);
|
|
|
|
|
|
|
|
ds << commentLen << m_DiskStart << m_InternalAttributes
|
|
|
|
<< m_ExternalAttributes << wx_truncate_cast(wxUint32, GetOffset());
|
|
|
|
|
|
|
|
stream.Write(name, nameLen);
|
|
|
|
if (extraLen)
|
|
|
|
stream.Write(GetExtra(), extraLen);
|
|
|
|
stream.Write(comment, commentLen);
|
|
|
|
|
|
|
|
return CENTRAL_SIZE + nameLen + extraLen + commentLen;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Info-zip prefixes this record with a signature, but pkzip doesn't. So if
|
|
|
|
// the 1st value is the signature then it is probably an info-zip record,
|
|
|
|
// though there is a small chance that it is in fact a pkzip record which
|
|
|
|
// happens to have the signature as it's CRC.
|
|
|
|
//
|
|
|
|
size_t wxZipEntry::ReadDescriptor(wxInputStream& stream)
|
|
|
|
{
|
|
|
|
wxZipHeader ds(stream, SUMS_SIZE);
|
|
|
|
if (!ds)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
m_Crc = ds.Read32();
|
|
|
|
m_CompressedSize = ds.Read32();
|
|
|
|
m_Size = ds.Read32();
|
|
|
|
|
|
|
|
// if 1st value is the signature then this is probably an info-zip record
|
|
|
|
if (m_Crc == SUMS_MAGIC)
|
|
|
|
{
|
|
|
|
wxZipHeader buf(stream, 8);
|
|
|
|
wxUint32 u1 = buf.GetSize() >= 4 ? buf.Read32() : (wxUint32)LOCAL_MAGIC;
|
|
|
|
wxUint32 u2 = buf.GetSize() == 8 ? buf.Read32() : 0;
|
|
|
|
|
|
|
|
// look for the signature of the following record to decide which
|
|
|
|
if ((u1 == LOCAL_MAGIC || u1 == CENTRAL_MAGIC) &&
|
|
|
|
(u2 != LOCAL_MAGIC && u2 != CENTRAL_MAGIC))
|
|
|
|
{
|
|
|
|
// it's a pkzip style record after all!
|
|
|
|
if (buf.GetSize() > 0)
|
|
|
|
stream.Ungetch(buf.GetData(), buf.GetSize());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// it's an info-zip record as expected
|
|
|
|
if (buf.GetSize() > 4)
|
|
|
|
stream.Ungetch(buf.GetData() + 4, buf.GetSize() - 4);
|
|
|
|
m_Crc = wx_truncate_cast(wxUint32, m_CompressedSize);
|
|
|
|
m_CompressedSize = m_Size;
|
|
|
|
m_Size = u1;
|
|
|
|
return SUMS_SIZE + 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return SUMS_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t wxZipEntry::WriteDescriptor(wxOutputStream& stream, wxUint32 crc,
|
|
|
|
wxFileOffset compressedSize, wxFileOffset size)
|
|
|
|
{
|
|
|
|
m_Crc = crc;
|
|
|
|
m_CompressedSize = compressedSize;
|
|
|
|
m_Size = size;
|
|
|
|
|
|
|
|
wxDataOutputStream ds(stream);
|
|
|
|
|
|
|
|
ds.Write32(crc);
|
|
|
|
ds.Write32(wx_truncate_cast(wxUint32, compressedSize));
|
|
|
|
ds.Write32(wx_truncate_cast(wxUint32, size));
|
|
|
|
|
|
|
|
return SUMS_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// wxZipEndRec - holds the end of central directory record
|
|
|
|
|
|
|
|
class wxZipEndRec
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
wxZipEndRec();
|
|
|
|
|
|
|
|
int GetDiskNumber() const { return m_DiskNumber; }
|
|
|
|
int GetStartDisk() const { return m_StartDisk; }
|
|
|
|
int GetEntriesHere() const { return m_EntriesHere; }
|
|
|
|
int GetTotalEntries() const { return m_TotalEntries; }
|
|
|
|
wxFileOffset GetSize() const { return m_Size; }
|
|
|
|
wxFileOffset GetOffset() const { return m_Offset; }
|
|
|
|
wxString GetComment() const { return m_Comment; }
|
|
|
|
|
|
|
|
void SetDiskNumber(int num)
|
|
|
|
{ m_DiskNumber = wx_truncate_cast(wxUint16, num); }
|
|
|
|
void SetStartDisk(int num)
|
|
|
|
{ m_StartDisk = wx_truncate_cast(wxUint16, num); }
|
|
|
|
void SetEntriesHere(int num)
|
|
|
|
{ m_EntriesHere = wx_truncate_cast(wxUint16, num); }
|
|
|
|
void SetTotalEntries(int num)
|
|
|
|
{ m_TotalEntries = wx_truncate_cast(wxUint16, num); }
|
|
|
|
void SetSize(wxFileOffset size)
|
|
|
|
{ m_Size = wx_truncate_cast(wxUint32, size); }
|
|
|
|
void SetOffset(wxFileOffset offset)
|
|
|
|
{ m_Offset = wx_truncate_cast(wxUint32, offset); }
|
|
|
|
void SetComment(const wxString& comment)
|
|
|
|
{ m_Comment = comment; }
|
|
|
|
|
|
|
|
bool Read(wxInputStream& stream, wxMBConv& conv);
|
|
|
|
bool Write(wxOutputStream& stream, wxMBConv& conv) const;
|
|
|
|
|
|
|
|
private:
|
|
|
|
wxUint16 m_DiskNumber;
|
|
|
|
wxUint16 m_StartDisk;
|
|
|
|
wxUint16 m_EntriesHere;
|
|
|
|
wxUint16 m_TotalEntries;
|
|
|
|
wxUint32 m_Size;
|
|
|
|
wxUint32 m_Offset;
|
|
|
|
wxString m_Comment;
|
|
|
|
};
|
|
|
|
|
|
|
|
wxZipEndRec::wxZipEndRec()
|
|
|
|
: m_DiskNumber(0),
|
|
|
|
m_StartDisk(0),
|
|
|
|
m_EntriesHere(0),
|
|
|
|
m_TotalEntries(0),
|
|
|
|
m_Size(0),
|
|
|
|
m_Offset(0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool wxZipEndRec::Write(wxOutputStream& stream, wxMBConv& conv) const
|
|
|
|
{
|
|
|
|
const wxWX2MBbuf comment_buf = m_Comment.mb_str(conv);
|
|
|
|
const char *comment = comment_buf;
|
|
|
|
if (!comment) comment = "";
|
|
|
|
wxUint16 commentLen = (wxUint16)strlen(comment);
|
|
|
|
|
|
|
|
wxDataOutputStream ds(stream);
|
|
|
|
|
|
|
|
ds << END_MAGIC << m_DiskNumber << m_StartDisk << m_EntriesHere
|
|
|
|
<< m_TotalEntries << m_Size << m_Offset << commentLen;
|
|
|
|
|
|
|
|
stream.Write(comment, commentLen);
|
|
|
|
|
|
|
|
return stream.IsOk();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool wxZipEndRec::Read(wxInputStream& stream, wxMBConv& conv)
|
|
|
|
{
|
|
|
|
wxZipHeader ds(stream, END_SIZE - 4);
|
|
|
|
if (!ds)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
wxUint16 commentLen;
|
|
|
|
|
|
|
|
ds >> m_DiskNumber >> m_StartDisk >> m_EntriesHere
|
|
|
|
>> m_TotalEntries >> m_Size >> m_Offset >> commentLen;
|
|
|
|
|
|
|
|
if (commentLen) {
|
|
|
|
m_Comment = ReadString(stream, commentLen, conv);
|
|
|
|
if (stream.LastRead() != commentLen + 0u)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_DiskNumber != 0 || m_StartDisk != 0 ||
|
|
|
|
m_EntriesHere != m_TotalEntries)
|
|
|
|
{
|
|
|
|
wxLogWarning(_("assuming this is a multi-part zip concatenated"));
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// A weak link from an input stream to an output stream
|
|
|
|
|
|
|
|
class wxZipStreamLink
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
wxZipStreamLink(wxZipOutputStream *stream) : m_ref(1), m_stream(stream) { }
|
|
|
|
|
|
|
|
wxZipStreamLink *AddRef() { m_ref++; return this; }
|
|
|
|
wxZipOutputStream *GetOutputStream() const { return m_stream; }
|
|
|
|
|
|
|
|
void Release(class wxZipInputStream *WXUNUSED(s))
|
|
|
|
{ if (--m_ref == 0) delete this; }
|
|
|
|
void Release(class wxZipOutputStream *WXUNUSED(s))
|
|
|
|
{ m_stream = NULL; if (--m_ref == 0) delete this; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
~wxZipStreamLink() { }
|
|
|
|
|
|
|
|
int m_ref;
|
|
|
|
wxZipOutputStream *m_stream;
|
|
|
|
|
|
|
|
wxSUPPRESS_GCC_PRIVATE_DTOR_WARNING(wxZipStreamLink)
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Input stream
|
|
|
|
|
|
|
|
// leave the default wxZipEntryPtr free for users
|
|
|
|
wxDECLARE_SCOPED_PTR(wxZipEntry, wxZipEntryPtr_)
|
|
|
|
wxDEFINE_SCOPED_PTR (wxZipEntry, wxZipEntryPtr_)
|
|
|
|
|
|
|
|
// constructor
|
|
|
|
//
|
|
|
|
wxZipInputStream::wxZipInputStream(wxInputStream& stream,
|
|
|
|
wxMBConv& conv /*=wxConvLocal*/)
|
|
|
|
: wxArchiveInputStream(stream, conv)
|
|
|
|
{
|
|
|
|
Init();
|
|
|
|
}
|
|
|
|
|
|
|
|
wxZipInputStream::wxZipInputStream(wxInputStream *stream,
|
|
|
|
wxMBConv& conv /*=wxConvLocal*/)
|
|
|
|
: wxArchiveInputStream(stream, conv)
|
|
|
|
{
|
|
|
|
Init();
|
|
|
|
}
|
|
|
|
|
|
|
|
#if WXWIN_COMPATIBILITY_2_6 && wxUSE_FFILE
|
|
|
|
|
|
|
|
// Part of the compatibility constructor, which has been made inline to
|
|
|
|
// avoid a problem with it not being exported by mingw 3.2.3
|
|
|
|
//
|
|
|
|
void wxZipInputStream::Init(const wxString& file)
|
|
|
|
{
|
|
|
|
// no error messages
|
|
|
|
wxLogNull nolog;
|
|
|
|
Init();
|
|
|
|
m_allowSeeking = true;
|
|
|
|
wxFFileInputStream *ffile;
|
|
|
|
ffile = static_cast<wxFFileInputStream*>(m_parent_i_stream);
|
|
|
|
wxZipEntryPtr_ entry;
|
|
|
|
|
2012-03-17 18:12:27 -07:00
|
|
|
if (ffile->IsOk()) {
|
2011-03-20 18:05:19 +00:00
|
|
|
do {
|
|
|
|
entry.reset(GetNextEntry());
|
|
|
|
}
|
|
|
|
while (entry.get() != NULL && entry->GetInternalName() != file);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (entry.get() == NULL)
|
|
|
|
m_lasterror = wxSTREAM_READ_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
wxInputStream* wxZipInputStream::OpenFile(const wxString& archive)
|
|
|
|
{
|
|
|
|
wxLogNull nolog;
|
|
|
|
return new wxFFileInputStream(archive);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // WXWIN_COMPATIBILITY_2_6 && wxUSE_FFILE
|
|
|
|
|
|
|
|
void wxZipInputStream::Init()
|
|
|
|
{
|
|
|
|
m_store = new wxStoredInputStream(*m_parent_i_stream);
|
|
|
|
m_inflate = NULL;
|
|
|
|
m_rawin = NULL;
|
|
|
|
m_raw = false;
|
|
|
|
m_headerSize = 0;
|
|
|
|
m_decomp = NULL;
|
|
|
|
m_parentSeekable = false;
|
|
|
|
m_weaklinks = new wxZipWeakLinks;
|
|
|
|
m_streamlink = NULL;
|
|
|
|
m_offsetAdjustment = 0;
|
|
|
|
m_position = wxInvalidOffset;
|
|
|
|
m_signature = 0;
|
|
|
|
m_TotalEntries = 0;
|
|
|
|
m_lasterror = m_parent_i_stream->GetLastError();
|
|
|
|
#if WXWIN_COMPATIBILITY_2_6
|
|
|
|
m_allowSeeking = false;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
wxZipInputStream::~wxZipInputStream()
|
|
|
|
{
|
|
|
|
CloseDecompressor(m_decomp);
|
|
|
|
|
|
|
|
delete m_store;
|
|
|
|
delete m_inflate;
|
|
|
|
delete m_rawin;
|
|
|
|
|
|
|
|
m_weaklinks->Release(this);
|
|
|
|
|
|
|
|
if (m_streamlink)
|
|
|
|
m_streamlink->Release(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
wxString wxZipInputStream::GetComment()
|
|
|
|
{
|
|
|
|
if (m_position == wxInvalidOffset)
|
|
|
|
if (!LoadEndRecord())
|
|
|
|
return wxEmptyString;
|
|
|
|
|
|
|
|
if (!m_parentSeekable && Eof() && m_signature) {
|
|
|
|
m_lasterror = wxSTREAM_NO_ERROR;
|
|
|
|
m_lasterror = ReadLocal(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
return m_Comment;
|
|
|
|
}
|
|
|
|
|
|
|
|
int wxZipInputStream::GetTotalEntries()
|
|
|
|
{
|
|
|
|
if (m_position == wxInvalidOffset)
|
|
|
|
LoadEndRecord();
|
|
|
|
return m_TotalEntries;
|
|
|
|
}
|
|
|
|
|
|
|
|
wxZipStreamLink *wxZipInputStream::MakeLink(wxZipOutputStream *out)
|
|
|
|
{
|
|
|
|
wxZipStreamLink *link = NULL;
|
|
|
|
|
|
|
|
if (!m_parentSeekable && (IsOpened() || !Eof())) {
|
|
|
|
link = new wxZipStreamLink(out);
|
|
|
|
if (m_streamlink)
|
|
|
|
m_streamlink->Release(this);
|
|
|
|
m_streamlink = link->AddRef();
|
|
|
|
}
|
|
|
|
|
|
|
|
return link;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool wxZipInputStream::LoadEndRecord()
|
|
|
|
{
|
|
|
|
wxCHECK(m_position == wxInvalidOffset, false);
|
|
|
|
if (!IsOk())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
m_position = 0;
|
|
|
|
|
|
|
|
// First find the end-of-central-directory record.
|
|
|
|
if (!FindEndRecord()) {
|
|
|
|
// failed, so either this is a non-seekable stream (ok), or not a zip
|
|
|
|
if (m_parentSeekable) {
|
|
|
|
m_lasterror = wxSTREAM_READ_ERROR;
|
|
|
|
wxLogError(_("invalid zip file"));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
wxLogNull nolog;
|
|
|
|
wxFileOffset pos = m_parent_i_stream->TellI();
|
|
|
|
if (pos != wxInvalidOffset)
|
|
|
|
m_offsetAdjustment = m_position = pos;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
wxZipEndRec endrec;
|
|
|
|
|
|
|
|
// Read in the end record
|
|
|
|
wxFileOffset endPos = m_parent_i_stream->TellI() - 4;
|
|
|
|
if (!endrec.Read(*m_parent_i_stream, GetConv()))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
m_TotalEntries = endrec.GetTotalEntries();
|
|
|
|
m_Comment = endrec.GetComment();
|
|
|
|
|
|
|
|
wxUint32 magic = m_TotalEntries ? CENTRAL_MAGIC : END_MAGIC;
|
|
|
|
|
|
|
|
// Now find the central-directory. we have the file offset of
|
|
|
|
// the CD, so look there first.
|
|
|
|
if (m_parent_i_stream->SeekI(endrec.GetOffset()) != wxInvalidOffset &&
|
|
|
|
ReadSignature() == magic) {
|
|
|
|
m_signature = magic;
|
|
|
|
m_position = endrec.GetOffset();
|
|
|
|
m_offsetAdjustment = 0;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If it's not there, then it could be that the zip has been appended
|
|
|
|
// to a self extractor, so take the CD size (also in endrec), subtract
|
|
|
|
// it from the file offset of the end-central-directory and look there.
|
|
|
|
if (m_parent_i_stream->SeekI(endPos - endrec.GetSize())
|
|
|
|
!= wxInvalidOffset && ReadSignature() == magic) {
|
|
|
|
m_signature = magic;
|
|
|
|
m_position = endPos - endrec.GetSize();
|
|
|
|
m_offsetAdjustment = m_position - endrec.GetOffset();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
wxLogError(_("can't find central directory in zip"));
|
|
|
|
m_lasterror = wxSTREAM_READ_ERROR;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find the end-of-central-directory record.
|
|
|
|
// If found the stream will be positioned just past the 4 signature bytes.
|
|
|
|
//
|
|
|
|
bool wxZipInputStream::FindEndRecord()
|
|
|
|
{
|
|
|
|
if (!m_parent_i_stream->IsSeekable())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// usually it's 22 bytes in size and the last thing in the file
|
|
|
|
{
|
|
|
|
wxLogNull nolog;
|
|
|
|
if (m_parent_i_stream->SeekI(-END_SIZE, wxFromEnd) == wxInvalidOffset)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_parentSeekable = true;
|
|
|
|
m_signature = 0;
|
|
|
|
char magic[4];
|
|
|
|
if (m_parent_i_stream->Read(magic, 4).LastRead() != 4)
|
|
|
|
return false;
|
|
|
|
if ((m_signature = CrackUint32(magic)) == END_MAGIC)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// unfortunately, the record has a comment field that can be up to 65535
|
|
|
|
// bytes in length, so if the signature not found then search backwards.
|
|
|
|
wxFileOffset pos = m_parent_i_stream->TellI();
|
|
|
|
const int BUFSIZE = 1024;
|
|
|
|
wxCharBuffer buf(BUFSIZE);
|
|
|
|
|
|
|
|
memcpy(buf.data(), magic, 3);
|
|
|
|
wxFileOffset minpos = wxMax(pos - 65535L, 0);
|
|
|
|
|
|
|
|
while (pos > minpos) {
|
|
|
|
size_t len = wx_truncate_cast(size_t,
|
|
|
|
pos - wxMax(pos - (BUFSIZE - 3), minpos));
|
|
|
|
memcpy(buf.data() + len, buf, 3);
|
|
|
|
pos -= len;
|
|
|
|
|
|
|
|
if (m_parent_i_stream->SeekI(pos, wxFromStart) == wxInvalidOffset ||
|
|
|
|
m_parent_i_stream->Read(buf.data(), len).LastRead() != len)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
char *p = buf.data() + len;
|
|
|
|
|
|
|
|
while (p-- > buf.data()) {
|
|
|
|
if ((m_signature = CrackUint32(p)) == END_MAGIC) {
|
|
|
|
size_t remainder = buf.data() + len - p;
|
|
|
|
if (remainder > 4)
|
|
|
|
m_parent_i_stream->Ungetch(p + 4, remainder - 4);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
wxZipEntry *wxZipInputStream::GetNextEntry()
|
|
|
|
{
|
|
|
|
if (m_position == wxInvalidOffset)
|
|
|
|
if (!LoadEndRecord())
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
m_lasterror = m_parentSeekable ? ReadCentral() : ReadLocal();
|
|
|
|
if (!IsOk())
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
wxZipEntryPtr_ entry(new wxZipEntry(m_entry));
|
|
|
|
entry->m_backlink = m_weaklinks->AddEntry(entry.get(), entry->GetKey());
|
|
|
|
return entry.release();
|
|
|
|
}
|
|
|
|
|
|
|
|
wxStreamError wxZipInputStream::ReadCentral()
|
|
|
|
{
|
|
|
|
if (!AtHeader())
|
|
|
|
CloseEntry();
|
|
|
|
|
|
|
|
if (m_signature == END_MAGIC)
|
|
|
|
return wxSTREAM_EOF;
|
|
|
|
|
|
|
|
if (m_signature != CENTRAL_MAGIC) {
|
|
|
|
wxLogError(_("error reading zip central directory"));
|
|
|
|
return wxSTREAM_READ_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (QuietSeek(*m_parent_i_stream, m_position + 4) == wxInvalidOffset)
|
|
|
|
return wxSTREAM_READ_ERROR;
|
|
|
|
|
|
|
|
size_t size = m_entry.ReadCentral(*m_parent_i_stream, GetConv());
|
|
|
|
if (!size) {
|
|
|
|
m_signature = 0;
|
|
|
|
return wxSTREAM_READ_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_position += size;
|
|
|
|
m_signature = ReadSignature();
|
|
|
|
|
|
|
|
if (m_offsetAdjustment)
|
|
|
|
m_entry.SetOffset(m_entry.GetOffset() + m_offsetAdjustment);
|
|
|
|
m_entry.SetKey(m_entry.GetOffset());
|
|
|
|
|
|
|
|
return wxSTREAM_NO_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
wxStreamError wxZipInputStream::ReadLocal(bool readEndRec /*=false*/)
|
|
|
|
{
|
|
|
|
if (!AtHeader())
|
|
|
|
CloseEntry();
|
|
|
|
|
|
|
|
if (!m_signature)
|
|
|
|
m_signature = ReadSignature();
|
|
|
|
|
|
|
|
if (m_signature == CENTRAL_MAGIC || m_signature == END_MAGIC) {
|
|
|
|
if (m_streamlink && !m_streamlink->GetOutputStream()) {
|
|
|
|
m_streamlink->Release(this);
|
|
|
|
m_streamlink = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while (m_signature == CENTRAL_MAGIC) {
|
|
|
|
if (m_weaklinks->IsEmpty() && m_streamlink == NULL)
|
|
|
|
return wxSTREAM_EOF;
|
|
|
|
|
|
|
|
size_t size = m_entry.ReadCentral(*m_parent_i_stream, GetConv());
|
|
|
|
m_position += size;
|
|
|
|
m_signature = 0;
|
|
|
|
if (!size)
|
|
|
|
return wxSTREAM_READ_ERROR;
|
|
|
|
|
|
|
|
wxZipEntry *entry = m_weaklinks->GetEntry(m_entry.GetOffset());
|
|
|
|
if (entry) {
|
|
|
|
entry->SetSystemMadeBy(m_entry.GetSystemMadeBy());
|
|
|
|
entry->SetVersionMadeBy(m_entry.GetVersionMadeBy());
|
|
|
|
entry->SetComment(m_entry.GetComment());
|
|
|
|
entry->SetDiskStart(m_entry.GetDiskStart());
|
|
|
|
entry->SetInternalAttributes(m_entry.GetInternalAttributes());
|
|
|
|
entry->SetExternalAttributes(m_entry.GetExternalAttributes());
|
|
|
|
Copy(entry->m_Extra, m_entry.m_Extra);
|
|
|
|
entry->Notify();
|
|
|
|
m_weaklinks->RemoveEntry(entry->GetOffset());
|
|
|
|
}
|
|
|
|
|
|
|
|
m_signature = ReadSignature();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_signature == END_MAGIC) {
|
|
|
|
if (readEndRec || m_streamlink) {
|
|
|
|
wxZipEndRec endrec;
|
|
|
|
endrec.Read(*m_parent_i_stream, GetConv());
|
|
|
|
m_Comment = endrec.GetComment();
|
|
|
|
m_signature = 0;
|
|
|
|
if (m_streamlink) {
|
|
|
|
m_streamlink->GetOutputStream()->SetComment(endrec.GetComment());
|
|
|
|
m_streamlink->Release(this);
|
|
|
|
m_streamlink = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return wxSTREAM_EOF;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_signature == LOCAL_MAGIC) {
|
|
|
|
m_headerSize = m_entry.ReadLocal(*m_parent_i_stream, GetConv());
|
|
|
|
m_signature = 0;
|
|
|
|
m_entry.SetOffset(m_position);
|
|
|
|
m_entry.SetKey(m_position);
|
|
|
|
|
|
|
|
if (m_headerSize) {
|
|
|
|
m_TotalEntries++;
|
|
|
|
return wxSTREAM_NO_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
wxLogError(_("error reading zip local header"));
|
|
|
|
return wxSTREAM_READ_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
wxUint32 wxZipInputStream::ReadSignature()
|
|
|
|
{
|
|
|
|
char magic[4];
|
|
|
|
m_parent_i_stream->Read(magic, 4);
|
|
|
|
return m_parent_i_stream->LastRead() == 4 ? CrackUint32(magic) : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool wxZipInputStream::OpenEntry(wxArchiveEntry& entry)
|
|
|
|
{
|
|
|
|
wxZipEntry *zipEntry = wxStaticCast(&entry, wxZipEntry);
|
|
|
|
return zipEntry ? OpenEntry(*zipEntry) : false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Open an entry
|
|
|
|
//
|
|
|
|
bool wxZipInputStream::DoOpen(wxZipEntry *entry, bool raw)
|
|
|
|
{
|
|
|
|
if (m_position == wxInvalidOffset)
|
|
|
|
if (!LoadEndRecord())
|
|
|
|
return false;
|
|
|
|
if (m_lasterror == wxSTREAM_READ_ERROR)
|
|
|
|
return false;
|
|
|
|
if (IsOpened())
|
|
|
|
CloseEntry();
|
|
|
|
|
|
|
|
m_raw = raw;
|
|
|
|
|
|
|
|
if (entry) {
|
|
|
|
if (AfterHeader() && entry->GetKey() == m_entry.GetOffset())
|
|
|
|
return true;
|
|
|
|
// can only open the current entry on a non-seekable stream
|
|
|
|
wxCHECK(m_parentSeekable, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_lasterror = wxSTREAM_READ_ERROR;
|
|
|
|
|
|
|
|
if (entry)
|
|
|
|
m_entry = *entry;
|
|
|
|
|
|
|
|
if (m_parentSeekable) {
|
|
|
|
if (QuietSeek(*m_parent_i_stream, m_entry.GetOffset())
|
|
|
|
== wxInvalidOffset)
|
|
|
|
return false;
|
|
|
|
if (ReadSignature() != LOCAL_MAGIC) {
|
|
|
|
wxLogError(_("bad zipfile offset to entry"));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_parentSeekable || AtHeader()) {
|
|
|
|
m_headerSize = m_entry.ReadLocal(*m_parent_i_stream, GetConv());
|
|
|
|
if (m_headerSize && m_parentSeekable) {
|
|
|
|
wxZipEntry *ref = m_weaklinks->GetEntry(m_entry.GetKey());
|
|
|
|
if (ref) {
|
|
|
|
Copy(ref->m_LocalExtra, m_entry.m_LocalExtra);
|
|
|
|
ref->Notify();
|
|
|
|
m_weaklinks->RemoveEntry(ref->GetKey());
|
|
|
|
}
|
|
|
|
if (entry && entry != ref) {
|
|
|
|
Copy(entry->m_LocalExtra, m_entry.m_LocalExtra);
|
|
|
|
entry->Notify();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_headerSize)
|
|
|
|
m_lasterror = wxSTREAM_NO_ERROR;
|
|
|
|
return IsOk();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool wxZipInputStream::OpenDecompressor(bool raw /*=false*/)
|
|
|
|
{
|
|
|
|
wxASSERT(AfterHeader());
|
|
|
|
|
|
|
|
wxFileOffset compressedSize = m_entry.GetCompressedSize();
|
|
|
|
|
|
|
|
if (raw)
|
|
|
|
m_raw = true;
|
|
|
|
|
|
|
|
if (m_raw) {
|
|
|
|
if (compressedSize != wxInvalidOffset) {
|
|
|
|
m_store->Open(compressedSize);
|
|
|
|
m_decomp = m_store;
|
|
|
|
} else {
|
|
|
|
if (!m_rawin)
|
|
|
|
m_rawin = new wxRawInputStream(*m_parent_i_stream);
|
|
|
|
m_decomp = m_rawin->Open(OpenDecompressor(m_rawin->GetTee()));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (compressedSize != wxInvalidOffset &&
|
|
|
|
(m_entry.GetMethod() != wxZIP_METHOD_DEFLATE ||
|
|
|
|
wxZlibInputStream::CanHandleGZip())) {
|
|
|
|
m_store->Open(compressedSize);
|
|
|
|
m_decomp = OpenDecompressor(*m_store);
|
|
|
|
} else {
|
|
|
|
m_decomp = OpenDecompressor(*m_parent_i_stream);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_crcAccumulator = crc32(0, Z_NULL, 0);
|
|
|
|
m_lasterror = m_decomp ? m_decomp->GetLastError() : wxSTREAM_READ_ERROR;
|
|
|
|
return IsOk();
|
|
|
|
}
|
|
|
|
|
2011-03-23 09:13:01 +00:00
|
|
|
// Can be overridden to add support for additional decompression methods
|
2011-03-20 18:05:19 +00:00
|
|
|
//
|
|
|
|
wxInputStream *wxZipInputStream::OpenDecompressor(wxInputStream& stream)
|
|
|
|
{
|
|
|
|
switch (m_entry.GetMethod()) {
|
|
|
|
case wxZIP_METHOD_STORE:
|
|
|
|
if (m_entry.GetSize() == wxInvalidOffset) {
|
|
|
|
wxLogError(_("stored file length not in Zip header"));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
m_store->Open(m_entry.GetSize());
|
|
|
|
return m_store;
|
|
|
|
|
|
|
|
case wxZIP_METHOD_DEFLATE:
|
|
|
|
if (!m_inflate)
|
|
|
|
m_inflate = new wxZlibInputStream2(stream);
|
|
|
|
else
|
|
|
|
m_inflate->Open(stream);
|
|
|
|
return m_inflate;
|
|
|
|
|
|
|
|
default:
|
|
|
|
wxLogError(_("unsupported Zip compression method"));
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool wxZipInputStream::CloseDecompressor(wxInputStream *decomp)
|
|
|
|
{
|
|
|
|
if (decomp && decomp == m_rawin)
|
|
|
|
return CloseDecompressor(m_rawin->GetFilterInputStream());
|
|
|
|
if (decomp != m_store && decomp != m_inflate)
|
|
|
|
delete decomp;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Closes the current entry and positions the underlying stream at the start
|
|
|
|
// of the next entry
|
|
|
|
//
|
|
|
|
bool wxZipInputStream::CloseEntry()
|
|
|
|
{
|
|
|
|
if (AtHeader())
|
|
|
|
return true;
|
|
|
|
if (m_lasterror == wxSTREAM_READ_ERROR)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!m_parentSeekable) {
|
|
|
|
if (!IsOpened() && !OpenDecompressor(true))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
const int BUFSIZE = 8192;
|
|
|
|
wxCharBuffer buf(BUFSIZE);
|
|
|
|
while (IsOk())
|
|
|
|
Read(buf.data(), BUFSIZE);
|
|
|
|
|
|
|
|
m_position += m_headerSize + m_entry.GetCompressedSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_lasterror == wxSTREAM_EOF)
|
|
|
|
m_lasterror = wxSTREAM_NO_ERROR;
|
|
|
|
|
|
|
|
CloseDecompressor(m_decomp);
|
|
|
|
m_decomp = NULL;
|
|
|
|
m_entry = wxZipEntry();
|
|
|
|
m_headerSize = 0;
|
|
|
|
m_raw = false;
|
|
|
|
|
|
|
|
return IsOk();
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t wxZipInputStream::OnSysRead(void *buffer, size_t size)
|
|
|
|
{
|
|
|
|
if (!IsOpened())
|
|
|
|
if ((AtHeader() && !DoOpen()) || !OpenDecompressor())
|
|
|
|
m_lasterror = wxSTREAM_READ_ERROR;
|
|
|
|
if (!IsOk() || !size)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
size_t count = m_decomp->Read(buffer, size).LastRead();
|
|
|
|
if (!m_raw)
|
|
|
|
m_crcAccumulator = crc32(m_crcAccumulator, (Byte*)buffer, count);
|
|
|
|
if (count < size)
|
|
|
|
m_lasterror = m_decomp->GetLastError();
|
|
|
|
|
|
|
|
if (Eof()) {
|
|
|
|
if ((m_entry.GetFlags() & wxZIP_SUMS_FOLLOW) != 0) {
|
|
|
|
m_headerSize += m_entry.ReadDescriptor(*m_parent_i_stream);
|
|
|
|
wxZipEntry *entry = m_weaklinks->GetEntry(m_entry.GetKey());
|
|
|
|
|
|
|
|
if (entry) {
|
|
|
|
entry->SetCrc(m_entry.GetCrc());
|
|
|
|
entry->SetCompressedSize(m_entry.GetCompressedSize());
|
|
|
|
entry->SetSize(m_entry.GetSize());
|
|
|
|
entry->Notify();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_raw) {
|
|
|
|
m_lasterror = wxSTREAM_READ_ERROR;
|
|
|
|
|
|
|
|
if (m_entry.GetSize() != TellI())
|
|
|
|
{
|
|
|
|
wxLogError(_("reading zip stream (entry %s): bad length"),
|
|
|
|
m_entry.GetName().c_str());
|
|
|
|
}
|
|
|
|
else if (m_crcAccumulator != m_entry.GetCrc())
|
|
|
|
{
|
|
|
|
wxLogError(_("reading zip stream (entry %s): bad crc"),
|
|
|
|
m_entry.GetName().c_str());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_lasterror = wxSTREAM_EOF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if WXWIN_COMPATIBILITY_2_6
|
|
|
|
|
|
|
|
// Borrowed from VS's zip stream (c) 1999 Vaclav Slavik
|
|
|
|
//
|
|
|
|
wxFileOffset wxZipInputStream::OnSysSeek(wxFileOffset seek, wxSeekMode mode)
|
|
|
|
{
|
|
|
|
// seeking works when the stream is created with the compatibility
|
|
|
|
// constructor
|
|
|
|
if (!m_allowSeeking)
|
|
|
|
return wxInvalidOffset;
|
|
|
|
if (!IsOpened())
|
|
|
|
if ((AtHeader() && !DoOpen()) || !OpenDecompressor())
|
|
|
|
m_lasterror = wxSTREAM_READ_ERROR;
|
|
|
|
if (!IsOk())
|
|
|
|
return wxInvalidOffset;
|
|
|
|
|
|
|
|
// NB: since ZIP files don't natively support seeking, we have to
|
|
|
|
// implement a brute force workaround -- reading all the data
|
|
|
|
// between current and the new position (or between beginning of
|
|
|
|
// the file and new position...)
|
|
|
|
|
|
|
|
wxFileOffset nextpos;
|
|
|
|
wxFileOffset pos = TellI();
|
|
|
|
|
|
|
|
switch ( mode )
|
|
|
|
{
|
|
|
|
case wxFromCurrent : nextpos = seek + pos; break;
|
|
|
|
case wxFromStart : nextpos = seek; break;
|
|
|
|
case wxFromEnd : nextpos = GetLength() + seek; break;
|
|
|
|
default : nextpos = pos; break; /* just to fool compiler, never happens */
|
|
|
|
}
|
|
|
|
|
|
|
|
wxFileOffset toskip wxDUMMY_INITIALIZE(0);
|
|
|
|
if ( nextpos >= pos )
|
|
|
|
{
|
|
|
|
toskip = nextpos - pos;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
wxZipEntry current(m_entry);
|
|
|
|
if (!OpenEntry(current))
|
|
|
|
{
|
|
|
|
m_lasterror = wxSTREAM_READ_ERROR;
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
toskip = nextpos;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( toskip > 0 )
|
|
|
|
{
|
|
|
|
const int BUFSIZE = 4096;
|
|
|
|
size_t sz;
|
|
|
|
char buffer[BUFSIZE];
|
|
|
|
while ( toskip > 0 )
|
|
|
|
{
|
|
|
|
sz = wx_truncate_cast(size_t, wxMin(toskip, BUFSIZE));
|
|
|
|
Read(buffer, sz);
|
|
|
|
toskip -= sz;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pos = nextpos;
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // WXWIN_COMPATIBILITY_2_6
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Output stream
|
|
|
|
|
|
|
|
#include "wx/listimpl.cpp"
|
|
|
|
WX_DEFINE_LIST(wxZipEntryList_)
|
|
|
|
|
|
|
|
wxZipOutputStream::wxZipOutputStream(wxOutputStream& stream,
|
|
|
|
int level /*=-1*/,
|
|
|
|
wxMBConv& conv /*=wxConvLocal*/)
|
|
|
|
: wxArchiveOutputStream(stream, conv)
|
|
|
|
{
|
|
|
|
Init(level);
|
|
|
|
}
|
|
|
|
|
|
|
|
wxZipOutputStream::wxZipOutputStream(wxOutputStream *stream,
|
|
|
|
int level /*=-1*/,
|
|
|
|
wxMBConv& conv /*=wxConvLocal*/)
|
|
|
|
: wxArchiveOutputStream(stream, conv)
|
|
|
|
{
|
|
|
|
Init(level);
|
|
|
|
}
|
|
|
|
|
|
|
|
void wxZipOutputStream::Init(int level)
|
|
|
|
{
|
|
|
|
m_store = new wxStoredOutputStream(*m_parent_o_stream);
|
|
|
|
m_deflate = NULL;
|
|
|
|
m_backlink = NULL;
|
|
|
|
m_initialData = new char[OUTPUT_LATENCY];
|
|
|
|
m_initialSize = 0;
|
|
|
|
m_pending = NULL;
|
|
|
|
m_raw = false;
|
|
|
|
m_headerOffset = 0;
|
|
|
|
m_headerSize = 0;
|
|
|
|
m_entrySize = 0;
|
|
|
|
m_comp = NULL;
|
|
|
|
m_level = level;
|
|
|
|
m_offsetAdjustment = wxInvalidOffset;
|
|
|
|
m_endrecWritten = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
wxZipOutputStream::~wxZipOutputStream()
|
|
|
|
{
|
|
|
|
Close();
|
|
|
|
WX_CLEAR_LIST(wxZipEntryList_, m_entries);
|
|
|
|
delete m_store;
|
|
|
|
delete m_deflate;
|
|
|
|
delete m_pending;
|
|
|
|
delete [] m_initialData;
|
|
|
|
if (m_backlink)
|
|
|
|
m_backlink->Release(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool wxZipOutputStream::PutNextEntry(
|
|
|
|
const wxString& name,
|
|
|
|
const wxDateTime& dt /*=wxDateTime::Now()*/,
|
|
|
|
wxFileOffset size /*=wxInvalidOffset*/)
|
|
|
|
{
|
|
|
|
return PutNextEntry(new wxZipEntry(name, dt, size));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool wxZipOutputStream::PutNextDirEntry(
|
|
|
|
const wxString& name,
|
|
|
|
const wxDateTime& dt /*=wxDateTime::Now()*/)
|
|
|
|
{
|
|
|
|
wxZipEntry *entry = new wxZipEntry(name, dt);
|
|
|
|
entry->SetIsDir();
|
|
|
|
return PutNextEntry(entry);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool wxZipOutputStream::CopyEntry(wxZipEntry *entry,
|
|
|
|
wxZipInputStream& inputStream)
|
|
|
|
{
|
|
|
|
wxZipEntryPtr_ e(entry);
|
|
|
|
|
|
|
|
return
|
|
|
|
inputStream.DoOpen(e.get(), true) &&
|
|
|
|
DoCreate(e.release(), true) &&
|
|
|
|
Write(inputStream).IsOk() && inputStream.Eof();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool wxZipOutputStream::PutNextEntry(wxArchiveEntry *entry)
|
|
|
|
{
|
|
|
|
wxZipEntry *zipEntry = wxStaticCast(entry, wxZipEntry);
|
|
|
|
if (!zipEntry)
|
|
|
|
delete entry;
|
|
|
|
return PutNextEntry(zipEntry);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool wxZipOutputStream::CopyEntry(wxArchiveEntry *entry,
|
|
|
|
wxArchiveInputStream& stream)
|
|
|
|
{
|
|
|
|
wxZipEntry *zipEntry = wxStaticCast(entry, wxZipEntry);
|
|
|
|
|
|
|
|
if (!zipEntry || !stream.OpenEntry(*zipEntry)) {
|
|
|
|
delete entry;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return CopyEntry(zipEntry, static_cast<wxZipInputStream&>(stream));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool wxZipOutputStream::CopyArchiveMetaData(wxZipInputStream& inputStream)
|
|
|
|
{
|
|
|
|
m_Comment = inputStream.GetComment();
|
|
|
|
if (m_backlink)
|
|
|
|
m_backlink->Release(this);
|
|
|
|
m_backlink = inputStream.MakeLink(this);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool wxZipOutputStream::CopyArchiveMetaData(wxArchiveInputStream& stream)
|
|
|
|
{
|
|
|
|
return CopyArchiveMetaData(static_cast<wxZipInputStream&>(stream));
|
|
|
|
}
|
|
|
|
|
|
|
|
void wxZipOutputStream::SetLevel(int level)
|
|
|
|
{
|
|
|
|
if (level != m_level) {
|
|
|
|
if (m_comp != m_deflate)
|
|
|
|
delete m_deflate;
|
|
|
|
m_deflate = NULL;
|
|
|
|
m_level = level;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool wxZipOutputStream::DoCreate(wxZipEntry *entry, bool raw /*=false*/)
|
|
|
|
{
|
|
|
|
CloseEntry();
|
|
|
|
|
|
|
|
m_pending = entry;
|
|
|
|
if (!m_pending)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// write the signature bytes right away
|
|
|
|
wxDataOutputStream ds(*m_parent_o_stream);
|
|
|
|
ds << LOCAL_MAGIC;
|
|
|
|
|
|
|
|
// and if this is the first entry test for seekability
|
|
|
|
if (m_headerOffset == 0 && m_parent_o_stream->IsSeekable()) {
|
|
|
|
#if wxUSE_LOG
|
|
|
|
bool logging = wxLog::IsEnabled();
|
|
|
|
wxLogNull nolog;
|
|
|
|
#endif // wxUSE_LOG
|
|
|
|
wxFileOffset here = m_parent_o_stream->TellO();
|
|
|
|
|
|
|
|
if (here != wxInvalidOffset && here >= 4) {
|
|
|
|
if (m_parent_o_stream->SeekO(here - 4) == here - 4) {
|
|
|
|
m_offsetAdjustment = here - 4;
|
|
|
|
#if wxUSE_LOG
|
|
|
|
wxLog::EnableLogging(logging);
|
|
|
|
#endif // wxUSE_LOG
|
|
|
|
m_parent_o_stream->SeekO(here);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_pending->SetOffset(m_headerOffset);
|
|
|
|
|
|
|
|
m_crcAccumulator = crc32(0, Z_NULL, 0);
|
|
|
|
|
|
|
|
if (raw)
|
|
|
|
m_raw = true;
|
|
|
|
|
|
|
|
m_lasterror = wxSTREAM_NO_ERROR;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-03-23 09:13:01 +00:00
|
|
|
// Can be overridden to add support for additional compression methods
|
2011-03-20 18:05:19 +00:00
|
|
|
//
|
|
|
|
wxOutputStream *wxZipOutputStream::OpenCompressor(
|
|
|
|
wxOutputStream& stream,
|
|
|
|
wxZipEntry& entry,
|
|
|
|
const Buffer bufs[])
|
|
|
|
{
|
|
|
|
if (entry.GetMethod() == wxZIP_METHOD_DEFAULT) {
|
|
|
|
if (GetLevel() == 0
|
|
|
|
&& (IsParentSeekable()
|
|
|
|
|| entry.GetCompressedSize() != wxInvalidOffset
|
|
|
|
|| entry.GetSize() != wxInvalidOffset)) {
|
|
|
|
entry.SetMethod(wxZIP_METHOD_STORE);
|
|
|
|
} else {
|
|
|
|
int size = 0;
|
|
|
|
for (int i = 0; bufs[i].m_data; ++i)
|
|
|
|
size += bufs[i].m_size;
|
|
|
|
entry.SetMethod(size <= 6 ?
|
|
|
|
wxZIP_METHOD_STORE : wxZIP_METHOD_DEFLATE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (entry.GetMethod()) {
|
|
|
|
case wxZIP_METHOD_STORE:
|
|
|
|
if (entry.GetCompressedSize() == wxInvalidOffset)
|
|
|
|
entry.SetCompressedSize(entry.GetSize());
|
|
|
|
return m_store;
|
|
|
|
|
|
|
|
case wxZIP_METHOD_DEFLATE:
|
|
|
|
{
|
|
|
|
int defbits = wxZIP_DEFLATE_NORMAL;
|
|
|
|
switch (GetLevel()) {
|
|
|
|
case 0: case 1:
|
|
|
|
defbits = wxZIP_DEFLATE_SUPERFAST;
|
|
|
|
break;
|
|
|
|
case 2: case 3: case 4:
|
|
|
|
defbits = wxZIP_DEFLATE_FAST;
|
|
|
|
break;
|
|
|
|
case 8: case 9:
|
|
|
|
defbits = wxZIP_DEFLATE_EXTRA;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
entry.SetFlags((entry.GetFlags() & ~wxZIP_DEFLATE_MASK) |
|
|
|
|
defbits | wxZIP_SUMS_FOLLOW);
|
|
|
|
|
|
|
|
if (!m_deflate)
|
|
|
|
m_deflate = new wxZlibOutputStream2(stream, GetLevel());
|
|
|
|
else
|
|
|
|
m_deflate->Open(stream);
|
|
|
|
|
|
|
|
return m_deflate;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
wxLogError(_("unsupported Zip compression method"));
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool wxZipOutputStream::CloseCompressor(wxOutputStream *comp)
|
|
|
|
{
|
|
|
|
if (comp == m_deflate)
|
|
|
|
m_deflate->Close();
|
|
|
|
else if (comp != m_store)
|
|
|
|
delete comp;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is called when OUPUT_LATENCY bytes has been written to the
|
|
|
|
// wxZipOutputStream to actually create the zip entry.
|
|
|
|
//
|
|
|
|
void wxZipOutputStream::CreatePendingEntry(const void *buffer, size_t size)
|
|
|
|
{
|
|
|
|
wxASSERT(IsOk() && m_pending && !m_comp);
|
|
|
|
wxZipEntryPtr_ spPending(m_pending);
|
|
|
|
m_pending = NULL;
|
|
|
|
|
|
|
|
Buffer bufs[] = {
|
|
|
|
{ m_initialData, m_initialSize },
|
|
|
|
{ (const char*)buffer, size },
|
|
|
|
{ NULL, 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
if (m_raw)
|
|
|
|
m_comp = m_store;
|
|
|
|
else
|
|
|
|
m_comp = OpenCompressor(*m_store, *spPending,
|
|
|
|
m_initialSize ? bufs : bufs + 1);
|
|
|
|
|
|
|
|
if (IsParentSeekable()
|
|
|
|
|| (spPending->m_Crc
|
|
|
|
&& spPending->m_CompressedSize != wxInvalidOffset
|
|
|
|
&& spPending->m_Size != wxInvalidOffset))
|
|
|
|
spPending->m_Flags &= ~wxZIP_SUMS_FOLLOW;
|
|
|
|
else
|
|
|
|
if (spPending->m_CompressedSize != wxInvalidOffset)
|
|
|
|
spPending->m_Flags |= wxZIP_SUMS_FOLLOW;
|
|
|
|
|
|
|
|
m_headerSize = spPending->WriteLocal(*m_parent_o_stream, GetConv());
|
|
|
|
m_lasterror = m_parent_o_stream->GetLastError();
|
|
|
|
|
|
|
|
if (IsOk()) {
|
|
|
|
m_entries.push_back(spPending.release());
|
|
|
|
OnSysWrite(m_initialData, m_initialSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_initialSize = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is called to write out the zip entry when Close has been called
|
|
|
|
// before OUTPUT_LATENCY bytes has been written to the wxZipOutputStream.
|
|
|
|
//
|
|
|
|
void wxZipOutputStream::CreatePendingEntry()
|
|
|
|
{
|
|
|
|
wxASSERT(IsOk() && m_pending && !m_comp);
|
|
|
|
wxZipEntryPtr_ spPending(m_pending);
|
|
|
|
m_pending = NULL;
|
|
|
|
m_lasterror = wxSTREAM_WRITE_ERROR;
|
|
|
|
|
|
|
|
if (!m_raw) {
|
|
|
|
// Initially compresses the data to memory, then fall back to 'store'
|
|
|
|
// if the compressor makes the data larger rather than smaller.
|
|
|
|
wxMemoryOutputStream mem;
|
|
|
|
Buffer bufs[] = { { m_initialData, m_initialSize }, { NULL, 0 } };
|
|
|
|
wxOutputStream *comp = OpenCompressor(mem, *spPending, bufs);
|
|
|
|
|
|
|
|
if (!comp)
|
|
|
|
return;
|
|
|
|
if (comp != m_store) {
|
|
|
|
bool ok = comp->Write(m_initialData, m_initialSize).IsOk();
|
|
|
|
CloseCompressor(comp);
|
|
|
|
if (!ok)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_entrySize = m_initialSize;
|
|
|
|
m_crcAccumulator = crc32(0, (Byte*)m_initialData, m_initialSize);
|
|
|
|
|
|
|
|
if (mem.GetSize() > 0 && mem.GetSize() < m_initialSize) {
|
|
|
|
m_initialSize = mem.GetSize();
|
|
|
|
mem.CopyTo(m_initialData, m_initialSize);
|
|
|
|
} else {
|
|
|
|
spPending->SetMethod(wxZIP_METHOD_STORE);
|
|
|
|
}
|
|
|
|
|
|
|
|
spPending->SetSize(m_entrySize);
|
|
|
|
spPending->SetCrc(m_crcAccumulator);
|
|
|
|
spPending->SetCompressedSize(m_initialSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
spPending->m_Flags &= ~wxZIP_SUMS_FOLLOW;
|
|
|
|
m_headerSize = spPending->WriteLocal(*m_parent_o_stream, GetConv());
|
|
|
|
|
|
|
|
if (m_parent_o_stream->IsOk()) {
|
|
|
|
m_entries.push_back(spPending.release());
|
|
|
|
m_comp = m_store;
|
|
|
|
m_store->Write(m_initialData, m_initialSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_initialSize = 0;
|
|
|
|
m_lasterror = m_parent_o_stream->GetLastError();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write the 'central directory' and the 'end-central-directory' records.
|
|
|
|
//
|
|
|
|
bool wxZipOutputStream::Close()
|
|
|
|
{
|
|
|
|
CloseEntry();
|
|
|
|
|
|
|
|
if (m_lasterror == wxSTREAM_WRITE_ERROR
|
|
|
|
|| (m_entries.size() == 0 && m_endrecWritten))
|
|
|
|
{
|
|
|
|
wxFilterOutputStream::Close();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
wxZipEndRec endrec;
|
|
|
|
|
|
|
|
endrec.SetEntriesHere(m_entries.size());
|
|
|
|
endrec.SetTotalEntries(m_entries.size());
|
|
|
|
endrec.SetOffset(m_headerOffset);
|
|
|
|
endrec.SetComment(m_Comment);
|
|
|
|
|
|
|
|
wxZipEntryList_::iterator it;
|
|
|
|
wxFileOffset size = 0;
|
|
|
|
|
|
|
|
for (it = m_entries.begin(); it != m_entries.end(); ++it) {
|
|
|
|
size += (*it)->WriteCentral(*m_parent_o_stream, GetConv());
|
|
|
|
delete *it;
|
|
|
|
}
|
|
|
|
m_entries.clear();
|
|
|
|
|
|
|
|
endrec.SetSize(size);
|
|
|
|
endrec.Write(*m_parent_o_stream, GetConv());
|
|
|
|
|
|
|
|
m_lasterror = m_parent_o_stream->GetLastError();
|
|
|
|
m_endrecWritten = true;
|
|
|
|
|
|
|
|
if (!wxFilterOutputStream::Close() || !IsOk())
|
|
|
|
return false;
|
|
|
|
m_lasterror = wxSTREAM_EOF;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finish writing the current entry
|
|
|
|
//
|
|
|
|
bool wxZipOutputStream::CloseEntry()
|
|
|
|
{
|
|
|
|
if (IsOk() && m_pending)
|
|
|
|
CreatePendingEntry();
|
|
|
|
if (!IsOk())
|
|
|
|
return false;
|
|
|
|
if (!m_comp)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
CloseCompressor(m_comp);
|
|
|
|
m_comp = NULL;
|
|
|
|
|
|
|
|
wxFileOffset compressedSize = m_store->TellO();
|
|
|
|
|
|
|
|
wxZipEntry& entry = *m_entries.back();
|
|
|
|
|
|
|
|
// When writing raw the crc and size can't be checked
|
|
|
|
if (m_raw) {
|
|
|
|
m_crcAccumulator = entry.GetCrc();
|
|
|
|
m_entrySize = entry.GetSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write the sums in the trailing 'data descriptor' if necessary
|
|
|
|
if (entry.m_Flags & wxZIP_SUMS_FOLLOW) {
|
|
|
|
wxASSERT(!IsParentSeekable());
|
|
|
|
m_headerOffset +=
|
|
|
|
entry.WriteDescriptor(*m_parent_o_stream, m_crcAccumulator,
|
|
|
|
compressedSize, m_entrySize);
|
|
|
|
m_lasterror = m_parent_o_stream->GetLastError();
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the local header didn't have the correct crc and size written to
|
|
|
|
// it then seek back and fix it
|
|
|
|
else if (m_crcAccumulator != entry.GetCrc()
|
|
|
|
|| m_entrySize != entry.GetSize()
|
|
|
|
|| compressedSize != entry.GetCompressedSize())
|
|
|
|
{
|
|
|
|
if (IsParentSeekable()) {
|
|
|
|
wxFileOffset here = m_parent_o_stream->TellO();
|
|
|
|
wxFileOffset headerOffset = m_headerOffset + m_offsetAdjustment;
|
|
|
|
m_parent_o_stream->SeekO(headerOffset + SUMS_OFFSET);
|
|
|
|
entry.WriteDescriptor(*m_parent_o_stream, m_crcAccumulator,
|
|
|
|
compressedSize, m_entrySize);
|
|
|
|
m_parent_o_stream->SeekO(here);
|
|
|
|
m_lasterror = m_parent_o_stream->GetLastError();
|
|
|
|
} else {
|
|
|
|
m_lasterror = wxSTREAM_WRITE_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_headerOffset += m_headerSize + compressedSize;
|
|
|
|
m_headerSize = 0;
|
|
|
|
m_entrySize = 0;
|
|
|
|
m_store->Close();
|
|
|
|
m_raw = false;
|
|
|
|
|
|
|
|
if (IsOk())
|
|
|
|
m_lasterror = m_parent_o_stream->GetLastError();
|
|
|
|
else
|
|
|
|
wxLogError(_("error writing zip entry '%s': bad crc or length"),
|
|
|
|
entry.GetName().c_str());
|
|
|
|
return IsOk();
|
|
|
|
}
|
|
|
|
|
|
|
|
void wxZipOutputStream::Sync()
|
|
|
|
{
|
|
|
|
if (IsOk() && m_pending)
|
|
|
|
CreatePendingEntry(NULL, 0);
|
|
|
|
if (!m_comp)
|
|
|
|
m_lasterror = wxSTREAM_WRITE_ERROR;
|
|
|
|
if (IsOk()) {
|
|
|
|
m_comp->Sync();
|
|
|
|
m_lasterror = m_comp->GetLastError();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t wxZipOutputStream::OnSysWrite(const void *buffer, size_t size)
|
|
|
|
{
|
|
|
|
if (IsOk() && m_pending) {
|
|
|
|
if (m_initialSize + size < OUTPUT_LATENCY) {
|
|
|
|
memcpy(m_initialData + m_initialSize, buffer, size);
|
|
|
|
m_initialSize += size;
|
|
|
|
return size;
|
|
|
|
} else {
|
|
|
|
CreatePendingEntry(buffer, size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_comp)
|
|
|
|
m_lasterror = wxSTREAM_WRITE_ERROR;
|
|
|
|
if (!IsOk() || !size)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (m_comp->Write(buffer, size).LastWrite() != size)
|
|
|
|
m_lasterror = wxSTREAM_WRITE_ERROR;
|
|
|
|
m_crcAccumulator = crc32(m_crcAccumulator, (Byte*)buffer, size);
|
|
|
|
m_entrySize += m_comp->LastWrite();
|
|
|
|
|
|
|
|
return m_comp->LastWrite();
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // wxUSE_ZIPSTREAM
|