dolphin-emulator/Externals/wxWidgets3/src/common/zipstrm.cpp
Soren Jorvang d14efe561b Import r67258 of the wxWidgets trunk, which I expect will before
long become wxWidgets 2.9.2, which in turn is expected to be the
last 2.9 release before the 3.0 stable release.

Since the full wxWidgets distribution is rather large, I have
imported only the parts that we use, on a subdirectory basis:

art
include/wx/*.*
include/wx/aui
include/wx/cocoa
include/wx/generic
include/wx/gtk
include/wx/meta
include/wx/msw
include/wx/osx
include/wx/persist
include/wx/private
include/wx/protocol
include/wx/unix
src/aui
src/common
src/generic
src/gtk
src/msw
src/osx
src/unix


git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@7380 8ced0084-cf51-0410-be5f-012b33b47a6e
2011-03-20 18:05:19 +00:00

2439 lines
66 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: src/common/zipstrm.cpp
// Purpose: Streams for Zip files
// Author: Mike Wetherell
// RCS-ID: $Id: zipstrm.cpp 61508 2009-07-23 20:30:22Z VZ $
// 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 = &notifier;
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;
if (ffile->Ok()) {
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();
}
// Can be overriden to add support for additional decompression methods
//
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;
}
// Can be overriden to add support for additional compression methods
//
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