1///////////////////////////////////////////////////////////////////////////// 2// Name: src/common/zipstrm.cpp 3// Purpose: Streams for Zip files 4// Author: Mike Wetherell 5// RCS-ID: $Id: zipstrm.cpp 51009 2008-01-03 17:11:45Z MW $ 6// Copyright: (c) Mike Wetherell 7// Licence: wxWindows licence 8///////////////////////////////////////////////////////////////////////////// 9 10// For compilers that support precompilation, includes "wx.h". 11#include "wx/wxprec.h" 12 13#ifdef __BORLANDC__ 14 #pragma hdrstop 15#endif 16 17#if wxUSE_ZIPSTREAM 18 19#include "wx/zipstrm.h" 20 21#ifndef WX_PRECOMP 22 #include "wx/hashmap.h" 23 #include "wx/intl.h" 24 #include "wx/log.h" 25 #include "wx/utils.h" 26#endif 27 28#include "wx/datstrm.h" 29#include "wx/zstream.h" 30#include "wx/mstream.h" 31#include "wx/ptr_scpd.h" 32#include "wx/wfstream.h" 33#include "zlib.h" 34 35// value for the 'version needed to extract' field (20 means 2.0) 36enum { 37 VERSION_NEEDED_TO_EXTRACT = 20 38}; 39 40// signatures for the various records (PKxx) 41enum { 42 CENTRAL_MAGIC = 0x02014b50, // central directory record 43 LOCAL_MAGIC = 0x04034b50, // local header 44 END_MAGIC = 0x06054b50, // end of central directory record 45 SUMS_MAGIC = 0x08074b50 // data descriptor (info-zip) 46}; 47 48// unix file attributes. zip stores them in the high 16 bits of the 49// 'external attributes' field, hence the extra zeros. 50enum { 51 wxZIP_S_IFMT = 0xF0000000, 52 wxZIP_S_IFDIR = 0x40000000, 53 wxZIP_S_IFREG = 0x80000000 54}; 55 56// minimum sizes for the various records 57enum { 58 CENTRAL_SIZE = 46, 59 LOCAL_SIZE = 30, 60 END_SIZE = 22, 61 SUMS_SIZE = 12 62}; 63 64// The number of bytes that must be written to an wxZipOutputStream before 65// a zip entry is created. The purpose of this latency is so that 66// OpenCompressor() can see a little data before deciding which compressor 67// it should use. 68enum { 69 OUTPUT_LATENCY = 4096 70}; 71 72// Some offsets into the local header 73enum { 74 SUMS_OFFSET = 14 75}; 76 77IMPLEMENT_DYNAMIC_CLASS(wxZipEntry, wxArchiveEntry) 78IMPLEMENT_DYNAMIC_CLASS(wxZipClassFactory, wxArchiveClassFactory) 79 80 81///////////////////////////////////////////////////////////////////////////// 82// Helpers 83 84// read a string of a given length 85// 86static wxString ReadString(wxInputStream& stream, wxUint16 len, wxMBConv& conv) 87{ 88 if (len == 0) 89 return wxEmptyString; 90 91#if wxUSE_UNICODE 92 wxCharBuffer buf(len); 93 stream.Read(buf.data(), len); 94 wxString str(buf, conv); 95#else 96 wxString str; 97 (void)conv; 98 { 99 wxStringBuffer buf(str, len); 100 stream.Read(buf, len); 101 } 102#endif 103 104 return str; 105} 106 107// Decode a little endian wxUint32 number from a character array 108// 109static inline wxUint32 CrackUint32(const char *m) 110{ 111 const unsigned char *n = (const unsigned char*)m; 112 return (n[3] << 24) | (n[2] << 16) | (n[1] << 8) | n[0]; 113} 114 115// Decode a little endian wxUint16 number from a character array 116// 117static inline wxUint16 CrackUint16(const char *m) 118{ 119 const unsigned char *n = (const unsigned char*)m; 120 return (n[1] << 8) | n[0]; 121} 122 123// Temporarily lower the logging level in debug mode to avoid a warning 124// from SeekI about seeking on a stream with data written back to it. 125// 126static wxFileOffset QuietSeek(wxInputStream& stream, wxFileOffset pos) 127{ 128#if defined(__WXDEBUG__) && wxUSE_LOG 129 wxLogLevel level = wxLog::GetLogLevel(); 130 wxLog::SetLogLevel(wxLOG_Debug - 1); 131 wxFileOffset result = stream.SeekI(pos); 132 wxLog::SetLogLevel(level); 133 return result; 134#else 135 return stream.SeekI(pos); 136#endif 137} 138 139 140///////////////////////////////////////////////////////////////////////////// 141// Class factory 142 143wxZipClassFactory g_wxZipClassFactory; 144 145wxZipClassFactory::wxZipClassFactory() 146{ 147 if (this == &g_wxZipClassFactory) 148 PushFront(); 149} 150 151const wxChar * const * 152wxZipClassFactory::GetProtocols(wxStreamProtocolType type) const 153{ 154 static const wxChar *protocols[] = { _T("zip"), NULL }; 155 static const wxChar *mimetypes[] = { _T("application/zip"), NULL }; 156 static const wxChar *fileexts[] = { _T(".zip"), _T(".htb"), NULL }; 157 static const wxChar *empty[] = { NULL }; 158 159 switch (type) { 160 case wxSTREAM_PROTOCOL: return protocols; 161 case wxSTREAM_MIMETYPE: return mimetypes; 162 case wxSTREAM_FILEEXT: return fileexts; 163 default: return empty; 164 } 165} 166 167 168///////////////////////////////////////////////////////////////////////////// 169// Read a zip header 170 171class wxZipHeader 172{ 173public: 174 wxZipHeader(wxInputStream& stream, size_t size); 175 176 inline wxUint8 Read8(); 177 inline wxUint16 Read16(); 178 inline wxUint32 Read32(); 179 180 const char *GetData() const { return m_data; } 181 size_t GetSize() const { return m_size; } 182 operator bool() const { return m_ok; } 183 184 size_t Seek(size_t pos) { m_pos = pos; return m_pos; } 185 size_t Skip(size_t size) { m_pos += size; return m_pos; } 186 187 wxZipHeader& operator>>(wxUint8& n) { n = Read8(); return *this; } 188 wxZipHeader& operator>>(wxUint16& n) { n = Read16(); return *this; } 189 wxZipHeader& operator>>(wxUint32& n) { n = Read32(); return *this; } 190 191private: 192 char m_data[64]; 193 size_t m_size; 194 size_t m_pos; 195 bool m_ok; 196}; 197 198wxZipHeader::wxZipHeader(wxInputStream& stream, size_t size) 199 : m_size(0), 200 m_pos(0), 201 m_ok(false) 202{ 203 wxCHECK_RET(size <= sizeof(m_data), _T("buffer too small")); 204 m_size = stream.Read(m_data, size).LastRead(); 205 m_ok = m_size == size; 206} 207 208inline wxUint8 wxZipHeader::Read8() 209{ 210 wxASSERT(m_pos < m_size); 211 return m_data[m_pos++]; 212} 213 214inline wxUint16 wxZipHeader::Read16() 215{ 216 wxASSERT(m_pos + 2 <= m_size); 217 wxUint16 n = CrackUint16(m_data + m_pos); 218 m_pos += 2; 219 return n; 220} 221 222inline wxUint32 wxZipHeader::Read32() 223{ 224 wxASSERT(m_pos + 4 <= m_size); 225 wxUint32 n = CrackUint32(m_data + m_pos); 226 m_pos += 4; 227 return n; 228} 229 230 231///////////////////////////////////////////////////////////////////////////// 232// Stored input stream 233// Trival decompressor for files which are 'stored' in the zip file. 234 235class wxStoredInputStream : public wxFilterInputStream 236{ 237public: 238 wxStoredInputStream(wxInputStream& stream); 239 240 void Open(wxFileOffset len) { Close(); m_len = len; } 241 void Close() { m_pos = 0; m_lasterror = wxSTREAM_NO_ERROR; } 242 243 virtual char Peek() { return wxInputStream::Peek(); } 244 virtual wxFileOffset GetLength() const { return m_len; } 245 246protected: 247 virtual size_t OnSysRead(void *buffer, size_t size); 248 virtual wxFileOffset OnSysTell() const { return m_pos; } 249 250private: 251 wxFileOffset m_pos; 252 wxFileOffset m_len; 253 254 DECLARE_NO_COPY_CLASS(wxStoredInputStream) 255}; 256 257wxStoredInputStream::wxStoredInputStream(wxInputStream& stream) 258 : wxFilterInputStream(stream), 259 m_pos(0), 260 m_len(0) 261{ 262} 263 264size_t wxStoredInputStream::OnSysRead(void *buffer, size_t size) 265{ 266 size_t count = wx_truncate_cast(size_t, 267 wxMin(size + wxFileOffset(0), m_len - m_pos + size_t(0))); 268 count = m_parent_i_stream->Read(buffer, count).LastRead(); 269 m_pos += count; 270 271 if (count < size) 272 m_lasterror = m_pos == m_len ? wxSTREAM_EOF : wxSTREAM_READ_ERROR; 273 274 return count; 275} 276 277 278///////////////////////////////////////////////////////////////////////////// 279// Stored output stream 280// Trival compressor for files which are 'stored' in the zip file. 281 282class wxStoredOutputStream : public wxFilterOutputStream 283{ 284public: 285 wxStoredOutputStream(wxOutputStream& stream) : 286 wxFilterOutputStream(stream), m_pos(0) { } 287 288 bool Close() { 289 m_pos = 0; 290 m_lasterror = wxSTREAM_NO_ERROR; 291 return true; 292 } 293 294protected: 295 virtual size_t OnSysWrite(const void *buffer, size_t size); 296 virtual wxFileOffset OnSysTell() const { return m_pos; } 297 298private: 299 wxFileOffset m_pos; 300 DECLARE_NO_COPY_CLASS(wxStoredOutputStream) 301}; 302 303size_t wxStoredOutputStream::OnSysWrite(const void *buffer, size_t size) 304{ 305 if (!IsOk() || !size) 306 return 0; 307 size_t count = m_parent_o_stream->Write(buffer, size).LastWrite(); 308 if (count != size) 309 m_lasterror = wxSTREAM_WRITE_ERROR; 310 m_pos += count; 311 return count; 312} 313 314 315///////////////////////////////////////////////////////////////////////////// 316// wxRawInputStream 317// 318// Used to handle the unusal case of raw copying an entry of unknown 319// length. This can only happen when the zip being copied from is being 320// read from a non-seekable stream, and also was original written to a 321// non-seekable stream. 322// 323// In this case there's no option but to decompress the stream to find 324// it's length, but we can still write the raw compressed data to avoid the 325// compression overhead (which is the greater one). 326// 327// Usage is like this: 328// m_rawin = new wxRawInputStream(*m_parent_i_stream); 329// m_decomp = m_rawin->Open(OpenDecompressor(m_rawin->GetTee())); 330// 331// The wxRawInputStream owns a wxTeeInputStream object, the role of which 332// is something like the unix 'tee' command; it is a transparent filter, but 333// allows the data read to be read a second time via an extra method 'GetData'. 334// 335// The wxRawInputStream then draws data through the tee using a decompressor 336// then instead of returning the decompressed data, retuns the raw data 337// from wxTeeInputStream::GetData(). 338 339class wxTeeInputStream : public wxFilterInputStream 340{ 341public: 342 wxTeeInputStream(wxInputStream& stream); 343 344 size_t GetCount() const { return m_end - m_start; } 345 size_t GetData(char *buffer, size_t size); 346 347 void Open(); 348 bool Final(); 349 350 wxInputStream& Read(void *buffer, size_t size); 351 352protected: 353 virtual size_t OnSysRead(void *buffer, size_t size); 354 virtual wxFileOffset OnSysTell() const { return m_pos; } 355 356private: 357 wxFileOffset m_pos; 358 wxMemoryBuffer m_buf; 359 size_t m_start; 360 size_t m_end; 361 362 DECLARE_NO_COPY_CLASS(wxTeeInputStream) 363}; 364 365wxTeeInputStream::wxTeeInputStream(wxInputStream& stream) 366 : wxFilterInputStream(stream), 367 m_pos(0), m_buf(8192), m_start(0), m_end(0) 368{ 369} 370 371void wxTeeInputStream::Open() 372{ 373 m_pos = m_start = m_end = 0; 374 m_lasterror = wxSTREAM_NO_ERROR; 375} 376 377bool wxTeeInputStream::Final() 378{ 379 bool final = m_end == m_buf.GetDataLen(); 380 m_end = m_buf.GetDataLen(); 381 return final; 382} 383 384wxInputStream& wxTeeInputStream::Read(void *buffer, size_t size) 385{ 386 size_t count = wxInputStream::Read(buffer, size).LastRead(); 387 m_end = m_buf.GetDataLen(); 388 m_buf.AppendData(buffer, count); 389 return *this; 390} 391 392size_t wxTeeInputStream::OnSysRead(void *buffer, size_t size) 393{ 394 size_t count = m_parent_i_stream->Read(buffer, size).LastRead(); 395 if (count < size) 396 m_lasterror = m_parent_i_stream->GetLastError(); 397 return count; 398} 399 400size_t wxTeeInputStream::GetData(char *buffer, size_t size) 401{ 402 if (m_wbacksize) { 403 size_t len = m_buf.GetDataLen(); 404 len = len > m_wbacksize ? len - m_wbacksize : 0; 405 m_buf.SetDataLen(len); 406 if (m_end > len) { 407 wxFAIL; // we've already returned data that's now being ungot 408 m_end = len; 409 } 410 m_parent_i_stream->Reset(); 411 m_parent_i_stream->Ungetch(m_wback, m_wbacksize); 412 free(m_wback); 413 m_wback = NULL; 414 m_wbacksize = 0; 415 m_wbackcur = 0; 416 } 417 418 if (size > GetCount()) 419 size = GetCount(); 420 if (size) { 421 memcpy(buffer, m_buf + m_start, size); 422 m_start += size; 423 wxASSERT(m_start <= m_end); 424 } 425 426 if (m_start == m_end && m_start > 0 && m_buf.GetDataLen() > 0) { 427 size_t len = m_buf.GetDataLen(); 428 char *buf = (char*)m_buf.GetWriteBuf(len); 429 len -= m_end; 430 memmove(buf, buf + m_end, len); 431 m_buf.UngetWriteBuf(len); 432 m_start = m_end = 0; 433 } 434 435 return size; 436} 437 438class wxRawInputStream : public wxFilterInputStream 439{ 440public: 441 wxRawInputStream(wxInputStream& stream); 442 virtual ~wxRawInputStream() { delete m_tee; } 443 444 wxInputStream* Open(wxInputStream *decomp); 445 wxInputStream& GetTee() const { return *m_tee; } 446 447protected: 448 virtual size_t OnSysRead(void *buffer, size_t size); 449 virtual wxFileOffset OnSysTell() const { return m_pos; } 450 451private: 452 wxFileOffset m_pos; 453 wxTeeInputStream *m_tee; 454 455 enum { BUFSIZE = 8192 }; 456 wxCharBuffer m_dummy; 457 458 DECLARE_NO_COPY_CLASS(wxRawInputStream) 459}; 460 461wxRawInputStream::wxRawInputStream(wxInputStream& stream) 462 : wxFilterInputStream(stream), 463 m_pos(0), 464 m_tee(new wxTeeInputStream(stream)), 465 m_dummy(BUFSIZE) 466{ 467} 468 469wxInputStream *wxRawInputStream::Open(wxInputStream *decomp) 470{ 471 if (decomp) { 472 m_parent_i_stream = decomp; 473 m_pos = 0; 474 m_lasterror = wxSTREAM_NO_ERROR; 475 m_tee->Open(); 476 return this; 477 } else { 478 return NULL; 479 } 480} 481 482size_t wxRawInputStream::OnSysRead(void *buffer, size_t size) 483{ 484 char *buf = (char*)buffer; 485 size_t count = 0; 486 487 while (count < size && IsOk()) 488 { 489 while (m_parent_i_stream->IsOk() && m_tee->GetCount() == 0) 490 m_parent_i_stream->Read(m_dummy.data(), BUFSIZE); 491 492 size_t n = m_tee->GetData(buf + count, size - count); 493 count += n; 494 495 if (n == 0 && m_tee->Final()) 496 m_lasterror = m_parent_i_stream->GetLastError(); 497 } 498 499 m_pos += count; 500 return count; 501} 502 503 504///////////////////////////////////////////////////////////////////////////// 505// Zlib streams than can be reused without recreating. 506 507class wxZlibOutputStream2 : public wxZlibOutputStream 508{ 509public: 510 wxZlibOutputStream2(wxOutputStream& stream, int level) : 511 wxZlibOutputStream(stream, level, wxZLIB_NO_HEADER) { } 512 513 bool Open(wxOutputStream& stream); 514 bool Close() { DoFlush(true); m_pos = wxInvalidOffset; return IsOk(); } 515}; 516 517bool wxZlibOutputStream2::Open(wxOutputStream& stream) 518{ 519 wxCHECK(m_pos == wxInvalidOffset, false); 520 521 m_deflate->next_out = m_z_buffer; 522 m_deflate->avail_out = m_z_size; 523 m_pos = 0; 524 m_lasterror = wxSTREAM_NO_ERROR; 525 m_parent_o_stream = &stream; 526 527 if (deflateReset(m_deflate) != Z_OK) { 528 wxLogError(_("can't re-initialize zlib deflate stream")); 529 m_lasterror = wxSTREAM_WRITE_ERROR; 530 return false; 531 } 532 533 return true; 534} 535 536class wxZlibInputStream2 : public wxZlibInputStream 537{ 538public: 539 wxZlibInputStream2(wxInputStream& stream) : 540 wxZlibInputStream(stream, wxZLIB_NO_HEADER) { } 541 542 bool Open(wxInputStream& stream); 543}; 544 545bool wxZlibInputStream2::Open(wxInputStream& stream) 546{ 547 m_inflate->avail_in = 0; 548 m_pos = 0; 549 m_lasterror = wxSTREAM_NO_ERROR; 550 m_parent_i_stream = &stream; 551 552 if (inflateReset(m_inflate) != Z_OK) { 553 wxLogError(_("can't re-initialize zlib inflate stream")); 554 m_lasterror = wxSTREAM_READ_ERROR; 555 return false; 556 } 557 558 return true; 559} 560 561 562///////////////////////////////////////////////////////////////////////////// 563// Class to hold wxZipEntry's Extra and LocalExtra fields 564 565class wxZipMemory 566{ 567public: 568 wxZipMemory() : m_data(NULL), m_size(0), m_capacity(0), m_ref(1) { } 569 570 wxZipMemory *AddRef() { m_ref++; return this; } 571 void Release() { if (--m_ref == 0) delete this; } 572 573 char *GetData() const { return m_data; } 574 size_t GetSize() const { return m_size; } 575 size_t GetCapacity() const { return m_capacity; } 576 577 wxZipMemory *Unique(size_t size); 578 579private: 580 ~wxZipMemory() { delete [] m_data; } 581 582 char *m_data; 583 size_t m_size; 584 size_t m_capacity; 585 int m_ref; 586 587 wxSUPPRESS_GCC_PRIVATE_DTOR_WARNING(wxZipMemory) 588}; 589 590wxZipMemory *wxZipMemory::Unique(size_t size) 591{ 592 wxZipMemory *zm; 593 594 if (m_ref > 1) { 595 --m_ref; 596 zm = new wxZipMemory; 597 } else { 598 zm = this; 599 } 600 601 if (zm->m_capacity < size) { 602 delete [] zm->m_data; 603 zm->m_data = new char[size]; 604 zm->m_capacity = size; 605 } 606 607 zm->m_size = size; 608 return zm; 609} 610 611static inline wxZipMemory *AddRef(wxZipMemory *zm) 612{ 613 if (zm) 614 zm->AddRef(); 615 return zm; 616} 617 618static inline void Release(wxZipMemory *zm) 619{ 620 if (zm) 621 zm->Release(); 622} 623 624static void Copy(wxZipMemory*& dest, wxZipMemory *src) 625{ 626 Release(dest); 627 dest = AddRef(src); 628} 629 630static void Unique(wxZipMemory*& zm, size_t size) 631{ 632 if (!zm && size) 633 zm = new wxZipMemory; 634 if (zm) 635 zm = zm->Unique(size); 636} 637 638 639///////////////////////////////////////////////////////////////////////////// 640// Collection of weak references to entries 641 642WX_DECLARE_HASH_MAP(long, wxZipEntry*, wxIntegerHash, 643 wxIntegerEqual, wxOffsetZipEntryMap_); 644 645class wxZipWeakLinks 646{ 647public: 648 wxZipWeakLinks() : m_ref(1) { } 649 650 void Release(const wxZipInputStream* WXUNUSED(x)) 651 { if (--m_ref == 0) delete this; } 652 void Release(wxFileOffset key) 653 { RemoveEntry(key); if (--m_ref == 0) delete this; } 654 655 wxZipWeakLinks *AddEntry(wxZipEntry *entry, wxFileOffset key); 656 void RemoveEntry(wxFileOffset key) 657 { m_entries.erase(wx_truncate_cast(key_type, key)); } 658 wxZipEntry *GetEntry(wxFileOffset key) const; 659 bool IsEmpty() const { return m_entries.empty(); } 660 661private: 662 ~wxZipWeakLinks() { wxASSERT(IsEmpty()); } 663 664 typedef wxOffsetZipEntryMap_::key_type key_type; 665 666 int m_ref; 667 wxOffsetZipEntryMap_ m_entries; 668 669 wxSUPPRESS_GCC_PRIVATE_DTOR_WARNING(wxZipWeakLinks) 670}; 671 672wxZipWeakLinks *wxZipWeakLinks::AddEntry(wxZipEntry *entry, wxFileOffset key) 673{ 674 m_entries[wx_truncate_cast(key_type, key)] = entry; 675 m_ref++; 676 return this; 677} 678 679wxZipEntry *wxZipWeakLinks::GetEntry(wxFileOffset key) const 680{ 681 wxOffsetZipEntryMap_::const_iterator it = 682 m_entries.find(wx_truncate_cast(key_type, key)); 683 return it != m_entries.end() ? it->second : NULL; 684} 685 686 687///////////////////////////////////////////////////////////////////////////// 688// ZipEntry 689 690wxZipEntry::wxZipEntry( 691 const wxString& name /*=wxEmptyString*/, 692 const wxDateTime& dt /*=wxDateTime::Now()*/, 693 wxFileOffset size /*=wxInvalidOffset*/) 694 : 695 m_SystemMadeBy(wxZIP_SYSTEM_MSDOS), 696 m_VersionMadeBy(wxMAJOR_VERSION * 10 + wxMINOR_VERSION), 697 m_VersionNeeded(VERSION_NEEDED_TO_EXTRACT), 698 m_Flags(0), 699 m_Method(wxZIP_METHOD_DEFAULT), 700 m_DateTime(dt), 701 m_Crc(0), 702 m_CompressedSize(wxInvalidOffset), 703 m_Size(size), 704 m_Key(wxInvalidOffset), 705 m_Offset(wxInvalidOffset), 706 m_DiskStart(0), 707 m_InternalAttributes(0), 708 m_ExternalAttributes(0), 709 m_Extra(NULL), 710 m_LocalExtra(NULL), 711 m_zipnotifier(NULL), 712 m_backlink(NULL) 713{ 714 if (!name.empty()) 715 SetName(name); 716} 717 718wxZipEntry::~wxZipEntry() 719{ 720 if (m_backlink) 721 m_backlink->Release(m_Key); 722 Release(m_Extra); 723 Release(m_LocalExtra); 724} 725 726wxZipEntry::wxZipEntry(const wxZipEntry& e) 727 : wxArchiveEntry(e), 728 m_SystemMadeBy(e.m_SystemMadeBy), 729 m_VersionMadeBy(e.m_VersionMadeBy), 730 m_VersionNeeded(e.m_VersionNeeded), 731 m_Flags(e.m_Flags), 732 m_Method(e.m_Method), 733 m_DateTime(e.m_DateTime), 734 m_Crc(e.m_Crc), 735 m_CompressedSize(e.m_CompressedSize), 736 m_Size(e.m_Size), 737 m_Name(e.m_Name), 738 m_Key(e.m_Key), 739 m_Offset(e.m_Offset), 740 m_Comment(e.m_Comment), 741 m_DiskStart(e.m_DiskStart), 742 m_InternalAttributes(e.m_InternalAttributes), 743 m_ExternalAttributes(e.m_ExternalAttributes), 744 m_Extra(AddRef(e.m_Extra)), 745 m_LocalExtra(AddRef(e.m_LocalExtra)), 746 m_zipnotifier(NULL), 747 m_backlink(NULL) 748{ 749} 750 751wxZipEntry& wxZipEntry::operator=(const wxZipEntry& e) 752{ 753 if (&e != this) { 754 m_SystemMadeBy = e.m_SystemMadeBy; 755 m_VersionMadeBy = e.m_VersionMadeBy; 756 m_VersionNeeded = e.m_VersionNeeded; 757 m_Flags = e.m_Flags; 758 m_Method = e.m_Method; 759 m_DateTime = e.m_DateTime; 760 m_Crc = e.m_Crc; 761 m_CompressedSize = e.m_CompressedSize; 762 m_Size = e.m_Size; 763 m_Name = e.m_Name; 764 m_Key = e.m_Key; 765 m_Offset = e.m_Offset; 766 m_Comment = e.m_Comment; 767 m_DiskStart = e.m_DiskStart; 768 m_InternalAttributes = e.m_InternalAttributes; 769 m_ExternalAttributes = e.m_ExternalAttributes; 770 Copy(m_Extra, e.m_Extra); 771 Copy(m_LocalExtra, e.m_LocalExtra); 772 m_zipnotifier = NULL; 773 if (m_backlink) { 774 m_backlink->Release(m_Key); 775 m_backlink = NULL; 776 } 777 } 778 return *this; 779} 780 781wxString wxZipEntry::GetName(wxPathFormat format /*=wxPATH_NATIVE*/) const 782{ 783 bool isDir = IsDir() && !m_Name.empty(); 784 785 // optimisations for common (and easy) cases 786 switch (wxFileName::GetFormat(format)) { 787 case wxPATH_DOS: 788 { 789 wxString name(isDir ? m_Name + _T("\\") : m_Name); 790 for (size_t i = 0; i < name.length(); i++) 791 if (name[i] == _T('/')) 792 name[i] = _T('\\'); 793 return name; 794 } 795 796 case wxPATH_UNIX: 797 return isDir ? m_Name + _T("/") : m_Name; 798 799 default: 800 ; 801 } 802 803 wxFileName fn; 804 805 if (isDir) 806 fn.AssignDir(m_Name, wxPATH_UNIX); 807 else 808 fn.Assign(m_Name, wxPATH_UNIX); 809 810 return fn.GetFullPath(format); 811} 812 813// Static - Internally tars and zips use forward slashes for the path 814// separator, absolute paths aren't allowed, and directory names have a 815// trailing slash. This function converts a path into this internal format, 816// but without a trailing slash for a directory. 817// 818wxString wxZipEntry::GetInternalName(const wxString& name, 819 wxPathFormat format /*=wxPATH_NATIVE*/, 820 bool *pIsDir /*=NULL*/) 821{ 822 wxString internal; 823 824 if (wxFileName::GetFormat(format) != wxPATH_UNIX) 825 internal = wxFileName(name, format).GetFullPath(wxPATH_UNIX); 826 else 827 internal = name; 828 829 bool isDir = !internal.empty() && internal.Last() == '/'; 830 if (pIsDir) 831 *pIsDir = isDir; 832 if (isDir) 833 internal.erase(internal.length() - 1); 834 835 while (!internal.empty() && *internal.begin() == '/') 836 internal.erase(0, 1); 837 while (!internal.empty() && internal.compare(0, 2, _T("./")) == 0) 838 internal.erase(0, 2); 839 if (internal == _T(".") || internal == _T("..")) 840 internal = wxEmptyString; 841 842 return internal; 843} 844 845void wxZipEntry::SetSystemMadeBy(int system) 846{ 847 int mode = GetMode(); 848 bool wasUnix = IsMadeByUnix(); 849 850 m_SystemMadeBy = (wxUint8)system; 851 852 if (!wasUnix && IsMadeByUnix()) { 853 SetIsDir(IsDir()); 854 SetMode(mode); 855 } else if (wasUnix && !IsMadeByUnix()) { 856 m_ExternalAttributes &= 0xffff; 857 } 858} 859 860void wxZipEntry::SetIsDir(bool isDir /*=true*/) 861{ 862 if (isDir) 863 m_ExternalAttributes |= wxZIP_A_SUBDIR; 864 else 865 m_ExternalAttributes &= ~wxZIP_A_SUBDIR; 866 867 if (IsMadeByUnix()) { 868 m_ExternalAttributes &= ~wxZIP_S_IFMT; 869 if (isDir) 870 m_ExternalAttributes |= wxZIP_S_IFDIR; 871 else 872 m_ExternalAttributes |= wxZIP_S_IFREG; 873 } 874} 875 876// Return unix style permission bits 877// 878int wxZipEntry::GetMode() const 879{ 880 // return unix permissions if present 881 if (IsMadeByUnix()) 882 return (m_ExternalAttributes >> 16) & 0777; 883 884 // otherwise synthesize from the dos attribs 885 int mode = 0644; 886 if (m_ExternalAttributes & wxZIP_A_RDONLY) 887 mode &= ~0200; 888 if (m_ExternalAttributes & wxZIP_A_SUBDIR) 889 mode |= 0111; 890 891 return mode; 892} 893 894// Set unix permissions 895// 896void wxZipEntry::SetMode(int mode) 897{ 898 // Set dos attrib bits to be compatible 899 if (mode & 0222) 900 m_ExternalAttributes &= ~wxZIP_A_RDONLY; 901 else 902 m_ExternalAttributes |= wxZIP_A_RDONLY; 903 904 // set the actual unix permission bits if the system type allows 905 if (IsMadeByUnix()) { 906 m_ExternalAttributes &= ~(0777L << 16); 907 m_ExternalAttributes |= (mode & 0777L) << 16; 908 } 909} 910 911const char *wxZipEntry::GetExtra() const 912{ 913 return m_Extra ? m_Extra->GetData() : NULL; 914} 915 916size_t wxZipEntry::GetExtraLen() const 917{ 918 return m_Extra ? m_Extra->GetSize() : 0; 919} 920 921void wxZipEntry::SetExtra(const char *extra, size_t len) 922{ 923 Unique(m_Extra, len); 924 if (len) 925 memcpy(m_Extra->GetData(), extra, len); 926} 927 928const char *wxZipEntry::GetLocalExtra() const 929{ 930 return m_LocalExtra ? m_LocalExtra->GetData() : NULL; 931} 932 933size_t wxZipEntry::GetLocalExtraLen() const 934{ 935 return m_LocalExtra ? m_LocalExtra->GetSize() : 0; 936} 937 938void wxZipEntry::SetLocalExtra(const char *extra, size_t len) 939{ 940 Unique(m_LocalExtra, len); 941 if (len) 942 memcpy(m_LocalExtra->GetData(), extra, len); 943} 944 945void wxZipEntry::SetNotifier(wxZipNotifier& notifier) 946{ 947 wxArchiveEntry::UnsetNotifier(); 948 m_zipnotifier = ¬ifier; 949 m_zipnotifier->OnEntryUpdated(*this); 950} 951 952void wxZipEntry::Notify() 953{ 954 if (m_zipnotifier) 955 m_zipnotifier->OnEntryUpdated(*this); 956 else if (GetNotifier()) 957 GetNotifier()->OnEntryUpdated(*this); 958} 959 960void wxZipEntry::UnsetNotifier() 961{ 962 wxArchiveEntry::UnsetNotifier(); 963 m_zipnotifier = NULL; 964} 965 966size_t wxZipEntry::ReadLocal(wxInputStream& stream, wxMBConv& conv) 967{ 968 wxUint16 nameLen, extraLen; 969 wxUint32 compressedSize, size, crc; 970 971 wxZipHeader ds(stream, LOCAL_SIZE - 4); 972 if (!ds) 973 return 0; 974 975 ds >> m_VersionNeeded >> m_Flags >> m_Method; 976 SetDateTime(wxDateTime().SetFromDOS(ds.Read32())); 977 ds >> crc >> compressedSize >> size >> nameLen >> extraLen; 978 979 bool sumsValid = (m_Flags & wxZIP_SUMS_FOLLOW) == 0; 980 981 if (sumsValid || crc) 982 m_Crc = crc; 983 if ((sumsValid || compressedSize) || m_Method == wxZIP_METHOD_STORE) 984 m_CompressedSize = compressedSize; 985 if ((sumsValid || size) || m_Method == wxZIP_METHOD_STORE) 986 m_Size = size; 987 988 SetName(ReadString(stream, nameLen, conv), wxPATH_UNIX); 989 if (stream.LastRead() != nameLen + 0u) 990 return 0; 991 992 if (extraLen || GetLocalExtraLen()) { 993 Unique(m_LocalExtra, extraLen); 994 if (extraLen) { 995 stream.Read(m_LocalExtra->GetData(), extraLen); 996 if (stream.LastRead() != extraLen + 0u) 997 return 0; 998 } 999 } 1000 1001 return LOCAL_SIZE + nameLen + extraLen; 1002} 1003 1004size_t wxZipEntry::WriteLocal(wxOutputStream& stream, wxMBConv& conv) const 1005{ 1006 wxString unixName = GetName(wxPATH_UNIX); 1007 const wxWX2MBbuf name_buf = conv.cWX2MB(unixName); 1008 const char *name = name_buf; 1009 if (!name) name = ""; 1010 wxUint16 nameLen = wx_truncate_cast(wxUint16, strlen(name)); 1011 1012 wxDataOutputStream ds(stream); 1013 1014 ds << m_VersionNeeded << m_Flags << m_Method; 1015 ds.Write32(GetDateTime().GetAsDOS()); 1016 1017 ds.Write32(m_Crc); 1018 ds.Write32(m_CompressedSize != wxInvalidOffset ? 1019 wx_truncate_cast(wxUint32, m_CompressedSize) : 0); 1020 ds.Write32(m_Size != wxInvalidOffset ? 1021 wx_truncate_cast(wxUint32, m_Size) : 0); 1022 1023 ds << nameLen; 1024 wxUint16 extraLen = wx_truncate_cast(wxUint16, GetLocalExtraLen()); 1025 ds.Write16(extraLen); 1026 1027 stream.Write(name, nameLen); 1028 if (extraLen) 1029 stream.Write(m_LocalExtra->GetData(), extraLen); 1030 1031 return LOCAL_SIZE + nameLen + extraLen; 1032} 1033 1034size_t wxZipEntry::ReadCentral(wxInputStream& stream, wxMBConv& conv) 1035{ 1036 wxUint16 nameLen, extraLen, commentLen; 1037 1038 wxZipHeader ds(stream, CENTRAL_SIZE - 4); 1039 if (!ds) 1040 return 0; 1041 1042 ds >> m_VersionMadeBy >> m_SystemMadeBy; 1043 1044 SetVersionNeeded(ds.Read16()); 1045 SetFlags(ds.Read16()); 1046 SetMethod(ds.Read16()); 1047 SetDateTime(wxDateTime().SetFromDOS(ds.Read32())); 1048 SetCrc(ds.Read32()); 1049 SetCompressedSize(ds.Read32()); 1050 SetSize(ds.Read32()); 1051 1052 ds >> nameLen >> extraLen >> commentLen 1053 >> m_DiskStart >> m_InternalAttributes >> m_ExternalAttributes; 1054 SetOffset(ds.Read32()); 1055 1056 SetName(ReadString(stream, nameLen, conv), wxPATH_UNIX); 1057 if (stream.LastRead() != nameLen + 0u) 1058 return 0; 1059 1060 if (extraLen || GetExtraLen()) { 1061 Unique(m_Extra, extraLen); 1062 if (extraLen) { 1063 stream.Read(m_Extra->GetData(), extraLen); 1064 if (stream.LastRead() != extraLen + 0u) 1065 return 0; 1066 } 1067 } 1068 1069 if (commentLen) { 1070 m_Comment = ReadString(stream, commentLen, conv); 1071 if (stream.LastRead() != commentLen + 0u) 1072 return 0; 1073 } else { 1074 m_Comment.clear(); 1075 } 1076 1077 return CENTRAL_SIZE + nameLen + extraLen + commentLen; 1078} 1079 1080size_t wxZipEntry::WriteCentral(wxOutputStream& stream, wxMBConv& conv) const 1081{ 1082 wxString unixName = GetName(wxPATH_UNIX); 1083 const wxWX2MBbuf name_buf = conv.cWX2MB(unixName); 1084 const char *name = name_buf; 1085 if (!name) name = ""; 1086 wxUint16 nameLen = wx_truncate_cast(wxUint16, strlen(name)); 1087 1088 const wxWX2MBbuf comment_buf = conv.cWX2MB(m_Comment); 1089 const char *comment = comment_buf; 1090 if (!comment) comment = ""; 1091 wxUint16 commentLen = wx_truncate_cast(wxUint16, strlen(comment)); 1092 1093 wxUint16 extraLen = wx_truncate_cast(wxUint16, GetExtraLen()); 1094 1095 wxDataOutputStream ds(stream); 1096 1097 ds << CENTRAL_MAGIC << m_VersionMadeBy << m_SystemMadeBy; 1098 1099 ds.Write16(wx_truncate_cast(wxUint16, GetVersionNeeded())); 1100 ds.Write16(wx_truncate_cast(wxUint16, GetFlags())); 1101 ds.Write16(wx_truncate_cast(wxUint16, GetMethod())); 1102 ds.Write32(GetDateTime().GetAsDOS()); 1103 ds.Write32(GetCrc()); 1104 ds.Write32(wx_truncate_cast(wxUint32, GetCompressedSize())); 1105 ds.Write32(wx_truncate_cast(wxUint32, GetSize())); 1106 ds.Write16(nameLen); 1107 ds.Write16(extraLen); 1108 1109 ds << commentLen << m_DiskStart << m_InternalAttributes 1110 << m_ExternalAttributes << wx_truncate_cast(wxUint32, GetOffset()); 1111 1112 stream.Write(name, nameLen); 1113 if (extraLen) 1114 stream.Write(GetExtra(), extraLen); 1115 stream.Write(comment, commentLen); 1116 1117 return CENTRAL_SIZE + nameLen + extraLen + commentLen; 1118} 1119 1120// Info-zip prefixes this record with a signature, but pkzip doesn't. So if 1121// the 1st value is the signature then it is probably an info-zip record, 1122// though there is a small chance that it is in fact a pkzip record which 1123// happens to have the signature as it's CRC. 1124// 1125size_t wxZipEntry::ReadDescriptor(wxInputStream& stream) 1126{ 1127 wxZipHeader ds(stream, SUMS_SIZE); 1128 if (!ds) 1129 return 0; 1130 1131 m_Crc = ds.Read32(); 1132 m_CompressedSize = ds.Read32(); 1133 m_Size = ds.Read32(); 1134 1135 // if 1st value is the signature then this is probably an info-zip record 1136 if (m_Crc == SUMS_MAGIC) 1137 { 1138 wxZipHeader buf(stream, 8); 1139 wxUint32 u1 = buf.GetSize() >= 4 ? buf.Read32() : (wxUint32)LOCAL_MAGIC; 1140 wxUint32 u2 = buf.GetSize() == 8 ? buf.Read32() : 0; 1141 1142 // look for the signature of the following record to decide which 1143 if ((u1 == LOCAL_MAGIC || u1 == CENTRAL_MAGIC) && 1144 (u2 != LOCAL_MAGIC && u2 != CENTRAL_MAGIC)) 1145 { 1146 // it's a pkzip style record after all! 1147 if (buf.GetSize() > 0) 1148 stream.Ungetch(buf.GetData(), buf.GetSize()); 1149 } 1150 else 1151 { 1152 // it's an info-zip record as expected 1153 if (buf.GetSize() > 4) 1154 stream.Ungetch(buf.GetData() + 4, buf.GetSize() - 4); 1155 m_Crc = wx_truncate_cast(wxUint32, m_CompressedSize); 1156 m_CompressedSize = m_Size; 1157 m_Size = u1; 1158 return SUMS_SIZE + 4; 1159 } 1160 } 1161 1162 return SUMS_SIZE; 1163} 1164 1165size_t wxZipEntry::WriteDescriptor(wxOutputStream& stream, wxUint32 crc, 1166 wxFileOffset compressedSize, wxFileOffset size) 1167{ 1168 m_Crc = crc; 1169 m_CompressedSize = compressedSize; 1170 m_Size = size; 1171 1172 wxDataOutputStream ds(stream); 1173 1174 ds.Write32(crc); 1175 ds.Write32(wx_truncate_cast(wxUint32, compressedSize)); 1176 ds.Write32(wx_truncate_cast(wxUint32, size)); 1177 1178 return SUMS_SIZE; 1179} 1180 1181 1182///////////////////////////////////////////////////////////////////////////// 1183// wxZipEndRec - holds the end of central directory record 1184 1185class wxZipEndRec 1186{ 1187public: 1188 wxZipEndRec(); 1189 1190 int GetDiskNumber() const { return m_DiskNumber; } 1191 int GetStartDisk() const { return m_StartDisk; } 1192 int GetEntriesHere() const { return m_EntriesHere; } 1193 int GetTotalEntries() const { return m_TotalEntries; } 1194 wxFileOffset GetSize() const { return m_Size; } 1195 wxFileOffset GetOffset() const { return m_Offset; } 1196 wxString GetComment() const { return m_Comment; } 1197 1198 void SetDiskNumber(int num) 1199 { m_DiskNumber = wx_truncate_cast(wxUint16, num); } 1200 void SetStartDisk(int num) 1201 { m_StartDisk = wx_truncate_cast(wxUint16, num); } 1202 void SetEntriesHere(int num) 1203 { m_EntriesHere = wx_truncate_cast(wxUint16, num); } 1204 void SetTotalEntries(int num) 1205 { m_TotalEntries = wx_truncate_cast(wxUint16, num); } 1206 void SetSize(wxFileOffset size) 1207 { m_Size = wx_truncate_cast(wxUint32, size); } 1208 void SetOffset(wxFileOffset offset) 1209 { m_Offset = wx_truncate_cast(wxUint32, offset); } 1210 void SetComment(const wxString& comment) 1211 { m_Comment = comment; } 1212 1213 bool Read(wxInputStream& stream, wxMBConv& conv); 1214 bool Write(wxOutputStream& stream, wxMBConv& conv) const; 1215 1216private: 1217 wxUint16 m_DiskNumber; 1218 wxUint16 m_StartDisk; 1219 wxUint16 m_EntriesHere; 1220 wxUint16 m_TotalEntries; 1221 wxUint32 m_Size; 1222 wxUint32 m_Offset; 1223 wxString m_Comment; 1224}; 1225 1226wxZipEndRec::wxZipEndRec() 1227 : m_DiskNumber(0), 1228 m_StartDisk(0), 1229 m_EntriesHere(0), 1230 m_TotalEntries(0), 1231 m_Size(0), 1232 m_Offset(0) 1233{ 1234} 1235 1236bool wxZipEndRec::Write(wxOutputStream& stream, wxMBConv& conv) const 1237{ 1238 const wxWX2MBbuf comment_buf = conv.cWX2MB(m_Comment); 1239 const char *comment = comment_buf; 1240 if (!comment) comment = ""; 1241 wxUint16 commentLen = (wxUint16)strlen(comment); 1242 1243 wxDataOutputStream ds(stream); 1244 1245 ds << END_MAGIC << m_DiskNumber << m_StartDisk << m_EntriesHere 1246 << m_TotalEntries << m_Size << m_Offset << commentLen; 1247 1248 stream.Write(comment, commentLen); 1249 1250 return stream.IsOk(); 1251} 1252 1253bool wxZipEndRec::Read(wxInputStream& stream, wxMBConv& conv) 1254{ 1255 wxZipHeader ds(stream, END_SIZE - 4); 1256 if (!ds) 1257 return false; 1258 1259 wxUint16 commentLen; 1260 1261 ds >> m_DiskNumber >> m_StartDisk >> m_EntriesHere 1262 >> m_TotalEntries >> m_Size >> m_Offset >> commentLen; 1263 1264 if (commentLen) { 1265 m_Comment = ReadString(stream, commentLen, conv); 1266 if (stream.LastRead() != commentLen + 0u) 1267 return false; 1268 } 1269 1270 if (m_DiskNumber != 0 || m_StartDisk != 0 || 1271 m_EntriesHere != m_TotalEntries) 1272 wxLogWarning(_("assuming this is a multi-part zip concatenated")); 1273 1274 return true; 1275} 1276 1277 1278///////////////////////////////////////////////////////////////////////////// 1279// A weak link from an input stream to an output stream 1280 1281class wxZipStreamLink 1282{ 1283public: 1284 wxZipStreamLink(wxZipOutputStream *stream) : m_ref(1), m_stream(stream) { } 1285 1286 wxZipStreamLink *AddRef() { m_ref++; return this; } 1287 wxZipOutputStream *GetOutputStream() const { return m_stream; } 1288 1289 void Release(class wxZipInputStream *WXUNUSED(s)) 1290 { if (--m_ref == 0) delete this; } 1291 void Release(class wxZipOutputStream *WXUNUSED(s)) 1292 { m_stream = NULL; if (--m_ref == 0) delete this; } 1293 1294private: 1295 ~wxZipStreamLink() { } 1296 1297 int m_ref; 1298 wxZipOutputStream *m_stream; 1299 1300 wxSUPPRESS_GCC_PRIVATE_DTOR_WARNING(wxZipStreamLink) 1301}; 1302 1303 1304///////////////////////////////////////////////////////////////////////////// 1305// Input stream 1306 1307// leave the default wxZipEntryPtr free for users 1308wxDECLARE_SCOPED_PTR(wxZipEntry, wxZipEntryPtr_) 1309wxDEFINE_SCOPED_PTR (wxZipEntry, wxZipEntryPtr_) 1310 1311// constructor 1312// 1313wxZipInputStream::wxZipInputStream(wxInputStream& stream, 1314 wxMBConv& conv /*=wxConvLocal*/) 1315 : wxArchiveInputStream(stream, conv) 1316{ 1317 Init(); 1318} 1319 1320wxZipInputStream::wxZipInputStream(wxInputStream *stream, 1321 wxMBConv& conv /*=wxConvLocal*/) 1322 : wxArchiveInputStream(stream, conv) 1323{ 1324 Init(); 1325} 1326 1327#if WXWIN_COMPATIBILITY_2_6 && wxUSE_FFILE 1328 1329// Part of the compatibility constructor, which has been made inline to 1330// avoid a problem with it not being exported by mingw 3.2.3 1331// 1332void wxZipInputStream::Init(const wxString& file) 1333{ 1334 // no error messages 1335 wxLogNull nolog; 1336 Init(); 1337 m_allowSeeking = true; 1338 wxFFileInputStream *ffile; 1339 ffile = wx_static_cast(wxFFileInputStream*, m_parent_i_stream); 1340 wxZipEntryPtr_ entry; 1341 1342 if (ffile->Ok()) { 1343 do { 1344 entry.reset(GetNextEntry()); 1345 } 1346 while (entry.get() != NULL && entry->GetInternalName() != file); 1347 } 1348 1349 if (entry.get() == NULL) 1350 m_lasterror = wxSTREAM_READ_ERROR; 1351} 1352 1353wxInputStream* wxZipInputStream::OpenFile(const wxString& archive) 1354{ 1355 wxLogNull nolog; 1356 return new wxFFileInputStream(archive); 1357} 1358 1359#endif // WXWIN_COMPATIBILITY_2_6 && wxUSE_FFILE 1360 1361void wxZipInputStream::Init() 1362{ 1363 m_store = new wxStoredInputStream(*m_parent_i_stream); 1364 m_inflate = NULL; 1365 m_rawin = NULL; 1366 m_raw = false; 1367 m_headerSize = 0; 1368 m_decomp = NULL; 1369 m_parentSeekable = false; 1370 m_weaklinks = new wxZipWeakLinks; 1371 m_streamlink = NULL; 1372 m_offsetAdjustment = 0; 1373 m_position = wxInvalidOffset; 1374 m_signature = 0; 1375 m_TotalEntries = 0; 1376 m_lasterror = m_parent_i_stream->GetLastError(); 1377#if WXWIN_COMPATIBILITY_2_6 1378 m_allowSeeking = false; 1379#endif 1380} 1381 1382wxZipInputStream::~wxZipInputStream() 1383{ 1384 CloseDecompressor(m_decomp); 1385 1386 delete m_store; 1387 delete m_inflate; 1388 delete m_rawin; 1389 1390 m_weaklinks->Release(this); 1391 1392 if (m_streamlink) 1393 m_streamlink->Release(this); 1394} 1395 1396wxString wxZipInputStream::GetComment() 1397{ 1398 if (m_position == wxInvalidOffset) 1399 if (!LoadEndRecord()) 1400 return wxEmptyString; 1401 1402 if (!m_parentSeekable && Eof() && m_signature) { 1403 m_lasterror = wxSTREAM_NO_ERROR; 1404 m_lasterror = ReadLocal(true); 1405 } 1406 1407 return m_Comment; 1408} 1409 1410int wxZipInputStream::GetTotalEntries() 1411{ 1412 if (m_position == wxInvalidOffset) 1413 LoadEndRecord(); 1414 return m_TotalEntries; 1415} 1416 1417wxZipStreamLink *wxZipInputStream::MakeLink(wxZipOutputStream *out) 1418{ 1419 wxZipStreamLink *link = NULL; 1420 1421 if (!m_parentSeekable && (IsOpened() || !Eof())) { 1422 link = new wxZipStreamLink(out); 1423 if (m_streamlink) 1424 m_streamlink->Release(this); 1425 m_streamlink = link->AddRef(); 1426 } 1427 1428 return link; 1429} 1430 1431bool wxZipInputStream::LoadEndRecord() 1432{ 1433 wxCHECK(m_position == wxInvalidOffset, false); 1434 if (!IsOk()) 1435 return false; 1436 1437 m_position = 0; 1438 1439 // First find the end-of-central-directory record. 1440 if (!FindEndRecord()) { 1441 // failed, so either this is a non-seekable stream (ok), or not a zip 1442 if (m_parentSeekable) { 1443 m_lasterror = wxSTREAM_READ_ERROR; 1444 wxLogError(_("invalid zip file")); 1445 return false; 1446 } 1447 else { 1448 wxLogNull nolog; 1449 wxFileOffset pos = m_parent_i_stream->TellI(); 1450 if (pos != wxInvalidOffset) 1451 m_offsetAdjustment = m_position = pos; 1452 return true; 1453 } 1454 } 1455 1456 wxZipEndRec endrec; 1457 1458 // Read in the end record 1459 wxFileOffset endPos = m_parent_i_stream->TellI() - 4; 1460 if (!endrec.Read(*m_parent_i_stream, GetConv())) 1461 return false; 1462 1463 m_TotalEntries = endrec.GetTotalEntries(); 1464 m_Comment = endrec.GetComment(); 1465 1466 // Now find the central-directory. we have the file offset of 1467 // the CD, so look there first. 1468 if (m_parent_i_stream->SeekI(endrec.GetOffset()) != wxInvalidOffset && 1469 ReadSignature() == CENTRAL_MAGIC) { 1470 m_signature = CENTRAL_MAGIC; 1471 m_position = endrec.GetOffset(); 1472 m_offsetAdjustment = 0; 1473 return true; 1474 } 1475 1476 // If it's not there, then it could be that the zip has been appended 1477 // to a self extractor, so take the CD size (also in endrec), subtract 1478 // it from the file offset of the end-central-directory and look there. 1479 if (m_parent_i_stream->SeekI(endPos - endrec.GetSize()) 1480 != wxInvalidOffset && ReadSignature() == CENTRAL_MAGIC) { 1481 m_signature = CENTRAL_MAGIC; 1482 m_position = endPos - endrec.GetSize(); 1483 m_offsetAdjustment = m_position - endrec.GetOffset(); 1484 return true; 1485 } 1486 1487 wxLogError(_("can't find central directory in zip")); 1488 m_lasterror = wxSTREAM_READ_ERROR; 1489 return false; 1490} 1491 1492// Find the end-of-central-directory record. 1493// If found the stream will be positioned just past the 4 signature bytes. 1494// 1495bool wxZipInputStream::FindEndRecord() 1496{ 1497 if (!m_parent_i_stream->IsSeekable()) 1498 return false; 1499 1500 // usually it's 22 bytes in size and the last thing in the file 1501 { 1502 wxLogNull nolog; 1503 if (m_parent_i_stream->SeekI(-END_SIZE, wxFromEnd) == wxInvalidOffset) 1504 return false; 1505 } 1506 1507 m_parentSeekable = true; 1508 m_signature = 0; 1509 char magic[4]; 1510 if (m_parent_i_stream->Read(magic, 4).LastRead() != 4) 1511 return false; 1512 if ((m_signature = CrackUint32(magic)) == END_MAGIC) 1513 return true; 1514 1515 // unfortunately, the record has a comment field that can be up to 65535 1516 // bytes in length, so if the signature not found then search backwards. 1517 wxFileOffset pos = m_parent_i_stream->TellI(); 1518 const int BUFSIZE = 1024; 1519 wxCharBuffer buf(BUFSIZE); 1520 1521 memcpy(buf.data(), magic, 3); 1522 wxFileOffset minpos = wxMax(pos - 65535L, 0); 1523 1524 while (pos > minpos) { 1525 size_t len = wx_truncate_cast(size_t, 1526 pos - wxMax(pos - (BUFSIZE - 3), minpos)); 1527 memcpy(buf.data() + len, buf, 3); 1528 pos -= len; 1529 1530 if (m_parent_i_stream->SeekI(pos, wxFromStart) == wxInvalidOffset || 1531 m_parent_i_stream->Read(buf.data(), len).LastRead() != len) 1532 return false; 1533 1534 char *p = buf.data() + len; 1535 1536 while (p-- > buf.data()) { 1537 if ((m_signature = CrackUint32(p)) == END_MAGIC) { 1538 size_t remainder = buf.data() + len - p; 1539 if (remainder > 4) 1540 m_parent_i_stream->Ungetch(p + 4, remainder - 4); 1541 return true; 1542 } 1543 } 1544 } 1545 1546 return false; 1547} 1548 1549wxZipEntry *wxZipInputStream::GetNextEntry() 1550{ 1551 if (m_position == wxInvalidOffset) 1552 if (!LoadEndRecord()) 1553 return NULL; 1554 1555 m_lasterror = m_parentSeekable ? ReadCentral() : ReadLocal(); 1556 if (!IsOk()) 1557 return NULL; 1558 1559 wxZipEntryPtr_ entry(new wxZipEntry(m_entry)); 1560 entry->m_backlink = m_weaklinks->AddEntry(entry.get(), entry->GetKey()); 1561 return entry.release(); 1562} 1563 1564wxStreamError wxZipInputStream::ReadCentral() 1565{ 1566 if (!AtHeader()) 1567 CloseEntry(); 1568 1569 if (m_signature == END_MAGIC) 1570 return wxSTREAM_EOF; 1571 1572 if (m_signature != CENTRAL_MAGIC) { 1573 wxLogError(_("error reading zip central directory")); 1574 return wxSTREAM_READ_ERROR; 1575 } 1576 1577 if (QuietSeek(*m_parent_i_stream, m_position + 4) == wxInvalidOffset) 1578 return wxSTREAM_READ_ERROR; 1579 1580 size_t size = m_entry.ReadCentral(*m_parent_i_stream, GetConv()); 1581 if (!size) { 1582 m_signature = 0; 1583 return wxSTREAM_READ_ERROR; 1584 } 1585 1586 m_position += size; 1587 m_signature = ReadSignature(); 1588 1589 if (m_offsetAdjustment) 1590 m_entry.SetOffset(m_entry.GetOffset() + m_offsetAdjustment); 1591 m_entry.SetKey(m_entry.GetOffset()); 1592 1593 return wxSTREAM_NO_ERROR; 1594} 1595 1596wxStreamError wxZipInputStream::ReadLocal(bool readEndRec /*=false*/) 1597{ 1598 if (!AtHeader()) 1599 CloseEntry(); 1600 1601 if (!m_signature) 1602 m_signature = ReadSignature(); 1603 1604 if (m_signature == CENTRAL_MAGIC || m_signature == END_MAGIC) { 1605 if (m_streamlink && !m_streamlink->GetOutputStream()) { 1606 m_streamlink->Release(this); 1607 m_streamlink = NULL; 1608 } 1609 } 1610 1611 while (m_signature == CENTRAL_MAGIC) { 1612 if (m_weaklinks->IsEmpty() && m_streamlink == NULL) 1613 return wxSTREAM_EOF; 1614 1615 size_t size = m_entry.ReadCentral(*m_parent_i_stream, GetConv()); 1616 m_position += size; 1617 m_signature = 0; 1618 if (!size) 1619 return wxSTREAM_READ_ERROR; 1620 1621 wxZipEntry *entry = m_weaklinks->GetEntry(m_entry.GetOffset()); 1622 if (entry) { 1623 entry->SetSystemMadeBy(m_entry.GetSystemMadeBy()); 1624 entry->SetVersionMadeBy(m_entry.GetVersionMadeBy()); 1625 entry->SetComment(m_entry.GetComment()); 1626 entry->SetDiskStart(m_entry.GetDiskStart()); 1627 entry->SetInternalAttributes(m_entry.GetInternalAttributes()); 1628 entry->SetExternalAttributes(m_entry.GetExternalAttributes()); 1629 Copy(entry->m_Extra, m_entry.m_Extra); 1630 entry->Notify(); 1631 m_weaklinks->RemoveEntry(entry->GetOffset()); 1632 } 1633 1634 m_signature = ReadSignature(); 1635 } 1636 1637 if (m_signature == END_MAGIC) { 1638 if (readEndRec || m_streamlink) { 1639 wxZipEndRec endrec; 1640 endrec.Read(*m_parent_i_stream, GetConv()); 1641 m_Comment = endrec.GetComment(); 1642 m_signature = 0; 1643 if (m_streamlink) { 1644 m_streamlink->GetOutputStream()->SetComment(endrec.GetComment()); 1645 m_streamlink->Release(this); 1646 m_streamlink = NULL; 1647 } 1648 } 1649 return wxSTREAM_EOF; 1650 } 1651 1652 if (m_signature == LOCAL_MAGIC) { 1653 m_headerSize = m_entry.ReadLocal(*m_parent_i_stream, GetConv()); 1654 m_signature = 0; 1655 m_entry.SetOffset(m_position); 1656 m_entry.SetKey(m_position); 1657 1658 if (m_headerSize) { 1659 m_TotalEntries++; 1660 return wxSTREAM_NO_ERROR; 1661 } 1662 } 1663 1664 wxLogError(_("error reading zip local header")); 1665 return wxSTREAM_READ_ERROR; 1666} 1667 1668wxUint32 wxZipInputStream::ReadSignature() 1669{ 1670 char magic[4]; 1671 m_parent_i_stream->Read(magic, 4); 1672 return m_parent_i_stream->LastRead() == 4 ? CrackUint32(magic) : 0; 1673} 1674 1675bool wxZipInputStream::OpenEntry(wxArchiveEntry& entry) 1676{ 1677 wxZipEntry *zipEntry = wxStaticCast(&entry, wxZipEntry); 1678 return zipEntry ? OpenEntry(*zipEntry) : false; 1679} 1680 1681// Open an entry 1682// 1683bool wxZipInputStream::DoOpen(wxZipEntry *entry, bool raw) 1684{ 1685 if (m_position == wxInvalidOffset) 1686 if (!LoadEndRecord()) 1687 return false; 1688 if (m_lasterror == wxSTREAM_READ_ERROR) 1689 return false; 1690 if (IsOpened()) 1691 CloseEntry(); 1692 1693 m_raw = raw; 1694 1695 if (entry) { 1696 if (AfterHeader() && entry->GetKey() == m_entry.GetOffset()) 1697 return true; 1698 // can only open the current entry on a non-seekable stream 1699 wxCHECK(m_parentSeekable, false); 1700 } 1701 1702 m_lasterror = wxSTREAM_READ_ERROR; 1703 1704 if (entry) 1705 m_entry = *entry; 1706 1707 if (m_parentSeekable) { 1708 if (QuietSeek(*m_parent_i_stream, m_entry.GetOffset()) 1709 == wxInvalidOffset) 1710 return false; 1711 if (ReadSignature() != LOCAL_MAGIC) { 1712 wxLogError(_("bad zipfile offset to entry")); 1713 return false; 1714 } 1715 } 1716 1717 if (m_parentSeekable || AtHeader()) { 1718 m_headerSize = m_entry.ReadLocal(*m_parent_i_stream, GetConv()); 1719 if (m_headerSize && m_parentSeekable) { 1720 wxZipEntry *ref = m_weaklinks->GetEntry(m_entry.GetKey()); 1721 if (ref) { 1722 Copy(ref->m_LocalExtra, m_entry.m_LocalExtra); 1723 ref->Notify(); 1724 m_weaklinks->RemoveEntry(ref->GetKey()); 1725 } 1726 if (entry && entry != ref) { 1727 Copy(entry->m_LocalExtra, m_entry.m_LocalExtra); 1728 entry->Notify(); 1729 } 1730 } 1731 } 1732 1733 if (m_headerSize) 1734 m_lasterror = wxSTREAM_NO_ERROR; 1735 return IsOk(); 1736} 1737 1738bool wxZipInputStream::OpenDecompressor(bool raw /*=false*/) 1739{ 1740 wxASSERT(AfterHeader()); 1741 1742 wxFileOffset compressedSize = m_entry.GetCompressedSize(); 1743 1744 if (raw) 1745 m_raw = true; 1746 1747 if (m_raw) { 1748 if (compressedSize != wxInvalidOffset) { 1749 m_store->Open(compressedSize); 1750 m_decomp = m_store; 1751 } else { 1752 if (!m_rawin) 1753 m_rawin = new wxRawInputStream(*m_parent_i_stream); 1754 m_decomp = m_rawin->Open(OpenDecompressor(m_rawin->GetTee())); 1755 } 1756 } else { 1757 if (compressedSize != wxInvalidOffset && 1758 (m_entry.GetMethod() != wxZIP_METHOD_DEFLATE || 1759 wxZlibInputStream::CanHandleGZip())) { 1760 m_store->Open(compressedSize); 1761 m_decomp = OpenDecompressor(*m_store); 1762 } else { 1763 m_decomp = OpenDecompressor(*m_parent_i_stream); 1764 } 1765 } 1766 1767 m_crcAccumulator = crc32(0, Z_NULL, 0); 1768 m_lasterror = m_decomp ? m_decomp->GetLastError() : wxSTREAM_READ_ERROR; 1769 return IsOk(); 1770} 1771 1772// Can be overriden to add support for additional decompression methods 1773// 1774wxInputStream *wxZipInputStream::OpenDecompressor(wxInputStream& stream) 1775{ 1776 switch (m_entry.GetMethod()) { 1777 case wxZIP_METHOD_STORE: 1778 if (m_entry.GetSize() == wxInvalidOffset) { 1779 wxLogError(_("stored file length not in Zip header")); 1780 break; 1781 } 1782 m_store->Open(m_entry.GetSize()); 1783 return m_store; 1784 1785 case wxZIP_METHOD_DEFLATE: 1786 if (!m_inflate) 1787 m_inflate = new wxZlibInputStream2(stream); 1788 else 1789 m_inflate->Open(stream); 1790 return m_inflate; 1791 1792 default: 1793 wxLogError(_("unsupported Zip compression method")); 1794 } 1795 1796 return NULL; 1797} 1798 1799bool wxZipInputStream::CloseDecompressor(wxInputStream *decomp) 1800{ 1801 if (decomp && decomp == m_rawin) 1802 return CloseDecompressor(m_rawin->GetFilterInputStream()); 1803 if (decomp != m_store && decomp != m_inflate) 1804 delete decomp; 1805 return true; 1806} 1807 1808// Closes the current entry and positions the underlying stream at the start 1809// of the next entry 1810// 1811bool wxZipInputStream::CloseEntry() 1812{ 1813 if (AtHeader()) 1814 return true; 1815 if (m_lasterror == wxSTREAM_READ_ERROR) 1816 return false; 1817 1818 if (!m_parentSeekable) { 1819 if (!IsOpened() && !OpenDecompressor(true)) 1820 return false; 1821 1822 const int BUFSIZE = 8192; 1823 wxCharBuffer buf(BUFSIZE); 1824 while (IsOk()) 1825 Read(buf.data(), BUFSIZE); 1826 1827 m_position += m_headerSize + m_entry.GetCompressedSize(); 1828 } 1829 1830 if (m_lasterror == wxSTREAM_EOF) 1831 m_lasterror = wxSTREAM_NO_ERROR; 1832 1833 CloseDecompressor(m_decomp); 1834 m_decomp = NULL; 1835 m_entry = wxZipEntry(); 1836 m_headerSize = 0; 1837 m_raw = false; 1838 1839 return IsOk(); 1840} 1841 1842size_t wxZipInputStream::OnSysRead(void *buffer, size_t size) 1843{ 1844 if (!IsOpened()) 1845 if ((AtHeader() && !DoOpen()) || !OpenDecompressor()) 1846 m_lasterror = wxSTREAM_READ_ERROR; 1847 if (!IsOk() || !size) 1848 return 0; 1849 1850 size_t count = m_decomp->Read(buffer, size).LastRead(); 1851 if (!m_raw) 1852 m_crcAccumulator = crc32(m_crcAccumulator, (Byte*)buffer, count); 1853 if (count < size) 1854 m_lasterror = m_decomp->GetLastError(); 1855 1856 if (Eof()) { 1857 if ((m_entry.GetFlags() & wxZIP_SUMS_FOLLOW) != 0) { 1858 m_headerSize += m_entry.ReadDescriptor(*m_parent_i_stream); 1859 wxZipEntry *entry = m_weaklinks->GetEntry(m_entry.GetKey()); 1860 1861 if (entry) { 1862 entry->SetCrc(m_entry.GetCrc()); 1863 entry->SetCompressedSize(m_entry.GetCompressedSize()); 1864 entry->SetSize(m_entry.GetSize()); 1865 entry->Notify(); 1866 } 1867 } 1868 1869 if (!m_raw) { 1870 m_lasterror = wxSTREAM_READ_ERROR; 1871 1872 if (m_entry.GetSize() != TellI()) 1873 wxLogError(_("reading zip stream (entry %s): bad length"), 1874 m_entry.GetName().c_str()); 1875 else if (m_crcAccumulator != m_entry.GetCrc()) 1876 wxLogError(_("reading zip stream (entry %s): bad crc"), 1877 m_entry.GetName().c_str()); 1878 else 1879 m_lasterror = wxSTREAM_EOF; 1880 } 1881 } 1882 1883 return count; 1884} 1885 1886#if WXWIN_COMPATIBILITY_2_6 1887 1888// Borrowed from VS's zip stream (c) 1999 Vaclav Slavik 1889// 1890wxFileOffset wxZipInputStream::OnSysSeek(wxFileOffset seek, wxSeekMode mode) 1891{ 1892 // seeking works when the stream is created with the compatibility 1893 // constructor 1894 if (!m_allowSeeking) 1895 return wxInvalidOffset; 1896 if (!IsOpened()) 1897 if ((AtHeader() && !DoOpen()) || !OpenDecompressor()) 1898 m_lasterror = wxSTREAM_READ_ERROR; 1899 if (!IsOk()) 1900 return wxInvalidOffset; 1901 1902 // NB: since ZIP files don't natively support seeking, we have to 1903 // implement a brute force workaround -- reading all the data 1904 // between current and the new position (or between beginning of 1905 // the file and new position...) 1906 1907 wxFileOffset nextpos; 1908 wxFileOffset pos = TellI(); 1909 1910 switch ( mode ) 1911 { 1912 case wxFromCurrent : nextpos = seek + pos; break; 1913 case wxFromStart : nextpos = seek; break; 1914 case wxFromEnd : nextpos = GetLength() + seek; break; 1915 default : nextpos = pos; break; /* just to fool compiler, never happens */ 1916 } 1917 1918 wxFileOffset toskip wxDUMMY_INITIALIZE(0); 1919 if ( nextpos >= pos ) 1920 { 1921 toskip = nextpos - pos; 1922 } 1923 else 1924 { 1925 wxZipEntry current(m_entry); 1926 if (!OpenEntry(current)) 1927 { 1928 m_lasterror = wxSTREAM_READ_ERROR; 1929 return pos; 1930 } 1931 toskip = nextpos; 1932 } 1933 1934 if ( toskip > 0 ) 1935 { 1936 const int BUFSIZE = 4096; 1937 size_t sz; 1938 char buffer[BUFSIZE]; 1939 while ( toskip > 0 ) 1940 { 1941 sz = wx_truncate_cast(size_t, wxMin(toskip, BUFSIZE)); 1942 Read(buffer, sz); 1943 toskip -= sz; 1944 } 1945 } 1946 1947 pos = nextpos; 1948 return pos; 1949} 1950 1951#endif // WXWIN_COMPATIBILITY_2_6 1952 1953 1954///////////////////////////////////////////////////////////////////////////// 1955// Output stream 1956 1957#include "wx/listimpl.cpp" 1958WX_DEFINE_LIST(wxZipEntryList_) 1959 1960wxZipOutputStream::wxZipOutputStream(wxOutputStream& stream, 1961 int level /*=-1*/, 1962 wxMBConv& conv /*=wxConvLocal*/) 1963 : wxArchiveOutputStream(stream, conv) 1964{ 1965 Init(level); 1966} 1967 1968wxZipOutputStream::wxZipOutputStream(wxOutputStream *stream, 1969 int level /*=-1*/, 1970 wxMBConv& conv /*=wxConvLocal*/) 1971 : wxArchiveOutputStream(stream, conv) 1972{ 1973 Init(level); 1974} 1975 1976void wxZipOutputStream::Init(int level) 1977{ 1978 m_store = new wxStoredOutputStream(*m_parent_o_stream); 1979 m_deflate = NULL; 1980 m_backlink = NULL; 1981 m_initialData = new char[OUTPUT_LATENCY]; 1982 m_initialSize = 0; 1983 m_pending = NULL; 1984 m_raw = false; 1985 m_headerOffset = 0; 1986 m_headerSize = 0; 1987 m_entrySize = 0; 1988 m_comp = NULL; 1989 m_level = level; 1990 m_offsetAdjustment = wxInvalidOffset; 1991} 1992 1993wxZipOutputStream::~wxZipOutputStream() 1994{ 1995 Close(); 1996 WX_CLEAR_LIST(wxZipEntryList_, m_entries); 1997 delete m_store; 1998 delete m_deflate; 1999 delete m_pending; 2000 delete [] m_initialData; 2001 if (m_backlink) 2002 m_backlink->Release(this); 2003} 2004 2005bool wxZipOutputStream::PutNextEntry( 2006 const wxString& name, 2007 const wxDateTime& dt /*=wxDateTime::Now()*/, 2008 wxFileOffset size /*=wxInvalidOffset*/) 2009{ 2010 return PutNextEntry(new wxZipEntry(name, dt, size)); 2011} 2012 2013bool wxZipOutputStream::PutNextDirEntry( 2014 const wxString& name, 2015 const wxDateTime& dt /*=wxDateTime::Now()*/) 2016{ 2017 wxZipEntry *entry = new wxZipEntry(name, dt); 2018 entry->SetIsDir(); 2019 return PutNextEntry(entry); 2020} 2021 2022bool wxZipOutputStream::CopyEntry(wxZipEntry *entry, 2023 wxZipInputStream& inputStream) 2024{ 2025 wxZipEntryPtr_ e(entry); 2026 2027 return 2028 inputStream.DoOpen(e.get(), true) && 2029 DoCreate(e.release(), true) && 2030 Write(inputStream).IsOk() && inputStream.Eof(); 2031} 2032 2033bool wxZipOutputStream::PutNextEntry(wxArchiveEntry *entry) 2034{ 2035 wxZipEntry *zipEntry = wxStaticCast(entry, wxZipEntry); 2036 if (!zipEntry) 2037 delete entry; 2038 return PutNextEntry(zipEntry); 2039} 2040 2041bool wxZipOutputStream::CopyEntry(wxArchiveEntry *entry, 2042 wxArchiveInputStream& stream) 2043{ 2044 wxZipEntry *zipEntry = wxStaticCast(entry, wxZipEntry); 2045 2046 if (!zipEntry || !stream.OpenEntry(*zipEntry)) { 2047 delete entry; 2048 return false; 2049 } 2050 2051 return CopyEntry(zipEntry, wx_static_cast(wxZipInputStream&, stream)); 2052} 2053 2054bool wxZipOutputStream::CopyArchiveMetaData(wxZipInputStream& inputStream) 2055{ 2056 m_Comment = inputStream.GetComment(); 2057 if (m_backlink) 2058 m_backlink->Release(this); 2059 m_backlink = inputStream.MakeLink(this); 2060 return true; 2061} 2062 2063bool wxZipOutputStream::CopyArchiveMetaData(wxArchiveInputStream& stream) 2064{ 2065 return CopyArchiveMetaData(wx_static_cast(wxZipInputStream&, stream)); 2066} 2067 2068void wxZipOutputStream::SetLevel(int level) 2069{ 2070 if (level != m_level) { 2071 if (m_comp != m_deflate) 2072 delete m_deflate; 2073 m_deflate = NULL; 2074 m_level = level; 2075 } 2076} 2077 2078bool wxZipOutputStream::DoCreate(wxZipEntry *entry, bool raw /*=false*/) 2079{ 2080 CloseEntry(); 2081 2082 m_pending = entry; 2083 if (!m_pending) 2084 return false; 2085 2086 // write the signature bytes right away 2087 wxDataOutputStream ds(*m_parent_o_stream); 2088 ds << LOCAL_MAGIC; 2089 2090 // and if this is the first entry test for seekability 2091 if (m_headerOffset == 0 && m_parent_o_stream->IsSeekable()) { 2092#if wxUSE_LOG 2093 bool logging = wxLog::IsEnabled(); 2094 wxLogNull nolog; 2095#endif // wxUSE_LOG 2096 wxFileOffset here = m_parent_o_stream->TellO(); 2097 2098 if (here != wxInvalidOffset && here >= 4) { 2099 if (m_parent_o_stream->SeekO(here - 4) == here - 4) { 2100 m_offsetAdjustment = here - 4; 2101#if wxUSE_LOG 2102 wxLog::EnableLogging(logging); 2103#endif // wxUSE_LOG 2104 m_parent_o_stream->SeekO(here); 2105 } 2106 } 2107 } 2108 2109 m_pending->SetOffset(m_headerOffset); 2110 2111 m_crcAccumulator = crc32(0, Z_NULL, 0); 2112 2113 if (raw) 2114 m_raw = true; 2115 2116 m_lasterror = wxSTREAM_NO_ERROR; 2117 return true; 2118} 2119 2120// Can be overriden to add support for additional compression methods 2121// 2122wxOutputStream *wxZipOutputStream::OpenCompressor( 2123 wxOutputStream& stream, 2124 wxZipEntry& entry, 2125 const Buffer bufs[]) 2126{ 2127 if (entry.GetMethod() == wxZIP_METHOD_DEFAULT) { 2128 if (GetLevel() == 0 2129 && (IsParentSeekable() 2130 || entry.GetCompressedSize() != wxInvalidOffset 2131 || entry.GetSize() != wxInvalidOffset)) { 2132 entry.SetMethod(wxZIP_METHOD_STORE); 2133 } else { 2134 int size = 0; 2135 for (int i = 0; bufs[i].m_data; ++i) 2136 size += bufs[i].m_size; 2137 entry.SetMethod(size <= 6 ? 2138 wxZIP_METHOD_STORE : wxZIP_METHOD_DEFLATE); 2139 } 2140 } 2141 2142 switch (entry.GetMethod()) { 2143 case wxZIP_METHOD_STORE: 2144 if (entry.GetCompressedSize() == wxInvalidOffset) 2145 entry.SetCompressedSize(entry.GetSize()); 2146 return m_store; 2147 2148 case wxZIP_METHOD_DEFLATE: 2149 { 2150 int defbits = wxZIP_DEFLATE_NORMAL; 2151 switch (GetLevel()) { 2152 case 0: case 1: 2153 defbits = wxZIP_DEFLATE_SUPERFAST; 2154 break; 2155 case 2: case 3: case 4: 2156 defbits = wxZIP_DEFLATE_FAST; 2157 break; 2158 case 8: case 9: 2159 defbits = wxZIP_DEFLATE_EXTRA; 2160 break; 2161 } 2162 entry.SetFlags((entry.GetFlags() & ~wxZIP_DEFLATE_MASK) | 2163 defbits | wxZIP_SUMS_FOLLOW); 2164 2165 if (!m_deflate) 2166 m_deflate = new wxZlibOutputStream2(stream, GetLevel()); 2167 else 2168 m_deflate->Open(stream); 2169 2170 return m_deflate; 2171 } 2172 2173 default: 2174 wxLogError(_("unsupported Zip compression method")); 2175 } 2176 2177 return NULL; 2178} 2179 2180bool wxZipOutputStream::CloseCompressor(wxOutputStream *comp) 2181{ 2182 if (comp == m_deflate) 2183 m_deflate->Close(); 2184 else if (comp != m_store) 2185 delete comp; 2186 return true; 2187} 2188 2189// This is called when OUPUT_LATENCY bytes has been written to the 2190// wxZipOutputStream to actually create the zip entry. 2191// 2192void wxZipOutputStream::CreatePendingEntry(const void *buffer, size_t size) 2193{ 2194 wxASSERT(IsOk() && m_pending && !m_comp); 2195 wxZipEntryPtr_ spPending(m_pending); 2196 m_pending = NULL; 2197 2198 Buffer bufs[] = { 2199 { m_initialData, m_initialSize }, 2200 { (const char*)buffer, size }, 2201 { NULL, 0 } 2202 }; 2203 2204 if (m_raw) 2205 m_comp = m_store; 2206 else 2207 m_comp = OpenCompressor(*m_store, *spPending, 2208 m_initialSize ? bufs : bufs + 1); 2209 2210 if (IsParentSeekable() 2211 || (spPending->m_Crc 2212 && spPending->m_CompressedSize != wxInvalidOffset 2213 && spPending->m_Size != wxInvalidOffset)) 2214 spPending->m_Flags &= ~wxZIP_SUMS_FOLLOW; 2215 else 2216 if (spPending->m_CompressedSize != wxInvalidOffset) 2217 spPending->m_Flags |= wxZIP_SUMS_FOLLOW; 2218 2219 m_headerSize = spPending->WriteLocal(*m_parent_o_stream, GetConv()); 2220 m_lasterror = m_parent_o_stream->GetLastError(); 2221 2222 if (IsOk()) { 2223 m_entries.push_back(spPending.release()); 2224 OnSysWrite(m_initialData, m_initialSize); 2225 } 2226 2227 m_initialSize = 0; 2228} 2229 2230// This is called to write out the zip entry when Close has been called 2231// before OUTPUT_LATENCY bytes has been written to the wxZipOutputStream. 2232// 2233void wxZipOutputStream::CreatePendingEntry() 2234{ 2235 wxASSERT(IsOk() && m_pending && !m_comp); 2236 wxZipEntryPtr_ spPending(m_pending); 2237 m_pending = NULL; 2238 m_lasterror = wxSTREAM_WRITE_ERROR; 2239 2240 if (!m_raw) { 2241 // Initially compresses the data to memory, then fall back to 'store' 2242 // if the compressor makes the data larger rather than smaller. 2243 wxMemoryOutputStream mem; 2244 Buffer bufs[] = { { m_initialData, m_initialSize }, { NULL, 0 } }; 2245 wxOutputStream *comp = OpenCompressor(mem, *spPending, bufs); 2246 2247 if (!comp) 2248 return; 2249 if (comp != m_store) { 2250 bool ok = comp->Write(m_initialData, m_initialSize).IsOk(); 2251 CloseCompressor(comp); 2252 if (!ok) 2253 return; 2254 } 2255 2256 m_entrySize = m_initialSize; 2257 m_crcAccumulator = crc32(0, (Byte*)m_initialData, m_initialSize); 2258 2259 if (mem.GetSize() > 0 && mem.GetSize() < m_initialSize) { 2260 m_initialSize = mem.GetSize(); 2261 mem.CopyTo(m_initialData, m_initialSize); 2262 } else { 2263 spPending->SetMethod(wxZIP_METHOD_STORE); 2264 } 2265 2266 spPending->SetSize(m_entrySize); 2267 spPending->SetCrc(m_crcAccumulator); 2268 spPending->SetCompressedSize(m_initialSize); 2269 } 2270 2271 spPending->m_Flags &= ~wxZIP_SUMS_FOLLOW; 2272 m_headerSize = spPending->WriteLocal(*m_parent_o_stream, GetConv()); 2273 2274 if (m_parent_o_stream->IsOk()) { 2275 m_entries.push_back(spPending.release()); 2276 m_comp = m_store; 2277 m_store->Write(m_initialData, m_initialSize); 2278 } 2279 2280 m_initialSize = 0; 2281 m_lasterror = m_parent_o_stream->GetLastError(); 2282} 2283 2284// Write the 'central directory' and the 'end-central-directory' records. 2285// 2286bool wxZipOutputStream::Close() 2287{ 2288 CloseEntry(); 2289 2290 if (m_lasterror == wxSTREAM_WRITE_ERROR || m_entries.size() == 0) { 2291 wxFilterOutputStream::Close(); 2292 return false; 2293 } 2294 2295 wxZipEndRec endrec; 2296 2297 endrec.SetEntriesHere(m_entries.size()); 2298 endrec.SetTotalEntries(m_entries.size()); 2299 endrec.SetOffset(m_headerOffset); 2300 endrec.SetComment(m_Comment); 2301 2302 wxZipEntryList_::iterator it; 2303 wxFileOffset size = 0; 2304 2305 for (it = m_entries.begin(); it != m_entries.end(); ++it) { 2306 size += (*it)->WriteCentral(*m_parent_o_stream, GetConv()); 2307 delete *it; 2308 } 2309 m_entries.clear(); 2310 2311 endrec.SetSize(size); 2312 endrec.Write(*m_parent_o_stream, GetConv()); 2313 2314 m_lasterror = m_parent_o_stream->GetLastError(); 2315 2316 if (!wxFilterOutputStream::Close() || !IsOk()) 2317 return false; 2318 m_lasterror = wxSTREAM_EOF; 2319 return true; 2320} 2321 2322// Finish writing the current entry 2323// 2324bool wxZipOutputStream::CloseEntry() 2325{ 2326 if (IsOk() && m_pending) 2327 CreatePendingEntry(); 2328 if (!IsOk()) 2329 return false; 2330 if (!m_comp) 2331 return true; 2332 2333 CloseCompressor(m_comp); 2334 m_comp = NULL; 2335 2336 wxFileOffset compressedSize = m_store->TellO(); 2337 2338 wxZipEntry& entry = *m_entries.back(); 2339 2340 // When writing raw the crc and size can't be checked 2341 if (m_raw) { 2342 m_crcAccumulator = entry.GetCrc(); 2343 m_entrySize = entry.GetSize(); 2344 } 2345 2346 // Write the sums in the trailing 'data descriptor' if necessary 2347 if (entry.m_Flags & wxZIP_SUMS_FOLLOW) { 2348 wxASSERT(!IsParentSeekable()); 2349 m_headerOffset += 2350 entry.WriteDescriptor(*m_parent_o_stream, m_crcAccumulator, 2351 compressedSize, m_entrySize); 2352 m_lasterror = m_parent_o_stream->GetLastError(); 2353 } 2354 2355 // If the local header didn't have the correct crc and size written to 2356 // it then seek back and fix it 2357 else if (m_crcAccumulator != entry.GetCrc() 2358 || m_entrySize != entry.GetSize() 2359 || compressedSize != entry.GetCompressedSize()) 2360 { 2361 if (IsParentSeekable()) { 2362 wxFileOffset here = m_parent_o_stream->TellO(); 2363 wxFileOffset headerOffset = m_headerOffset + m_offsetAdjustment; 2364 m_parent_o_stream->SeekO(headerOffset + SUMS_OFFSET); 2365 entry.WriteDescriptor(*m_parent_o_stream, m_crcAccumulator, 2366 compressedSize, m_entrySize); 2367 m_parent_o_stream->SeekO(here); 2368 m_lasterror = m_parent_o_stream->GetLastError(); 2369 } else { 2370 m_lasterror = wxSTREAM_WRITE_ERROR; 2371 } 2372 } 2373 2374 m_headerOffset += m_headerSize + compressedSize; 2375 m_headerSize = 0; 2376 m_entrySize = 0; 2377 m_store->Close(); 2378 m_raw = false; 2379 2380 if (IsOk()) 2381 m_lasterror = m_parent_o_stream->GetLastError(); 2382 else 2383 wxLogError(_("error writing zip entry '%s': bad crc or length"), 2384 entry.GetName().c_str()); 2385 return IsOk(); 2386} 2387 2388void wxZipOutputStream::Sync() 2389{ 2390 if (IsOk() && m_pending) 2391 CreatePendingEntry(NULL, 0); 2392 if (!m_comp) 2393 m_lasterror = wxSTREAM_WRITE_ERROR; 2394 if (IsOk()) { 2395 m_comp->Sync(); 2396 m_lasterror = m_comp->GetLastError(); 2397 } 2398} 2399 2400size_t wxZipOutputStream::OnSysWrite(const void *buffer, size_t size) 2401{ 2402 if (IsOk() && m_pending) { 2403 if (m_initialSize + size < OUTPUT_LATENCY) { 2404 memcpy(m_initialData + m_initialSize, buffer, size); 2405 m_initialSize += size; 2406 return size; 2407 } else { 2408 CreatePendingEntry(buffer, size); 2409 } 2410 } 2411 2412 if (!m_comp) 2413 m_lasterror = wxSTREAM_WRITE_ERROR; 2414 if (!IsOk() || !size) 2415 return 0; 2416 2417 if (m_comp->Write(buffer, size).LastWrite() != size) 2418 m_lasterror = wxSTREAM_WRITE_ERROR; 2419 m_crcAccumulator = crc32(m_crcAccumulator, (Byte*)buffer, size); 2420 m_entrySize += m_comp->LastWrite(); 2421 2422 return m_comp->LastWrite(); 2423} 2424 2425#endif // wxUSE_ZIPSTREAM 2426