Logo Search packages:      
Sourcecode: harbour version File versions  Download package

ziparchive.cpp

///////////////////////////////////////////////////////////////////////////////
// $Workfile: ZipArchive.cpp $
// $Archive: /ZipArchive/ZipArchive.cpp $
// $Date: 2004/04/03 04:29:41 $ $Author: lculik $
////////////////////////////////////////////////////////////////////////////////
// This source file is part of the ZipArchive library source distribution and
// is Copyright 2000-2003 by Tadeusz Dracz (http://www.artpol-software.com/)
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// For the licensing details see the file License.txt
////////////////////////////////////////////////////////////////////////////////


#include "hbcomprs.h"
#include "stdafx.h"
#include "ziparchive.h"
// #include "ZipPathComponent.h"
#include "zipplatform.h"
#include "zipcompatibility.h"
#include <time.h>

#ifndef DEF_MEM_LEVEL
#if MAX_MEM_LEVEL >= 8
#  define DEF_MEM_LEVEL 8
#else
#  define DEF_MEM_LEVEL  MAX_MEM_LEVEL
#endif
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

#define ZIP_COMPR_REPL_MASK 0xffffff00
#define ZIP_COMPR_REPL_SIGN 0x0100 // first 8 bits should be 00 (reserved for compression level), next 8 should be different from ff (to distinguish from -1)

const TCHAR CZipArchive::m_gszCopyright[] = {_T("ZipArchive library Copyright 2000 - 2003 Tadeusz Dracz")};


00044 void CZipAddNewFileInfo::Defaults()
{
      m_iSmartLevel = CZipArchive::zipsmSafeSmart;
      m_iReplaceIndex = -1;
      m_nBufSize = 65536;
      m_iComprLevel = -1; // default

}

CZipArchive::CZipArchive()
{

      m_bRemoveDriveLetter = true;
      m_bIgnoreCRC =
      m_bAutoFlush = false;
      m_centralDir.m_pStorage= &m_storage;
//    m_info.m_stream.zalloc = (alloc_func)_zliballoc;
//    m_info.m_stream.zfree = (free_func)_zlibfree;
//    m_bDetectZlibMemoryLeaks = true;
      m_iFileOpened = nothing;
      SetCaseSensitivity(ZipPlatform::GetSystemCaseSensitivity());
}


CZipArchive::~CZipArchive()
{
      //    Close(); // cannot be here: if an exception is thrown strange things can happen
      EmptyPtrList();

}



00077 void CZipArchive::Open(LPCTSTR szPathName, int iMode, int iVolumeSize)
{
      if (!IsClosed())
      {
            TRACE(_T("%s(%i) : ZipArchive already opened.\n"),__FILE__,__LINE__);
            return;
      }
      m_storage.Open(szPathName, iMode, iVolumeSize);
      OpenInternal(iMode);
}

00088 void CZipArchive::Open(CZipMemFile& mf,int iMode)
{
      if (!IsClosed())
      {
            TRACE(_T("%s(%i) : ZipArchive already opened.\n"),__FILE__,__LINE__);
            return;
      }
      if (iMode != zipOpen && iMode != zipOpenReadOnly && iMode != zipCreate)
      {
            TRACE(_T("%s(%i) : Mode not supported.\n"),__FILE__,__LINE__);
            return;
      }
      m_storage.Open(mf, iMode);
      OpenInternal(iMode);
}


00105 void CZipArchive::OpenInternal(int iMode)
{
      m_pszPassword.Release();
      m_iFileOpened = nothing;
      m_centralDir.Init();
      m_iArchiveSystCompatib = ZipPlatform::GetSystemID();
      m_szRootPath.Empty();
      if ((iMode == zipOpen) ||(iMode == zipOpenReadOnly))
      {
            m_centralDir.Read();
            // if there is at least one file, get system comp. from the first one
            if (m_centralDir.IsValidIndex(0))
            {
                  int iSystemComp = m_centralDir[0]->GetSystemCompatibility();
                  if (ZipCompatibility::IsPlatformSupported(iSystemComp))
                        m_iArchiveSystCompatib = iSystemComp;
            }
      }

}


00127 bool CZipArchive::IsClosed(bool bArchive) const
{
      return  bArchive ?(m_storage.GetCurrentDisk() == -1):(!m_storage.m_pFile || m_storage.m_pFile->IsClosed());
}


00133 void CZipArchive::ThrowError(int err, bool bZlib)
{
      if (bZlib)
            err = CZipException::ZlibErrToZip(err);
      CZipException::Throw(err, IsClosed() ? _T("") : (LPCTSTR)m_storage.m_pFile->GetFilePath());
}



00142 bool CZipArchive::GetFileInfo(CZipFileHeader & fhInfo, WORD uIndex) const
{
      if (IsClosed())
      {
            TRACE(_T("%s(%i) : ZipArchive is closed.\n"),__FILE__,__LINE__);
            return false;
      }

      if (!m_centralDir.IsValidIndex(uIndex))
            return false;

      fhInfo = *(m_centralDir[uIndex]);
      m_centralDir.ConvertFileName(true, false, &fhInfo);
      return true;
}

00158 int CZipArchive::FindFile(LPCTSTR lpszFileName, int iCaseSensitive, bool bFileNameOnly)
{
      if (IsClosed())
      {
            TRACE(_T("%s(%i) : ZipArchive is closed.\n"),__FILE__,__LINE__);
            return (int)-1;
      }
      bool bCS;
      bool bSporadically;
      switch (iCaseSensitive)
      {
      case ffCaseSens:
            bCS = true;
            bSporadically = true;
            break;
      case ffNoCaseSens:
            bCS = false;
            bSporadically = true;
            break;
      default:
            bCS = m_bCaseSensitive;
            bSporadically = false;
      }
      return m_centralDir.FindFile(lpszFileName, bCS, bSporadically, bFileNameOnly);
}

00184 bool CZipArchive::OpenFile(WORD uIndex)
{
      if (!m_centralDir.IsValidIndex(uIndex))
      {
            ASSERT(FALSE);
            return false;
      }
      if (m_storage.IsSpanMode() == 1)
      {
            TRACE(_T("%s(%i) : You cannot extract from the span in creation.\n"),__FILE__,__LINE__);
            return false;
      }


      if (m_iFileOpened)
      {
            TRACE(_T("%s(%i) : A file already opened.\n"),__FILE__,__LINE__);
            return false;
      }

      m_info.Init();
      m_centralDir.OpenFile(uIndex);
      if (CurrentFile()->IsEncrypted())
      {

            if (m_pszPassword.GetSize() == 0)
            {
                  TRACE(_T("%s(%i) : Password not set for the encrypted file.\n"),__FILE__,__LINE__);
                        ThrowError(CZipException::badPassword);
            }
            CryptInitKeys();
            if (!CryptCheck())
                  ThrowError(CZipException::badPassword);

      }
      else if (m_pszPassword.GetSize() != 0)
      {
            TRACE(_T("%s(%i) : Password set for a not encrypted file. Ignoring password.\n"),__FILE__,__LINE__);
      }

      WORD uMethod = CurrentFile()->m_uMethod;

      if ((uMethod != 0) &&(uMethod != Z_DEFLATED))
            ThrowError(CZipException::badZipFile);

      if (uMethod == Z_DEFLATED)
      {
//          m_info.m_stream.opaque =  m_bDetectZlibMemoryLeaks ? &m_list : 0;
            int err = inflateInit2(&m_info.m_stream, -MAX_WBITS);
            //                * windowBits is passed < 0 to tell that there is no zlib header.
            //          * Note that in this case inflate *requires* an extra "dummy" byte
            //          * after the compressed stream in order to complete decompression and
            //          * return Z_STREAM_END.
            CheckForError(err);
      }
      m_info.m_uComprLeft = CurrentFile()->m_uComprSize;
      if (CurrentFile()->IsEncrypted())
            m_info.m_uComprLeft -= ZIPARCHIVE_ENCR_HEADER_LEN;
      m_info.m_uUncomprLeft = CurrentFile()->m_uUncomprSize;
      m_info.m_uCrc32 = 0;
      m_info.m_stream.total_out = 0;
      m_info.m_stream.avail_in = 0;

      m_iFileOpened = extract;
      return true;
}


00252 int CZipArchive::GetLocalExtraField(char *pBuf, int iSize)const
{
      if (IsClosed())
      {
            TRACE(_T("%s(%i) : ZipArchive is closed.\n"),__FILE__,__LINE__);
            return -1;
      }

      if (m_iFileOpened != extract)
      {
            TRACE(_T("%s(%i) : A file must be opened to get the local extra field.\n"),__FILE__,__LINE__);
            return -1;
      }

      int size = m_centralDir.m_pLocalExtraField.GetSize();
      if (!pBuf|| !size)
            return size;

      if (iSize < size)
            size = iSize;

      memcpy(pBuf, m_centralDir.m_pLocalExtraField, size);
      return size;
}

/*
void* CZipArchive::_zliballoc(void* opaque, UINT items, UINT size)
{
      void* p = new char[size * items];
      if (opaque)
      {
            CZipPtrList<void*>* list  = (CZipPtrList<void*>*) opaque;
            list->AddTail(p);
      }
      return p;
}

void CZipArchive::_zlibfree(void* opaque, void* address)
{
      if (opaque)
      {
            CZipPtrList<void*>* list  = (CZipPtrList<void*>*) opaque;
            CZipPtrListIter iter = list->Find(address);
            if (list->IteratorValid(iter))
                  list->RemoveAt(iter);
      }
      delete[] (char*) address;
}
*/

00302 void CZipArchive::CheckForError(int iErr)
{
      if ((iErr == Z_OK) ||(iErr == Z_NEED_DICT))
            return;

      ThrowError(iErr, true);
}

00310 CZipFileHeader* CZipArchive::CurrentFile()
{
      ASSERT(m_centralDir.m_pOpenedFile);
      return m_centralDir.m_pOpenedFile;
}

00316 DWORD CZipArchive::ReadFile(void *pBuf,
                            DWORD iSize)
{
      if (m_iFileOpened != extract)
      {
            TRACE(_T("%s(%i) : Current file must be opened.\n"),__FILE__,__LINE__);
            return 0;
      }

      if (!pBuf || !iSize)
            return 0;

      m_info.m_stream.next_out = (Bytef*)pBuf;
      m_info.m_stream.avail_out = iSize > m_info.m_uUncomprLeft
            ? m_info.m_uUncomprLeft : iSize;


      DWORD iRead = 0;

      // may happen when the file is 0 sized
      bool bForce = m_info.m_stream.avail_out == 0 && m_info.m_uComprLeft > 0;
      while (m_info.m_stream.avail_out > 0 || (bForce && m_info.m_uComprLeft > 0))
      {
            if ((m_info.m_stream.avail_in == 0) &&
                  (m_info.m_uComprLeft >= 0)) // Also when there are zero bytes left!
            {
                  DWORD uToRead = m_info.m_pBuffer.GetSize();
                  if (m_info.m_uComprLeft < uToRead)
                        uToRead = m_info.m_uComprLeft;

                  if (uToRead == 0)
                  {
                        uToRead = 1; // Add dummy byte at end of compressed data.
                  }
                  else
                  {
                        m_storage.Read(m_info.m_pBuffer, uToRead, false);
                        CryptDecodeBuffer(uToRead);
                  }

                  m_info.m_uComprLeft -= uToRead;

                  m_info.m_stream.next_in = (Bytef*)(char*)m_info.m_pBuffer;
                  m_info.m_stream.avail_in = uToRead;
            }

            if (CurrentFile()->m_uMethod == 0)
            {
                  DWORD uToCopy = m_info.m_stream.avail_out < m_info.m_stream.avail_in
                        ? m_info.m_stream.avail_out : m_info.m_stream.avail_in;

                  memcpy(m_info.m_stream.next_out, m_info.m_stream.next_in, uToCopy);

                  m_info.m_uCrc32 = crc32(m_info.m_uCrc32, m_info.m_stream.next_out, uToCopy);

                  m_info.m_uUncomprLeft -= uToCopy;
                  m_info.m_stream.avail_in -= uToCopy;
                  m_info.m_stream.avail_out -= uToCopy;
                  m_info.m_stream.next_out += uToCopy;
                  m_info.m_stream.next_in += uToCopy;
            m_info.m_stream.total_out += uToCopy;
                  iRead += uToCopy;
            }
            else
            {
                  DWORD uTotal = m_info.m_stream.total_out;
                  Bytef* pOldBuf =  m_info.m_stream.next_out;
                  int err = inflate(&m_info.m_stream, Z_SYNC_FLUSH);
                  DWORD uToCopy = m_info.m_stream.total_out - uTotal;

                  m_info.m_uCrc32 = crc32(m_info.m_uCrc32, pOldBuf, uToCopy);

                  m_info.m_uUncomprLeft -= uToCopy;
                  iRead += uToCopy;

                  if (err == Z_STREAM_END)
                        return iRead;

                  CheckForError(err);
            }
      }

      return iRead;
}

00401 void CZipArchive::Close(int iAfterException, bool bUpdateTimeStamp)
{
      // if after an exception - the archive may be closed, but the file may be opened
      if (IsClosed() && (!iAfterException || IsClosed(false)))
      {
            TRACE(_T("%s(%i) : ZipArchive is already closed.\n"),__FILE__,__LINE__);
            return;
      }

      if (m_iFileOpened == extract)
            CloseFile(NULL, iAfterException != afNoException);

      if (m_iFileOpened == compress)
            CloseNewFile(iAfterException != afNoException);

      if (iAfterException != afAfterException && !IsClosed(false)) // in disk spanning when user aborts
            WriteCentralDirectory(false);  // we will flush in CZipStorage::Close

      time_t tNewestTime = 0;

      if (bUpdateTimeStamp)
      {
            int iSize = m_centralDir.m_headers.GetSize();
            for (int i = 0; i< iSize; i++)
            {
                  time_t tFileInZipTime = m_centralDir[i]->GetTime();
                  if (tFileInZipTime > tNewestTime)
                        tNewestTime = tFileInZipTime;
            }
      }
      m_centralDir.Clear();
      CZipString szFileName = m_storage.Close(iAfterException == afAfterException);
      if (bUpdateTimeStamp && !szFileName.IsEmpty())
            ZipPlatform::SetFileModTime(szFileName, tNewestTime);
}

00437 void CZipArchive::WriteCentralDirectory(bool bFlush)
{
      m_centralDir.Write(GetCallback(cbSave));
      if (bFlush)
            m_storage.Flush();
}

00444 void CZipArchive::SetCallback(CZipActionCallback* pCallback, int iWhich)
{
      CallbackType cbs[] = {cbAdd, cbAddTmp, cbAddStore,cbExtract,cbDeleteCnt,cbDelete,cbTest,cbSave, cbGetFromArchive, cbRename, cbReplace};
      int iCount = sizeof(cbs)/sizeof(CallbackType);
      for (int i = 0; i < iCount; i++)
      {
            CallbackType iCallback = cbs[i];
            if (iWhich & iCallback)
                  m_callbacks.Set(pCallback, iCallback);
      }
}

00456 void CZipArchive::SetAdvanced(int iWriteBuffer, int iGeneralBuffer, int iSearchBuffer)
{
      if (!IsClosed())
      {
            TRACE(_T("%s(%i) : Set this options before opening the archive.\n"),__FILE__,__LINE__);
            return;
      }

      m_storage.m_iWriteBufferSize = iWriteBuffer < 1024 ? 1024 : iWriteBuffer;
      m_info.m_iBufferSize = iGeneralBuffer < 1024 ? 1024 : iGeneralBuffer;
      m_centralDir.m_iBufferSize = iSearchBuffer < 1024 ? 1024 : iSearchBuffer;
}

00469 int CZipArchive::CloseFile(CZipFile &file)
{
      CZipString temp = file.GetFilePath();
      file.Close();
      return CloseFile(temp);
}

00476 int CZipArchive::CloseFile(LPCTSTR lpszFilePath, bool bAfterException)
{
      if (m_iFileOpened != extract)
      {
            TRACE(_T("%s(%i) : No opened file.\n"),__FILE__,__LINE__);
            return false;
      }

      int iRet = 1;
      if (!bAfterException)
      {
            if (m_info.m_uUncomprLeft == 0)
            {
                  if (!m_bIgnoreCRC && m_info.m_uCrc32 != CurrentFile()->m_uCrc32)
                        ThrowError(CZipException::badCrc);
            }
            else
                  iRet = -1;


            if (CurrentFile()->m_uMethod == Z_DEFLATED)
                  inflateEnd(&m_info.m_stream);


            if (lpszFilePath)
            {

                  if (!ZipPlatform::SetFileModTime(lpszFilePath, CurrentFile()->GetTime())
                        ||!ZipPlatform::SetFileAttr(lpszFilePath, CurrentFile()->GetSystemAttr()))
                              iRet = -2;
            }

      }

      m_centralDir.CloseFile(bAfterException);

      m_iFileOpened = nothing;
      m_info.ReleaseBuf();
      EmptyPtrList();
      return iRet;
}

00518 bool CZipArchive::OpenNewFile(CZipFileHeader & header,
                              int iLevel,
                              LPCTSTR lpszFilePath, DWORD uInternal)
{
      if (IsClosed())
      {
            TRACE(_T("%s(%i) : ZipArchive is closed.\n"),__FILE__,__LINE__);
            return false;
      }

      if (m_iFileOpened)
      {
            TRACE(_T("%s(%i) : A file already opened.\n"),__FILE__,__LINE__);
            return false;
      }

      if (m_storage.IsSpanMode() == -1)
      {
            TRACE(_T("%s(%i) : You cannot add files to the existing disk spannig archive.\n"),__FILE__,__LINE__);
            return false;
      }

      if (GetCount() ==(WORD)USHRT_MAX)
      {
            TRACE(_T("%s(%i) : Maximum file count inside archive reached.\n"),__FILE__,__LINE__);
            return false;
      }

      DWORD uAttr = 0; // ..compiler
      time_t ttime;
      if (lpszFilePath)
      {

            if (!ZipPlatform::GetFileAttr(lpszFilePath, uAttr))
                  // do not continue - if the file was a directory then not recognizing it will cause
                  // serious errors (need uAttr to recognize it)
                  return false;
            if (!ZipPlatform::GetFileModTime(lpszFilePath, ttime))
                  ttime = time(NULL);
      }

      m_info.Init();


      if (lpszFilePath)
      {
            header.SetTime(ttime);
            SetFileHeaderAttr(header, uAttr); // set system compatibility as well
      }
      else
            header.SetSystemCompatibility(m_iArchiveSystCompatib);

      CZipString szFileName = header.GetFileName();


      bool bIsDirectory = header.IsDirectory();
      if (bIsDirectory)
      {
            int iNameLen = szFileName.GetLength();
            if (!iNameLen || !CZipPathComponent::IsSeparator(szFileName[iNameLen-1]))
            {
                  szFileName += CZipPathComponent::m_cSeparator;
                  header.SetFileName(szFileName);
            }
      }

      if (szFileName.IsEmpty())
      {
            szFileName.Format(_T("file%i"), GetCount());
            header.SetFileName(szFileName);
      }

      // make sure that all slashes are correct (as the current system default)
      // because AddNewFile calls InsertFindFastElement if necessary and
      // the find array keeps all the files already converted to the current system standards
      // we do not perform Oem->Ansi here, because who would pass oem values here?
      //
      ZipCompatibility::SlashBackslashChg(header.m_pszFileName, true);

      bool bEncrypted = m_pszPassword.GetSize() != 0;

#ifdef _DEBUG
      if (bIsDirectory && bEncrypted)
      TRACE(_T("%s(%i) : Encrypting a directory. It's pointless.\n\
      Clear the password before adding a directory.\n"),__FILE__,__LINE__);
#endif



      int iReplaceIndex = -1;
      bool bReplace = (iLevel & 0xffff) == ZIP_COMPR_REPL_SIGN;
      if (bReplace)
      {
            int iMask = ZIP_COMPR_REPL_MASK;
            iReplaceIndex = (iLevel & iMask) >> 16;
            iLevel = (char)(iLevel & ~iMask);
            ASSERT(iLevel == 0);
      }
      else
            uInternal = 0;

      if (iLevel < -1 || iLevel > 9)
            iLevel = -1;

      if (!header.PrepareData(iLevel, m_storage.IsSpanMode() == 1, bEncrypted))
            ThrowError(CZipException::tooLongFileName);

      if (bReplace)
      {
            uInternal += header.GetSize(true);
            if (header.IsEncrypted())
                  uInternal += ZIPARCHIVE_ENCR_HEADER_LEN;
            if (header.IsDataDescr())
                  uInternal += ZIPARCHIVE_DATADESCRIPTOR_LEN + 4; // CZipCentralDir::CloseNewFile
      }
      m_centralDir.AddNewFile(header, iReplaceIndex);
      if (bReplace)
            MakeSpaceForReplace(iReplaceIndex, uInternal, szFileName);

      // this ensures the conversion will take place anyway (must take because we are going
      //    to write the local header in a moment
      m_centralDir.ConvertFileName(false, m_centralDir.m_bConvertAfterOpen);

      CurrentFile()->WriteLocal(m_storage);

      // we have written the local header, but if we keep filenames not converted
      // in memory , we have to restore the non-converted value
      if (m_centralDir.m_bConvertAfterOpen)
            CurrentFile()->SetFileName(szFileName);

      if (bEncrypted)
      {
            CZipAutoBuffer buf(ZIPARCHIVE_ENCR_HEADER_LEN);
            // use pseudo-crc since we don't know it yet
            CryptCryptHeader((long)header.m_uModTime << 16, buf);
            m_storage.Write(buf, ZIPARCHIVE_ENCR_HEADER_LEN, false);
      }


      m_info.m_uComprLeft = 0;
    m_info.m_stream.avail_in = (uInt)0;
    m_info.m_stream.avail_out = (uInt)m_info.m_pBuffer.GetSize();
    m_info.m_stream.next_out = (Bytef*)(char*)m_info.m_pBuffer;
    m_info.m_stream.total_in = 0;
    m_info.m_stream.total_out = 0;

      if (bIsDirectory && (CurrentFile()->m_uMethod != 0))
            CurrentFile()->m_uMethod = 0;

      if (CurrentFile()->m_uMethod == Z_DEFLATED)
    {
//        m_info.m_stream.opaque = m_bDetectZlibMemoryLeaks ? &m_list : 0;

        int err = deflateInit2(&m_info.m_stream, iLevel,
                  Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);

            CheckForError(err);
    }
      m_iFileOpened = compress;
      return true;
}


00681 bool CZipArchive::ExtractFile(WORD uIndex,
                              LPCTSTR lpszPath,
                              bool bFullPath,
                              LPCTSTR lpszNewName,
                              DWORD nBufSize)
{

      if (!nBufSize && !lpszPath)
            return false;

      CZipFileHeader header;
      GetFileInfo(header, uIndex); // to ensure that slash and oem conversions take place
      CZipString szFileNameInZip = (LPCTSTR)header.GetFileName();
      CZipString szFile = PredictExtractedFileName(szFileNameInZip, lpszPath, bFullPath, lpszNewName);
      CZipActionCallback* pCallback = GetCallback(cbExtract);
      if (pCallback)
            pCallback->Init(szFileNameInZip, szFile);


      if (header.IsDirectory())
      {
            if (pCallback)
                  pCallback->SetTotal(0); // in case of calling LeftToDo afterwards

            ZipPlatform::ForceDirectory(szFile);
            ZipPlatform::SetFileAttr(szFile, header.GetSystemAttr());

            if (pCallback)
                  pCallback->CallbackEnd();
            return true;
      }
      else
      {
            if (pCallback)
                  pCallback->SetTotal(header.m_uUncomprSize);

            if (!OpenFile(uIndex))
                  return false;

            CZipPathComponent zpc(szFile);
            ZipPlatform::ForceDirectory(zpc.GetFilePath());
            CZipFile f(szFile, CZipFile::modeWrite |
                  CZipFile::modeCreate | CZipFile::shareDenyWrite);
            DWORD iRead;
            CZipAutoBuffer buf(nBufSize);
            int iAborted = 0;
            do
            {
                  iRead = ReadFile(buf, buf.GetSize());
                  if (iRead)
                  {
                        f.Write(buf, iRead);
                        if (pCallback)
                              if (!(*pCallback)(iRead))
                              {
                                    if (iRead == buf.GetSize() && ReadFile(buf, 1) != 0) // test one byte if there is something left
                                          iAborted = CZipException::abortedAction;
                                    else
                                          iAborted = CZipException::abortedSafely; // we did it!
                                    break;
                              }

                  }
            }
            while (iRead == buf.GetSize());
            bool bRet = CloseFile(f) == 1;
            if (!bRet && iAborted == CZipException::abortedSafely)
                  iAborted = CZipException::abortedAction; // sorry, finished, but not successfull

            if (pCallback)
                  pCallback->CallbackEnd();

            if (iAborted)
                  CZipException::Throw(iAborted, szFile); // throw to distuingiush from other return codes
            return bRet;

      }
}

00760 bool CZipArchive::ExtractFile(WORD uIndex,
                              CZipMemFile& mf,
                              DWORD nBufSize)
{
      if (!nBufSize)
            return false;

      CZipFileHeader header;
      GetFileInfo(header, uIndex); // to ensure that slash and oem conversions take place
      CZipActionCallback* pCallback = GetCallback(cbExtract);
      if (pCallback)
      {
            pCallback->Init(header.GetFileName());
            pCallback->SetTotal(header.m_uUncomprSize);
      }

      if (header.IsDirectory() || !OpenFile(uIndex))
            return false;




      CZipAutoBuffer buf(nBufSize);
      mf.SeekToEnd();
      DWORD iRead;
      int iAborted = 0;
      do
      {
            iRead = ReadFile(buf, buf.GetSize());
            if (iRead)
            {
                  mf.Write(buf, iRead);
                  if (pCallback)
                        if (!(*pCallback)(iRead))
                        {
                              if (iRead == buf.GetSize() && ReadFile(buf, 1) != 0) // test one byte if there is something left
                                    iAborted = CZipException::abortedAction;
                              else
                                    iAborted = CZipException::abortedSafely; // we did it!
                              break;
                        }
            }
      }
      while (iRead == buf.GetSize());
      bool bRet = CloseFile() == 1;
      if (!bRet && iAborted == CZipException::abortedSafely)
            iAborted = CZipException::abortedAction; // sorry, finished, but not successfull

      if (pCallback)
            pCallback->CallbackEnd();

      if (iAborted)
            CZipException::Throw(iAborted); // throw to distuingiush from other return codes
      return bRet;
}


00817 void CZipArchive::SetExtraField(const char *pBuf, WORD iSize)
{
      if (m_iFileOpened != compress)
      {
            TRACE(_T("%s(%i) : A new file must be opened.\n"),__FILE__,__LINE__);
            return;
      }
      if (!pBuf || !iSize)
            return;

      CurrentFile()->m_pExtraField.Allocate(iSize);
      memcpy(CurrentFile()->m_pExtraField, pBuf, iSize);
}

00831 bool CZipArchive::WriteNewFile(const void *pBuf, DWORD iSize)
{
      if (m_iFileOpened != compress)
      {
            TRACE(_T("%s(%i) : A new file must be opened.\n"),__FILE__,__LINE__);
            return false;
      }


    m_info.m_stream.next_in = (Bytef*)pBuf;
    m_info.m_stream.avail_in = iSize;
    CurrentFile()->m_uCrc32 = crc32(CurrentFile()->m_uCrc32, (Bytef*)pBuf, iSize);


    while (m_info.m_stream.avail_in > 0)
    {
        if (m_info.m_stream.avail_out == 0)
        {
                  CryptEncodeBuffer();
                  m_storage.Write(m_info.m_pBuffer, m_info.m_uComprLeft, false);
                  m_info.m_uComprLeft = 0;
            m_info.m_stream.avail_out = m_info.m_pBuffer.GetSize();
            m_info.m_stream.next_out = (Bytef*)(char*)m_info.m_pBuffer;
        }

        if (CurrentFile()->m_uMethod == Z_DEFLATED)
        {
            DWORD uTotal = m_info.m_stream.total_out;
            int err = deflate(&m_info.m_stream,  Z_NO_FLUSH);
                  CheckForError(err);
            m_info.m_uComprLeft += m_info.m_stream.total_out - uTotal;
        }
        else
        {
            DWORD uToCopy = (m_info.m_stream.avail_in < m_info.m_stream.avail_out)
                        ? m_info.m_stream.avail_in : m_info.m_stream.avail_out;

                  memcpy(m_info.m_stream.next_out, m_info.m_stream.next_in, uToCopy);

            m_info.m_stream.avail_in -= uToCopy;
            m_info.m_stream.avail_out -= uToCopy;
            m_info.m_stream.next_in += uToCopy;
            m_info.m_stream.next_out += uToCopy;
            m_info.m_stream.total_in += uToCopy;
            m_info.m_stream.total_out += uToCopy;
            m_info.m_uComprLeft += uToCopy;
        }
    }

      return true;
}

00883 bool CZipArchive::CloseNewFile(bool bAfterException)
{
      if (m_iFileOpened != compress)
      {
            TRACE(_T("%s(%i) : A new file must be opened.\n"),__FILE__,__LINE__);
            return false;
      }

    m_info.m_stream.avail_in = 0;
    if (!bAfterException)
      {
            int err = Z_OK;
            if (CurrentFile()->m_uMethod == Z_DEFLATED)
                  while (err == Z_OK)
                  {
                        if (m_info.m_stream.avail_out == 0)
                        {
                              CryptEncodeBuffer();
                              m_storage.Write(m_info.m_pBuffer, m_info.m_uComprLeft, false);
                              m_info.m_uComprLeft = 0;
                              m_info.m_stream.avail_out = m_info.m_pBuffer.GetSize();
                              m_info.m_stream.next_out = (Bytef*)(char*)m_info.m_pBuffer;
                        }
                        DWORD uTotal = m_info.m_stream.total_out;
                        err = deflate(&m_info.m_stream,  Z_FINISH);
                        m_info.m_uComprLeft += m_info.m_stream.total_out - uTotal;
                  }

            if (err == Z_STREAM_END)
                  err = Z_OK;
            CheckForError(err);

            if (m_info.m_uComprLeft > 0)
            {
                  CryptEncodeBuffer();
                  m_storage.Write(m_info.m_pBuffer, m_info.m_uComprLeft, false);
            }

            if (CurrentFile()->m_uMethod == Z_DEFLATED)
            {
                  err = deflateEnd(&m_info.m_stream);
                  CheckForError(err);
            }


            // it may be increased by the encrypted header size
            CurrentFile()->m_uComprSize += m_info.m_stream.total_out;
            CurrentFile()->m_uUncomprSize = m_info.m_stream.total_in;

            m_centralDir.CloseNewFile();
      }
      else
            m_centralDir.m_pOpenedFile = NULL;

      m_iFileOpened = nothing;
      m_info.ReleaseBuf();
      EmptyPtrList();

      if (m_bAutoFlush && !bAfterException)
            Flush();

      return true;
}

00947 void CZipArchive::DeleteFile(WORD uIndex)
{
      CZipWordArray indexes;
      indexes.Add(uIndex);
      DeleteFiles(indexes);
}

00954 void CZipArchive::GetIndexes(const CZipStringArray &aNames, CZipWordArray& aIndexes)
{
      if (IsClosed())
      {
            TRACE(_T("%s(%i) : ZipArchive is closed.\n"),__FILE__,__LINE__);
            return;
      }
      int iSize = aNames.GetSize();
      for (WORD i = 0; i < iSize; i++)
      {
            int idx = FindFile(aNames[i], ffDefault, false);
            if (idx != -1)
                  aIndexes.Add((WORD)idx);
      }
}

00970 void CZipArchive::DeleteFiles(const CZipStringArray &aNames)
{
      CZipWordArray indexes;
      GetIndexes(aNames, indexes);
      DeleteFiles(indexes);
}


00978 void CZipArchive::DeleteFiles(CZipWordArray &aIndexes)
{
      if (IsClosed())
      {
            TRACE(_T("%s(%i) : ZipArchive is closed.\n"),__FILE__,__LINE__);
            return;
      }

      if (m_storage.IsSpanMode())
      {
            TRACE(_T("%s(%i) : You cannot delete files from the disk spannig archive.\n"),__FILE__,__LINE__);
            return;
      }

      if (m_iFileOpened)
      {
            TRACE(_T("%s(%i) : You cannot delete files if there is a file opened.\n"),__FILE__,__LINE__);
            return;
      }

      CZipActionCallback* pCallback = GetCallback(cbDeleteCnt);
      if (pCallback)
            pCallback->Init();

      int uSize = aIndexes.GetSize();
      if (!uSize)
      {
            TRACE(_T("%s(%i) : The indekses array is empty.\n"),__FILE__,__LINE__);
            return;
      }

      // remove all - that's easy so don't waste the time
      if (uSize == GetCount())
      {
            pCallback = GetCallback(cbDelete);
            if (pCallback)
            {
                  // do it right and sent the notification
                  pCallback->Init();
                  pCallback->SetTotal(uSize);
            }

            m_centralDir.RemoveFromDisk();
            m_storage.m_pFile->SetLength(m_centralDir.GetBytesBefore());
            m_centralDir.RemoveAll();
            if (m_bAutoFlush)
                  Flush();
            if (pCallback)
                  pCallback->CallbackEnd();
            return;
      }

      aIndexes.Sort(true);

      CZipArray<CZipDeleteInfo> aInfo;

      int iDelIndex = 0;


      int iStep = 0; // for the compiler
      if (pCallback)
      {
            pCallback->SetTotal(GetCount());
            iStep = CZipActionCallback::m_iStep; // we don't want to wait forever
      }

      int i;
      int uMaxDelIndex = aIndexes[uSize - 1];
      for (i = aIndexes[0]; i < GetCount(); i++)
      {
            CZipFileHeader* pHeader = m_centralDir[i];
            bool bDelete;
            if (i <= uMaxDelIndex && i == aIndexes[iDelIndex])
            {
                  iDelIndex++;
                  bDelete = true;
            }
            else
                  bDelete = false;
            aInfo.Add(CZipDeleteInfo(pHeader, bDelete));
            if (pCallback && (!(i % iStep)))
                  if (!(*pCallback)(iStep))
                        ThrowError(CZipException::abortedSafely);
      }
      ASSERT(iDelIndex == uSize);

      uSize = aInfo.GetSize();
      if (!uSize) // it is possible
            return;

      // now we start deleting (not safe to break)
      pCallback = GetCallback(cbDelete);
      if (pCallback)
            pCallback->Init();


      m_centralDir.RemoveFromDisk();

      DWORD uTotalToMoveBytes = 0, uLastOffset = m_storage.m_pFile->GetLength() - m_centralDir.GetBytesBefore();
      // count the number of bytes to move
      for (i = uSize - 1; i >=0 ; i--)
      {
            const CZipDeleteInfo& di = aInfo[i];
            if (!di.m_bDelete)
                  uTotalToMoveBytes += uLastOffset - di.m_pHeader->m_uOffset;
            uLastOffset = di.m_pHeader->m_uOffset;
      }
      if (pCallback)
            pCallback->CallbackEnd();


      if (pCallback)
            pCallback->SetTotal(uTotalToMoveBytes);


      m_info.Init();

      DWORD uMoveBy = 0, uOffsetStart = 0;
      for (i = 0; i < uSize; i++)
      {
            const CZipDeleteInfo& di = aInfo[i];

            if (di.m_bDelete)
            {
                  // next hole
                  DWORD uTemp = di.m_pHeader->m_uOffset;
                  m_centralDir.RemoveFile(di.m_pHeader); // first remove
                  if (uOffsetStart)
                  {
                        // copy the files over a previous holes
                        MovePackedFiles(uOffsetStart, uTemp, uMoveBy, pCallback);
                        uOffsetStart = 0;  // never be at the beginning, because the first file is always to be deleted
                  }
                  if (i == uSize - 1)
                        uTemp = (m_storage.m_pFile->GetLength() - m_centralDir.GetBytesBefore()) - uTemp;
                  else
                        uTemp = aInfo[i+1].m_pHeader->m_uOffset - uTemp;

                  uMoveBy += uTemp;

            }
            else
            {
                  if (uOffsetStart == 0) // find contiuos area to move
                        uOffsetStart = di.m_pHeader->m_uOffset;
                  di.m_pHeader->m_uOffset -= uMoveBy;
            }

      }
      if (uOffsetStart)
            MovePackedFiles(uOffsetStart,
                  m_storage.m_pFile->GetLength() - m_centralDir.GetBytesBefore(),
                  uMoveBy, pCallback);

      m_info.ReleaseBuf();
      if (uMoveBy) // just in case
            m_storage.m_pFile->SetLength(m_storage.m_pFile->GetLength() - uMoveBy);

      if (pCallback)
            pCallback->CallbackEnd();

      if (m_bAutoFlush)
            Flush();
}



01145 bool CZipArchive::AddNewFile(LPCTSTR lpszFilePath,
                             int iComprLevel,
                             bool bFullPath,
                                           int iSmartLevel,
                             unsigned long nBufSize)
{

      CZipAddNewFileInfo zanfi (lpszFilePath, bFullPath);
      zanfi.m_iComprLevel = iComprLevel;
      zanfi.m_iSmartLevel = zipsmSafeSmart;
      zanfi.m_nBufSize = nBufSize;
      return AddNewFile(zanfi);
}

bool CZipArchive::AddNewFileDrv(LPCTSTR lpszFilePath,
                               int iComprLevel,
                               bool bFullPath,
                               int iSmartLevel,
                               unsigned long nBufSize)
{

   CZipAddNewFileInfo zanfi (lpszFilePath, bFullPath);
   zanfi.m_iComprLevel = iComprLevel;
   zanfi.m_iSmartLevel = zipsmSafeSmart;
   zanfi.m_nBufSize = nBufSize;
   return AddNewFileDrv(zanfi);
}

01173 bool CZipArchive::AddNewFile(LPCTSTR lpszFilePath,
                                           LPCTSTR lpszFileNameInZip,
                             int iComprLevel,
                                           int iSmartLevel,
                             unsigned long nBufSize)
{
      CZipAddNewFileInfo zanfi(lpszFilePath, lpszFileNameInZip);
      zanfi.m_iComprLevel = iComprLevel;
      zanfi.m_iSmartLevel = zipsmSafeSmart;
      zanfi.m_nBufSize = nBufSize;
      return AddNewFile(zanfi);
}

01186 bool CZipArchive::AddNewFile(CZipMemFile& mf,
                                           LPCTSTR lpszFileNameInZip,
                             int iComprLevel,
                                           int iSmartLevel,
                             unsigned long nBufSize)
{
      CZipAddNewFileInfo zanfi(&mf, lpszFileNameInZip);
      zanfi.m_iComprLevel = iComprLevel;
      zanfi.m_iSmartLevel = zipsmSafeSmart;
      zanfi.m_nBufSize = nBufSize;
      return AddNewFile(zanfi);
}


bool CZipArchive::AddNewFileDrv(CZipAddNewFileInfo& info)
{
   // no need for ASSERT and TRACE here - it will be done by OpenNewFile

   if (!m_info.m_iBufferSize)
      return false;
   CZipPathComponent::RemoveSeparators(info.m_szFilePath);
   if (!info.m_szFilePath.IsEmpty()) // it may be empty after removing sep.
   {
      if (info.m_szFileNameInZip.IsEmpty())
      {
         CZipPathComponent zpc(info.m_szFilePath);
         info.m_szFileNameInZip = info.m_bFullPath ? zpc.GetNoDrive() : TrimRootPath(zpc);
      }
   }
   else if (!info.m_pFile)
      return false;

   bool bSpan = GetSpanMode() != 0;

   // checking the iReplace index
   if (!UpdateReplaceIndex(info.m_iReplaceIndex, info.m_szFileNameInZip))
      return false;

   bool bReplace = info.m_iReplaceIndex >= 0;

   DWORD uAttr;
   time_t ttime;
   if (info.m_pFile)
   {
      uAttr = ZipPlatform::GetDefaultAttributes();
      ttime = time(NULL);
   }
   else
   {
      if (!ZipPlatform::GetFileAttr(info.m_szFilePath, uAttr))
         return false; // we don't know whether it is a file or a directory
      if (!ZipPlatform::GetFileModTime(info.m_szFilePath, ttime))
         ttime = time(NULL);
   }
   CZipFileHeader header;
   header.SetFileName(info.m_szFileNameInZip);
   if (ZipPlatform::GetSystemID() != ZipCompatibility::zcUnix)
      uAttr |= ZipCompatibility::ConvertToSystem(uAttr, ZipPlatform::GetSystemID(), ZipCompatibility::zcUnix);  // make it readable under Unix as well, since it stores its attributes in HIWORD(uAttr)
   SetFileHeaderAttr(header, uAttr);
   header.SetTime(ttime);
   bool bInternal = (info.m_iSmartLevel & zipsmInternal01) != 0;
   CZipActionCallback* pCallback = NULL;
   if (!bInternal)
   {
      pCallback = GetCallback(cbAdd);
      if (pCallback)
         pCallback->Init(info.m_szFileNameInZip, info.m_szFilePath);
   }



   if (header.IsDirectory()) // will never be when m_pFile is not NULL, so we don't check it
   {
      ASSERT(!info.m_pFile); // should never happened
      ASSERT(!bInternal);

      if (pCallback)
         pCallback->SetTotal(0); // in case of calling LeftToDo afterwards

      // clear password for a directory
      bool bRet = false;
      CZipSmClrPass smcp;
      if (info.m_iSmartLevel & zipsmCPassDir)
         smcp.ClearPasswordSmartly(this);

      bRet = OpenNewFile(header, bReplace ? (info.m_iReplaceIndex << 16) | ZIP_COMPR_REPL_SIGN : 0);

      CloseNewFile();
      if (pCallback)
         pCallback->CallbackEnd();

      return bRet;
   }

   CZipSmClrPass smcp;
   bool bIsCompression = info.m_iComprLevel != 0;
   bool bEff = (info.m_iSmartLevel & zipsmCheckForEff)&& bIsCompression;
   bool bCheckForZeroSized = (info.m_iSmartLevel & zipsmCPFile0) && !GetPassword().IsEmpty();
   bool bCheckForSmallFiles = (info.m_iSmartLevel & zipsmNotCompSmall) && bIsCompression;
   DWORD iFileSize = DWORD(-1);
   bool bNeedTempArchive = (bEff && bSpan) || (bReplace && bIsCompression);
   if (bCheckForSmallFiles || bCheckForZeroSized || bNeedTempArchive)
   {

      if (info.m_pFile)
         iFileSize = info.m_pFile->GetLength();
      else
      {
         if (!ZipPlatform::GetFileSize(info.m_szFilePath, iFileSize) && bEff)
            bEff = false; // the file size is needed only when eff. in span mode
      }
      if (iFileSize !=  DWORD(-1))
      {
         if (bCheckForZeroSized && iFileSize == 0)
            smcp.ClearPasswordSmartly(this);
         if (bCheckForSmallFiles && iFileSize < 5)
            info.m_iComprLevel = 0;
      }
   }
   bool bEffInMem = bEff && (info.m_iSmartLevel & zipsmMemoryFlag);
   CZipString szTempFileName;
   if (bNeedTempArchive && (bEffInMem ||
      !(szTempFileName = ZipPlatform::GetTmpFileName(
         m_szTempPath.IsEmpty() ? NULL : (LPCTSTR)m_szTempPath, iFileSize)
      ).IsEmpty()))
   {
      CZipMemFile* pmf = NULL;
      CZipArchive zip;
      try
      {
         // compress first to a temporary file, if ok - copy the data, if not - add storing

         if (bEffInMem)
         {
            pmf = new CZipMemFile;
            zip.Open(*pmf, zipCreate);
         }
         else
            zip.Open(szTempFileName, zipCreate);
         zip.SetRootPath(m_szRootPath);
         zip.SetPassword(GetPassword());
         zip.SetSystemCompatibility(m_iArchiveSystCompatib);
         zip.SetCallback(pCallback, cbAdd);
         // create a temporary file
         int iTempReplaceIndex = info.m_iReplaceIndex;
         info.m_iSmartLevel = zipsmLazy;
         info.m_iReplaceIndex = -1;
         if (!zip.AddNewFile(info))
            throw false;
         info.m_iReplaceIndex = iTempReplaceIndex;

         // this may also happen when bReplace, but not in span mode
         if (bEff)
         {
            CZipFileHeader fh;
            zip.GetFileInfo(fh, 0);
            if (!fh.CompressionEfficient())
            {
               info.m_iComprLevel = 0;
               info.m_iSmartLevel = zipsmInternal01;
               // compression is pointless, store instead
               throw AddNewFile(info);
            }
         }

         m_info.Init();
         throw GetFromArchive(zip, 0, info.m_iReplaceIndex, true, GetCallback(cbAddTmp));
      }
      catch (bool bRet)
      {

         zip.Close(!bRet); // that doesn't really matter how it will be closed

         if (pmf)
            delete pmf;
         if (!bEffInMem)
            ZipPlatform::RemoveFile(szTempFileName, false);
         m_info.ReleaseBuf();
         return bRet;
      }
      catch (...)
      {
         zip.Close(true);

         if (pmf)
            delete pmf;
         if (!bEffInMem)
            ZipPlatform::RemoveFile(szTempFileName, false);
         m_info.ReleaseBuf();
         throw;
      }
   }

   // try to open before adding
   CZipFile f;
   CZipAbstractFile *pf;
   if (info.m_pFile)
      pf = info.m_pFile;
   else
   {
      if (!f.Open(info.m_szFilePath, CZipFile::modeRead | CZipFile::shareDenyWrite, false))
      {
         if (pCallback)
            pCallback->CallbackEnd();
         return false;
      }
      pf = &f;
   }

   ASSERT(pf);
   // call init before opening (in case of exception we have the names)
   iFileSize = pf->GetLength();


   bool bRet;
   if (bReplace)
   {
      ASSERT(!bIsCompression);
      bRet = OpenNewFile(header, (info.m_iReplaceIndex << 16) | ZIP_COMPR_REPL_SIGN , NULL, iFileSize);
   }
   else
      bRet = OpenNewFile(header, info.m_iComprLevel);
   if (!bRet)
   {
      if (pCallback)
         pCallback->CallbackEnd();

      return false;
   }
   if (bInternal)
   {
      // we do it here, because if in OpenNewFile is replacing
      // then we get called cbReplace callback before and it would
      // overwrite callback information written in pCallback->Init
      pCallback = GetCallback(cbAddStore);
      if (pCallback)
         pCallback->Init(info.m_szFileNameInZip, info.m_szFilePath);
   }
   if (pCallback)
      pCallback->SetTotal(iFileSize);

   CZipAutoBuffer buf(info.m_nBufSize);
   DWORD iRead;
   int iAborted = 0;
   do
   {
      iRead = pf->Read(buf, info.m_nBufSize);
      if (iRead)
      {
         WriteNewFile(buf, iRead);
         if (pCallback)
            if (!(*pCallback)(iRead))
            {
               // todo: we could remove here the bytes of the file partially added if not disk-spanning
               if (iRead == buf.GetSize() && pf->Read(buf, 1) != 0) // test one byte if there is something left
               {
                  if (!m_storage.IsSpanMode() && !bReplace)
                  {
                     RemoveLast(true);
                     CloseNewFile(true);
                     iAborted = CZipException::abortedSafely;
                  }
                  else
                     iAborted = CZipException::abortedAction;
               }
               else
               {
                  iAborted = CZipException::abortedSafely; // we did it!
                  CloseNewFile();
               }
               break;
            }

      }

   }
   while (iRead == buf.GetSize());
   if (!iAborted)
      CloseNewFile();

   if (pCallback)
      pCallback->CallbackEnd();

   if (iAborted)
      CZipException::Throw(iAborted); // throw to distuinguish from other return codes

   if (bEff)
   {
      // remove the last file and add it without the compression if needed
      if (!info.m_pFile)
         f.Close();

      buf.Release();
      if (RemoveLast())
      {
         info.m_iComprLevel = 0;
         info.m_iSmartLevel = zipsmInternal01;
         return AddNewFile(info);
      }
   }
   return true;

}

01490 bool CZipArchive::AddNewFile(CZipAddNewFileInfo& info)
{
      // no need for ASSERT and TRACE here - it will be done by OpenNewFile

      if (!m_info.m_iBufferSize)
            return false;
      CZipPathComponent::RemoveSeparators(info.m_szFilePath);
      if (!info.m_szFilePath.IsEmpty()) // it may be empty after removing sep.
      {
            if (info.m_szFileNameInZip.IsEmpty())
            {
                  CZipPathComponent zpc(info.m_szFilePath);
                  if (info.m_bFullPath)
                  {
                        if (m_bRemoveDriveLetter)
                              info.m_szFileNameInZip = zpc.GetNoDrive();
                  }
                  else
                        info.m_szFileNameInZip = TrimRootPath(zpc);
            }
      }
      else if (!info.m_pFile)
            return false;

      bool bSpan = GetSpanMode() != 0;

      // checking the iReplace index
      if (!UpdateReplaceIndex(info.m_iReplaceIndex, info.m_szFileNameInZip))
            return false;

      bool bReplace = info.m_iReplaceIndex >= 0;

      DWORD uAttr;
      time_t ttime;
      if (info.m_pFile)
      {
            uAttr = ZipPlatform::GetDefaultAttributes();
            ttime = time(NULL);
      }
      else
      {
            if (!ZipPlatform::GetFileAttr(info.m_szFilePath, uAttr))
                  return false; // we don't know whether it is a file or a directory
            if (!ZipPlatform::GetFileModTime(info.m_szFilePath, ttime))
                  ttime = time(NULL);
      }
      CZipFileHeader header;
      header.SetFileName(info.m_szFileNameInZip);
      if (ZipPlatform::GetSystemID() != ZipCompatibility::zcUnix)
            uAttr |= ZipCompatibility::ConvertToSystem(uAttr, ZipPlatform::GetSystemID(), ZipCompatibility::zcUnix);  // make it readable under Unix as well, since it stores its attributes in HIWORD(uAttr)
      SetFileHeaderAttr(header, uAttr);
      header.SetTime(ttime);
      bool bInternal = (info.m_iSmartLevel & zipsmInternal01) != 0;
      CZipActionCallback* pCallback = NULL;
      if (!bInternal)
      {
            pCallback = GetCallback(cbAdd);
            if (pCallback)
                  pCallback->Init(info.m_szFileNameInZip, info.m_szFilePath);
      }



      if (header.IsDirectory()) // will never be when m_pFile is not NULL, so we don't check it
      {
            ASSERT(!info.m_pFile); // should never happened
            ASSERT(!bInternal);

            if (pCallback)
                  pCallback->SetTotal(0); // in case of calling LeftToDo afterwards

            // clear password for a directory
            bool bRet = false;
            CZipSmClrPass smcp;
            if (info.m_iSmartLevel & zipsmCPassDir)
                  smcp.ClearPasswordSmartly(this);

            bRet = OpenNewFile(header, bReplace ? (info.m_iReplaceIndex << 16) | ZIP_COMPR_REPL_SIGN : 0);

            CloseNewFile();
            if (pCallback)
                  pCallback->CallbackEnd();

            return bRet;
      }

      CZipSmClrPass smcp;
      bool bIsCompression = info.m_iComprLevel != 0;
      bool bEff = (info.m_iSmartLevel & zipsmCheckForEff)&& bIsCompression;
      bool bCheckForZeroSized = (info.m_iSmartLevel & zipsmCPFile0) && !GetPassword().IsEmpty();
      bool bCheckForSmallFiles = (info.m_iSmartLevel & zipsmNotCompSmall) && bIsCompression;
      DWORD iFileSize = DWORD(-1);
      bool bNeedTempArchive = (bEff && bSpan) || (bReplace && bIsCompression);
      if (bCheckForSmallFiles || bCheckForZeroSized || bNeedTempArchive)
      {

            if (info.m_pFile)
                  iFileSize = info.m_pFile->GetLength();
            else
            {
                  if (!ZipPlatform::GetFileSize(info.m_szFilePath, iFileSize) && bEff)
                        bEff = false; // the file size is needed only when eff. in span mode
            }
            if (iFileSize !=  DWORD(-1))
            {
                  if (bCheckForZeroSized && iFileSize == 0)
                        smcp.ClearPasswordSmartly(this);
                  if (bCheckForSmallFiles && iFileSize < 5)
                        info.m_iComprLevel = 0;
            }
      }
      bool bEffInMem = bEff && (info.m_iSmartLevel & zipsmMemoryFlag);
      CZipString szTempFileName;
      if (bNeedTempArchive && (bEffInMem ||
            !(szTempFileName = ZipPlatform::GetTmpFileName(
                  m_szTempPath.IsEmpty() ? NULL : (LPCTSTR)m_szTempPath, iFileSize)
            ).IsEmpty()))
      {
            CZipMemFile* pmf = NULL;
            CZipArchive zip;
            try
            {
                  // compress first to a temporary file, if ok - copy the data, if not - add storing

                  if (bEffInMem)
                  {
                        pmf = new CZipMemFile;
                        zip.Open(*pmf, zipCreate);
                  }
                  else
                        zip.Open(szTempFileName, zipCreate);
                  zip.SetRootPath(m_szRootPath);
                  zip.SetPassword(GetPassword());
                  zip.SetSystemCompatibility(m_iArchiveSystCompatib);
                  zip.SetCallback(pCallback, cbAdd);
                  // create a temporary file
                  int iTempReplaceIndex = info.m_iReplaceIndex;
                  info.m_iSmartLevel = zipsmLazy;
                  info.m_iReplaceIndex = -1;
                  if (!zip.AddNewFile(info))
                        throw false;
                  info.m_iReplaceIndex = iTempReplaceIndex;

                  // this may also happen when bReplace, but not in span mode
                  if (bEff)
                  {
                        CZipFileHeader fh;
                        zip.GetFileInfo(fh, 0);
                        if (!fh.CompressionEfficient())
                        {
                              info.m_iComprLevel = 0;
                              info.m_iSmartLevel = zipsmInternal01;
                              // compression is pointless, store instead
                              throw AddNewFile(info);
                        }
                  }

                  m_info.Init();
                  throw GetFromArchive(zip, 0, info.m_iReplaceIndex, true, GetCallback(cbAddTmp));
            }
            catch (bool bRet)
            {

                  zip.Close(!bRet); // that doesn't really matter how it will be closed
                  if (pmf)
                        delete pmf;
                  if (!bEffInMem)
                        ZipPlatform::RemoveFile(szTempFileName, false);
                  m_info.ReleaseBuf();
                  return bRet;
            }
            catch (...)
            {
                  zip.Close(true);
                  if (pmf)
                        delete pmf;
                  if (!bEffInMem)
                        ZipPlatform::RemoveFile(szTempFileName, false);
                  m_info.ReleaseBuf();
                  throw;
            }
      }

      // try to open before adding
      CZipFile f;
      CZipAbstractFile *pf;
      if (info.m_pFile)
            pf = info.m_pFile;
      else
      {
            // cannot be shareDenyWrite
            // from http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/base/creating_and_opening_files.asp :
            // If you specify the GENERIC_READ and GENERIC_WRITE access modes along with the FILE_SHARE_READ and FILE_SHARE_WRITE sharing modes in your first call to CreateFile. If you specify the GENERIC_READ and GENERIC_WRITE access modes and the FILE_SHARE_READ sharing mode only in your second call to CreateFile, the function will fail with a sharing violation because the read-only sharing mode specified in the second call conflicts with the read/write access that has been granted in the first call.
            if (!f.Open(info.m_szFilePath, CZipFile::modeRead | CZipFile::shareDenyNone, false))
            {
                  if (pCallback)
                        pCallback->CallbackEnd();
                  return false;
            }
            pf = &f;
      }

      ASSERT(pf);
      // call init before opening (in case of exception we have the names)
      iFileSize = pf->GetLength();


      bool bRet;
      if (bReplace)
      {
            ASSERT(!bIsCompression);
            bRet = OpenNewFile(header, (info.m_iReplaceIndex << 16) | ZIP_COMPR_REPL_SIGN , NULL, iFileSize);
      }
      else
            bRet = OpenNewFile(header, info.m_iComprLevel);
      if (!bRet)
      {
            if (pCallback)
                  pCallback->CallbackEnd();

            return false;
      }
      if (bInternal)
      {
            // we do it here, because if in OpenNewFile is replacing
            // then we get called cbReplace callback before and it would
            // overwrite callback information written in pCallback->Init
            pCallback = GetCallback(cbAddStore);
            if (pCallback)
                  pCallback->Init(info.m_szFileNameInZip, info.m_szFilePath);
      }
      if (pCallback)
            pCallback->SetTotal(iFileSize);

      CZipAutoBuffer buf(info.m_nBufSize);
      DWORD iRead;
      int iAborted = 0;
      do
      {
            iRead = pf->Read(buf, info.m_nBufSize);
            if (iRead)
            {
                  WriteNewFile(buf, iRead);
                  if (pCallback)
                        if (!(*pCallback)(iRead))
                        {
                              // todo: we could remove here the bytes of the file partially added if not disk-spanning
                              if (iRead == buf.GetSize() && pf->Read(buf, 1) != 0) // test one byte if there is something left
                              {
                                    if (!m_storage.IsSpanMode() && !bReplace)
                                    {
                                          RemoveLast(true);
                                          CloseNewFile(true);
                                          iAborted = CZipException::abortedSafely;
                                    }
                                    else
                                          iAborted = CZipException::abortedAction;
                              }
                              else
                              {
                                    iAborted = CZipException::abortedSafely; // we did it!
                                    CloseNewFile();
                              }
                              break;
                        }

            }

      }
      while (iRead == buf.GetSize());
      if (!iAborted)
            CloseNewFile();

      if (pCallback)
            pCallback->CallbackEnd();

      if (iAborted)
            CZipException::Throw(iAborted); // throw to distuinguish from other return codes

      if (bEff)
      {
            // remove the last file and add it without the compression if needed
            if (!info.m_pFile)
                  f.Close();

            buf.Release();
            if (RemoveLast())
            {
                  info.m_iComprLevel = 0;
                  info.m_iSmartLevel = zipsmInternal01;
                  return AddNewFile(info);
            }
      }
      return true;

}

01787 bool CZipArchive::RemoveLast(bool bRemoveAnyway)
{
      int iIndex = GetCount() - 1;
      if (iIndex < 0)
            return false;
      CZipFileHeader* pHeader = m_centralDir[iIndex];

      if (!bRemoveAnyway && pHeader->CompressionEfficient())
            return false;

      m_centralDir.RemoveLastFile(pHeader, iIndex);
      return true;
}




01804 CZipString CZipArchive::GetArchivePath() const
{
      if (IsClosed(false))
      {
            TRACE(_T("%s(%i) : ZipArchive is closed.\n"),__FILE__,__LINE__);
            return _T("");
      }
      return m_storage.m_pFile->GetFilePath();
}

01814 CZipString CZipArchive::GetGlobalComment() const
{
      if (IsClosed())
      {
            TRACE(_T("%s(%i) : ZipArchive is closed.\n"),__FILE__,__LINE__);
            return _T("");
      }
      CZipString temp;
      return SingleToWide(m_centralDir.m_pszComment, temp) != -1 ? (LPCTSTR)temp : _T("");
}

01825 bool CZipArchive::SetGlobalComment(LPCTSTR lpszComment)
{
      if (IsClosed())
      {
            TRACE(_T("%s(%i) : ZipArchive is closed.\n"),__FILE__,__LINE__);
            return false;
      }
      if (m_storage.IsSpanMode() == -1)
      {
            TRACE(_T("%s(%i) : You cannot modify the global comment of the existing disk spanning archive.\n"),__FILE__,__LINE__);
            return false;
      }

      WideToSingle(lpszComment, m_centralDir.m_pszComment);
      m_centralDir.RemoveFromDisk();
      if (m_bAutoFlush)
            Flush();

      return true;
}



01848 int CZipArchive::GetCurrentDisk() const
{
      return m_storage.GetCurrentDisk() + 1;
}

01853 bool CZipArchive::SetFileComment(WORD uIndex, LPCTSTR lpszComment)
{
      if (IsClosed())
      {
            TRACE(_T("%s(%i) : ZipArchive is closed.\n"),__FILE__,__LINE__);
            return false;
      }
      if (m_storage.IsSpanMode() == -1)
      {
            TRACE(_T("%s(%i) : You cannot modify the file comment in the existing disk spanning archive.\n"),__FILE__,__LINE__);
            return false;
      }

      if (!m_centralDir.IsValidIndex(uIndex))
      {
            ASSERT(FALSE);
            return false;
      }
      m_centralDir[uIndex]->SetComment(lpszComment);
      m_centralDir.RemoveFromDisk();
      if (m_bAutoFlush)
            Flush();
      return true;
}


01879 void CZipArchive::CryptInitKeys()
{
      ASSERT(m_pszPassword.GetSize());
      m_keys[0] = 305419896L;
      m_keys[1] = 591751049L;
      m_keys[2] = 878082192L;
      for (DWORD i = 0; i < m_pszPassword.GetSize(); i++)
            CryptUpdateKeys(m_pszPassword[i]);
}

01889 void CZipArchive::CryptUpdateKeys(char c)
{

      m_keys[0] = CryptCRC32(m_keys[0], c);
      m_keys[1] += m_keys[0] & 0xff;
      m_keys[1] = m_keys[1] * 134775813L + 1;
      c = char(m_keys[1] >> 24);
      m_keys[2] = CryptCRC32(m_keys[2], c);
}

01899 bool CZipArchive::CryptCheck()
{
      CZipAutoBuffer buf(ZIPARCHIVE_ENCR_HEADER_LEN);
      m_storage.Read(buf, ZIPARCHIVE_ENCR_HEADER_LEN, false);
      BYTE b = 0;
      for (int i = 0; i < ZIPARCHIVE_ENCR_HEADER_LEN; i++)
      {
            b = buf[i]; // only temporary
            CryptDecode((char&)b);
      }
      // check the last byte
      return CurrentFile()->IsDataDescr() ?
            (BYTE(CurrentFile()->m_uModTime >> 8) == b) : (BYTE(CurrentFile()->m_uCrc32 >> 24) == b);
}

01914 char CZipArchive::CryptDecryptByte()
{
      int temp = (m_keys[2] & 0xffff) | 2;
      return (char)(((temp * (temp ^ 1)) >> 8) & 0xff);
}

01920 void CZipArchive::CryptDecode(char &c)
{
      c ^= CryptDecryptByte();
      CryptUpdateKeys(c);
}

01926 bool CZipArchive::SetPassword(LPCTSTR lpszPassword)
{
      if (m_iFileOpened != nothing)
      {
            TRACE(_T("%s(%i) : You cannot change the password when the file is opened.\n"),__FILE__,__LINE__);
            return false; // it's important not to change the password when the file inside archive is opened
      }
      if (IsClosed())
      {
            TRACE(_T("%s(%i) : Setting the password for a closed archive has no effect.\n"),__FILE__,__LINE__);
      }
      if (lpszPassword)
      {
            int iLen = WideToSingle(lpszPassword, m_pszPassword);
            if (iLen == -1)
                  return false;
            for (size_t i = 0; (int)i < iLen; i++)
                  if (m_pszPassword[i] <= 0)
                  {
                        m_pszPassword.Release();
                        TRACE(_T("%s(%i) : The password contains forbidden characters. Password cleared.\n"),__FILE__,__LINE__);
                        return false;
                  }
      }
      else
            m_pszPassword.Release();
      return true;
}

01955 CZipString CZipArchive::GetPassword()const
{
      CZipString temp;
      CZipArchive::SingleToWide(m_pszPassword, temp);
      return temp;
}

01962 DWORD CZipArchive::CryptCRC32(DWORD l, char c)
{
      const DWORD *CRC_TABLE = get_crc_table();
      return CRC_TABLE[(l ^ c) & 0xff] ^ (l >> 8);
}

01968 void CZipArchive::CryptCryptHeader(long iCrc, CZipAutoBuffer &buf)
{
      CryptInitKeys();
      srand(UINT(time(NULL)));
      // genereate pseudo-random sequence
      char c;
      for (int i = 0; i < ZIPARCHIVE_ENCR_HEADER_LEN - 2; i++)
      {
            int t1 = rand();
            c = (char)(t1 >> 6);
            if (!c)
                  c = (char)t1;
            CryptEncode(c);
            buf[i] = c;

      }
      c = (char)((iCrc >> 16) & 0xff);
      CryptEncode(c);
      buf[ZIPARCHIVE_ENCR_HEADER_LEN - 2] = c;
      c = (char)((iCrc >> 24) & 0xff);
      CryptEncode(c);
      buf[ZIPARCHIVE_ENCR_HEADER_LEN - 1] = c;
}

01992 void CZipArchive::CryptEncode(char &c)
{
      char t = CryptDecryptByte();
      CryptUpdateKeys(c);
      c ^= t;
}

01999 void CZipArchive::CryptEncodeBuffer()
{
            if (CurrentFile()->IsEncrypted())
            for (DWORD i = 0; i < m_info.m_uComprLeft; i++)
                  CryptEncode(m_info.m_pBuffer[i]);
}

02006 void CZipArchive::CloseFileAfterTestFailed()
{
      if (m_iFileOpened != extract)
      {
            TRACE(_T("%s(%i) : No file opened.\n"),__FILE__,__LINE__);
            return;
      }
      m_info.ReleaseBuf();
      m_centralDir.Clear(false);
      m_iFileOpened = nothing;
}

02018 bool CZipArchive::TestFile(WORD uIndex, DWORD uBufSize)
{
      if (m_storage.IsSpanMode() == 1)
      {
            TRACE(_T("%s(%i) : You cannot test the spanning archive in creation.\n"),__FILE__,__LINE__);
            return false;
      }
      if (!uBufSize)
            return false;

      CZipFileHeader* pHeader = m_centralDir[uIndex];
      CZipActionCallback* pCallback = GetCallback(cbTest);
      if (pCallback)
      {
            pCallback->Init(m_centralDir.GetProperHeaderFileName(pHeader));
      }

      if (pHeader->IsDirectory())
      {
            if (pCallback)
                  pCallback->SetTotal(0);

            // we do not test whether the password for the encrypted directory
            // is correct, since it seems to be senseless (anyway password
            // encrypted directories should be avoided - it adds 12 bytes)
            DWORD iSize = pHeader->m_uComprSize;
            if ((iSize != 0 || iSize != pHeader->m_uUncomprSize)
                  // different treating compressed directories
                  && !(pHeader->IsEncrypted() && iSize == 12 && !pHeader->m_uUncomprSize))
                  CZipException::Throw(CZipException::dirWithSize);

            if (pCallback)
                  pCallback->CallbackEnd();

            return true;
      }
      else
      {
            try
            {
                  if (pCallback)
                        pCallback->SetTotal(pHeader->m_uUncomprSize);

                  if (!OpenFile(uIndex))
                        return false;
                  CZipAutoBuffer buf(uBufSize);
                  DWORD iRead;
                  int iAborted = 0;
                  do
                  {
                        iRead = ReadFile(buf, buf.GetSize());
                        if (pCallback && iRead)
                              if (!(*pCallback)(iRead))
                              {
                                    if (iRead == buf.GetSize() && ReadFile(buf, 1) != 0) // test one byte if there is something left
                                          iAborted = CZipException::abortedAction;
                                    else
                                          iAborted = CZipException::abortedSafely; // we did it!
                                    break;
                              }
                  }
                  while (iRead == buf.GetSize());
                  bool bRet = CloseFile() != -1;
                  if (!bRet && iAborted == CZipException::abortedSafely)
                        iAborted = CZipException::abortedAction; // sorry, finished, but not successfull

                  if (pCallback)
                        pCallback->CallbackEnd();

                  if (iAborted)
                        CZipException::Throw(iAborted); // throw to distuingiush from other return codes
                  if (bRet)
                        return true;
                  else
                        CZipException::Throw(CZipException::badZipFile);
                  return false; // to satisfy the compiler and eliminate warning
            }
            catch(...)
            {
                  CloseFileAfterTestFailed();
                  throw;
            }
      }
}

02103 int CZipArchive::WideToSingle(LPCTSTR lpWide, CZipAutoBuffer &szSingle)
{
#ifdef _UNICODE
      return ZipPlatform::WideToSingle(lpWide, szSingle);
#else

      size_t iLen = strlen(lpWide);
      // if not UNICODE just copy
      //    iLen does not include the NULL character
      szSingle.Allocate(iLen);
      memcpy(szSingle, lpWide, iLen);
      return iLen;
#endif

}

02119 int CZipArchive::SingleToWide(const CZipAutoBuffer &szSingle, CZipString& szWide)
{

#ifdef _UNICODE
      return ZipPlatform::SingleToWide(szSingle, szWide);
#else // if not UNICODE just copy
      int singleLen = szSingle.GetSize();
      //    iLen does not include the NULL character
      memcpy(szWide.GetBuffer(singleLen),szSingle.GetBuffer(), singleLen);
      szWide.ReleaseBuffer(singleLen);
      return singleLen;
#endif
}

02133 void CZipArchive::CryptDecodeBuffer(DWORD uCount)
{
      if (CurrentFile()->IsEncrypted())
            for (DWORD i = 0; i < uCount; i++)
                  CryptDecode(m_info.m_pBuffer[i]);
}

02140 void CZipArchive::EmptyPtrList()
{
      if (m_list.GetCount())
      {
            // if some memory hasn't been freed due to an error in zlib, so free it now
            CZipPtrListIter iter = m_list.GetHeadPosition();
            while (m_list.IteratorValid(iter))
                  delete[] (char*) m_list.GetNext(iter);
      }
      m_list.RemoveAll();
}



02154 void CZipArchive::SetFileHeaderAttr(CZipFileHeader& header, DWORD uAttr)
{
      header.SetSystemCompatibility(m_iArchiveSystCompatib);
      header.SetSystemAttr(uAttr);
}

02160 void CZipArchive::EnableFindFast(bool bEnable)
{
      if (IsClosed())
      {
            TRACE(_T("%s(%i) : Set it after opening the archive.\n"),__FILE__,__LINE__);
            return;
      }
      m_centralDir.EnableFindFast(bEnable, m_bCaseSensitive);

}

02171 bool CZipArchive::SetSystemCompatibility(int iSystemComp)
{
      if (IsClosed())
      {
            TRACE(_T("%s(%i) : Set it after opening the archive.\n"),__FILE__,__LINE__);
            return false;
      }

      if (m_iFileOpened == compress)
      {
            TRACE(_T("%s(%i) : Set it before opening a file inside archive.\n"),__FILE__,__LINE__);
            return false;
      }

      if (!ZipCompatibility::IsPlatformSupported(iSystemComp))
            return false;
      m_iArchiveSystCompatib = iSystemComp;
      return true;
}

02191 void CZipArchive::SetRootPath(LPCTSTR szPath)
{
      if (IsClosed())
      {
            TRACE(_T("%s(%i) : Set it after opening the archive.\n"),__FILE__,__LINE__);
            return;
      }

      if (m_iFileOpened != nothing)
      {
            TRACE(_T("%s(%i) : Set it before opening a file inside archive.\n"),__FILE__,__LINE__);
            return;
      }

      if (szPath)
      {
            m_szRootPath = szPath;
            CZipPathComponent::RemoveSeparators(m_szRootPath);
      }
      else
            m_szRootPath.Empty();
}

02214 CZipString CZipArchive::TrimRootPath(CZipPathComponent &zpc)const
{
      if (m_szRootPath.IsEmpty())
            return zpc.GetFileName();
      CZipString szPath = zpc.GetFullPath();
      return RemovePathBeginning(m_szRootPath, szPath, m_pZipCompare) ? szPath : zpc.GetFileName();
}

02222 bool CZipArchive::RemovePathBeginning(LPCTSTR lpszBeginning, CZipString& szPath, ZIPSTRINGCOMPARE pCompareFunction)
{
      CZipString szBeginning(lpszBeginning);
      CZipPathComponent::RemoveSeparators(szBeginning);
      int iRootPathLength = szBeginning.GetLength();
      if (iRootPathLength && szPath.GetLength() >= iRootPathLength &&
            (szPath.Left(iRootPathLength).*pCompareFunction)(szBeginning) == 0)
      {
            // the beginning is the same
            if (szPath.GetLength() == iRootPathLength)
            {
                  szPath.Empty();
                  return true;
            }
            // is the end of m_szPathRoot only a beginning of a directory name?
            // check for a separator
            // we know the length is larger, so we can write:
            if (CZipPathComponent::IsSeparator(szPath[iRootPathLength]))
            {
                  szPath = szPath.Mid(iRootPathLength);
                  CZipPathComponent::RemoveSeparatorsLeft(szPath);
                  return true;
            }
      }
      return false;
}

02249 void CZipArchive::SetTempPath(LPCTSTR lpszPath, bool bForce)
{
      m_szTempPath = lpszPath;
      if (lpszPath && bForce)
            ZipPlatform::ForceDirectory(lpszPath);
      CZipPathComponent::RemoveSeparators(m_szTempPath);
}

02257 CZipString CZipArchive::PredictFileNameInZip(LPCTSTR lpszFilePath,
                                     bool bFullPath, int iWhat, bool bExactly)const
{
      CZipString sz = lpszFilePath;
      if (sz.IsEmpty())
            return _T("");
      bool bAppend;
      switch (iWhat)
      {
      case prFile:
            bAppend = false;
            break;
      case prDir:
            bAppend = true;
            break;
      default:
            bAppend = CZipPathComponent::IsSeparator(sz[sz.GetLength() - 1]);
      }

      // remove for CZipPathComponent treating last name as a file even if dir
      CZipPathComponent::RemoveSeparators(sz);
      CZipPathComponent zpc(sz);

      if (bFullPath)
      {
            if (m_bRemoveDriveLetter)
                  sz = zpc.GetNoDrive();
      }
      else
            sz = TrimRootPath(zpc);

      if (bAppend && !sz.IsEmpty())
            CZipPathComponent::AppendSeparator(sz);
      CZipFileHeader fh; // create a temporary object to convert
      fh.SetFileName(sz);
      if (bExactly)
      {
            fh.SetSystemCompatibility(m_iArchiveSystCompatib);
            ZipCompatibility::FileNameUpdate(fh, false);
      }
      else
      {
            fh.SetSystemCompatibility(-1); // non existing system to prevent ansi oem conversion
            ZipCompatibility::FileNameUpdate(fh, true);// update only path separators
      }

      return fh.GetFileName();
}

02306 CZipString CZipArchive::PredictExtractedFileName(LPCTSTR lpszFileNameInZip, LPCTSTR lpszPath, bool bFullPath, LPCTSTR lpszNewName)const
{
      CZipString szFile = lpszPath;
      CZipString sz = lpszNewName ? lpszNewName : lpszFileNameInZip;
      if (sz.IsEmpty())
            return szFile;
      if (!szFile.IsEmpty())
            CZipPathComponent::AppendSeparator(szFile);


      // remove for CZipPathComponent treating last name as a file even if dir
      CZipPathComponent::RemoveSeparators(sz);
      CZipPathComponent zpc(sz);
      szFile += bFullPath ? (m_bRemoveDriveLetter ? zpc.GetNoDrive() : sz)
                                      : TrimRootPath(zpc);
      return szFile;
}


02325 void CZipArchive::SetAutoFlush(bool bAutoFlush)
{
      if (IsClosed())
      {
            TRACE(_T("%s(%i) : ZipArchive not yet opened.\n"),__FILE__,__LINE__);
            return;
      }
      if (m_storage.IsSpanMode() != 0)
      {
            TRACE(_T("%s(%i) : Cannot set auto-flush for the disk spanning archive.\n"),__FILE__,__LINE__);
            return;
      }
      m_bAutoFlush = bAutoFlush;
}

02340 void CZipArchive::Flush()
{
      if (IsClosed())
      {
            TRACE(_T("%s(%i) : ZipArchive not yet opened.\n"),__FILE__,__LINE__);
            return;
      }

      if (m_storage.IsSpanMode() < 0)
      {
            TRACE(_T("%s(%i) : Cannot flush an existing disk spanning archive.\n"),__FILE__,__LINE__);
            return;
      }
      WriteCentralDirectory();
      m_storage.FlushFile();
      if (m_storage.IsSpanMode() > 0) // try to finalize disk-spanning archive without closing it
            m_storage.FinalizeSpan();
}


02360 void CZipArchive::GetCentralDirInfo(CZipCentralDir::Info& info)const
{
      if (IsClosed())
      {
            TRACE(_T("%s(%i) : ZipArchive not yet opened.\n"),__FILE__,__LINE__);
            return;

      }
      m_centralDir.GetInfo(info);
      if (GetSpanMode() > 0)
            info.m_uDiskEntriesNo = m_storage.GetCurrentDisk();
}

02373 bool CZipArchive::CWildcard::IsPatternValid(LPCTSTR lpszPattern, int* iErrorType)
{
      try
      {
            /* loop through pattern to EOS */
            while (*lpszPattern)
            {
                  /* determine pattern type */
                  switch (*lpszPattern)
                  {
                        /* check literal escape, it cannot be at end of pattern */
                  case _T('\\'):
                        if (!*++lpszPattern)
                              throw patternEsc;
                        lpszPattern++;
                        break;

                        /* the [..] construct must be well formed */
                  case _T('['):
                        lpszPattern++;

                        /* if the next character is ']' then bad pattern */
                        if (*lpszPattern == _T(']'))
                              throw patternEmpty;

                        /* if end of pattern here then bad pattern */
                        if (!*lpszPattern)
                              throw patternClose;

                        /* loop to end of [..] construct */
                        while (*lpszPattern != _T(']'))
                        {
                              /* check for literal escape */
                              if (*lpszPattern == _T('\\'))
                              {
                                    lpszPattern++;

                                    /* if end of pattern here then bad pattern */
                                    if (!*lpszPattern++)
                                          throw patternEsc;
                              }
                              else  lpszPattern++;

                              /* if end of pattern here then bad pattern */
                              if (!*lpszPattern)
                                    throw patternClose;

                              /* if this a range */
                              if (*lpszPattern == _T('-'))
                              {
                                    /* we must have an end of range */
                                    if (!*++lpszPattern || *lpszPattern == ']')
                                          throw patternRange;
                                    else
                                    {

                                          /* check for literal escape */
                                          if (*lpszPattern == _T('\\'))
                                                lpszPattern++;

                                                /* if end of pattern here
                                          then bad pattern           */
                                          if (!*lpszPattern++)
                                                throw patternEsc;
                                    }
                              }
                        }
                        break;

                        /* all other characters are valid pattern elements */
                  case '*':
                  case '?':
                  default:
                        lpszPattern++;                              /* "normal" character */
                        break;
                  }
            }
            throw patternValid;
      }
      catch (int i)
      {
            if (iErrorType)
                  *iErrorType = i;
            return i == patternValid;
      }


}

02462 bool CZipArchive::CWildcard::IsPattern(LPCTSTR lpszPattern)
{
      while (*lpszPattern)
      {
        switch (*lpszPattern++)
        {
        case _T('?'):
        case _T('*'):
        case _T('['):
        case _T('\\'):
                  return true;
        }
      }
      return false;

}

02479 bool CZipArchive::CWildcard::IsMatch(LPCTSTR lpszText, int *iRetCode)
{
      CZipString sz;
      if (!m_bCaseSensitive)
      {
            sz = lpszText;
            sz.MakeLower();
            lpszText = (LPCTSTR)sz;
      }
      int i = Match((LPCTSTR)m_szPattern, lpszText);
      if (iRetCode)
            *iRetCode = i;
      return i == matchValid;
}

int CZipArchive::CWildcard::MatchAfterStar(LPCTSTR p, LPCTSTR t)
{
      int iMatch = matchNone;
      TCHAR nextp;

      /* pass over existing ? and * in pattern */

      while ( *p == _T('?') || *p == _T('*') )
      {
            /* take one char for each ? and + */

            if (*p == _T('?'))
            {
                  /* if end of text then no match */
                  if (!*t++)
                        return matchAbort;
            }

            /* move to next char in pattern */

            p++;
      }

      /* if end of pattern we have matched regardless of text left */

      if (!*p)
            return matchValid;

      /* get the next character to match which must be a literal or '[' */

      nextp = *p;
      if (nextp == _T('\\'))
      {
            nextp = p[1];

            /* if end of text then we have a bad pattern */

            if (!nextp)
                  return matchPattern;
      }

      /* Continue until we run out of text or definite result seen */

      do
      {
            /* a precondition for matching is that the next character
               in the pattern match the next character in the text or that
               the next pattern char is the beginning of a range.  Increment
               text pointer as we go here */

            if (nextp == *t || nextp == _T('['))
                  iMatch = Match(p, t);

            /* if the end of text is reached then no iMatch */

            if (!*t++)
                  iMatch = matchAbort;

      } while ( iMatch != matchValid &&
                iMatch != matchAbort &&
                iMatch != matchPattern);

      /* return result */

      return iMatch;
}


02562 int CZipArchive::CWildcard::Match(LPCTSTR lpszPattern, LPCTSTR lpszText)
{

      TCHAR range_start, range_end;  /* start and end in range */

      bool bInvert;             /* is this [..] or [!..] */
      bool bMemberMatch;       /* have I matched the [..] construct? */
      bool bLoop;               /* should I terminate? */

      for ( ; *lpszPattern; lpszPattern++, lpszText++)
      {
      /* if this is the end of the text
            then this is the end of the match */

            if (!*lpszText)
            {
                  if ( *lpszPattern == _T('*') && *++lpszPattern == _T('\0') )
                        return matchValid;
                  else
                        return matchAbort;
            }

            /* determine and react to pattern type */

            switch (*lpszPattern)
            {
            case _T('?'):                     /* single any character match */
                  break;

            case _T('*'):                     /* multiple any character match */
                  return MatchAfterStar (lpszPattern, lpszText);

                  /* [..] construct, single member/exclusion character match */
            case _T('['):
                  {
                        /* move to beginning of range */

                        lpszPattern++;

                        /* check if this is a member match or exclusion match */

                        bInvert = false;
                        if (*lpszPattern == _T('!') || *lpszPattern == _T('^'))
                        {
                              bInvert = true;
                              lpszPattern++;
                        }

                        /* if closing bracket here or at range start then we have a
                        malformed pattern */

                        if (*lpszPattern == _T(']'))
                              return matchPattern;

                        bMemberMatch = false;
                        bLoop = true;

                        while (bLoop)
                        {
                              /* if end of construct then bLoop is done */

                              if (*lpszPattern == _T(']'))
                              {
                                    bLoop = false;
                                    continue;
                              }

                              /* matching a '!', '^', '-', '\' or a ']' */

                              if (*lpszPattern == _T('\\'))
                                    range_start = range_end = *++lpszPattern;
                              else
                                    range_start = range_end = *lpszPattern;

                              /* if end of pattern then bad pattern (Missing ']') */

                              if (!*lpszPattern)
                                    return matchPattern;

                              /* check for range bar */
                              if (*++lpszPattern == _T('-'))
                              {
                                    /* get the range end */

                                    range_end = *++lpszPattern;

                                    /* if end of pattern or construct
                                    then bad pattern */

                                    if (range_end == _T('\0') || range_end == _T(']'))
                                          return matchPattern;
                                    /* special character range end */
                                    if (range_end == _T('\\'))
                                    {
                                          range_end = *++lpszPattern;

                                          /* if end of text then
                                          we have a bad pattern */
                                          if (!range_end)
                                                return matchPattern;
                                    }

                                    /* move just beyond this range */
                                    lpszPattern++;
                              }

                              /* if the text character is in range then match found.
                              make sure the range letters have the proper
                              relationship to one another before comparison */

                              if (range_start < range_end)
                              {
                                    if (*lpszText >= range_start && *lpszText <= range_end)
                                    {
                                          bMemberMatch = true;
                                          bLoop = false;
                                    }
                              }
                              else
                              {
                                    if (*lpszText >= range_end && *lpszText <= range_start)
                                    {
                                          bMemberMatch = true;
                                          bLoop = false;
                                    }
                              }
                        }

                        /* if there was a match in an exclusion set then no match */
                        /* if there was no match in a member set then no match */

                        if ((bInvert && bMemberMatch) || !(bInvert || bMemberMatch))
                              return matchRange;

                        /* if this is not an exclusion then skip the rest of
                        the [...] construct that already matched. */

                        if (bMemberMatch)
                        {
                              while (*lpszPattern != _T(']'))
                              {
                                    /* bad pattern (Missing ']') */
                                    if (!*lpszPattern)
                                          return matchPattern;

                                    /* skip exact match */
                                    if (*lpszPattern == _T('\\'))
                                    {
                                          lpszPattern++;

                                          /* if end of text then
                                          we have a bad pattern */

                                          if (!*lpszPattern)
                                                return matchPattern;
                                    }

                                    /* move to next pattern char */

                                    lpszPattern++;
                              }
                        }
                        break;
                  }
                  case _T('\\'):  /* next character is quoted and must match exactly */

                        /* move pattern pointer to quoted char and fall through */

                        lpszPattern++;

                        /* if end of text then we have a bad pattern */

                        if (!*lpszPattern)
                              return matchPattern;

                        /* must match this character exactly */

                  default:
                        if (*lpszPattern != *lpszText)
                              return matchPattern;
                  }
        }
        /* if end of text not reached then the pattern fails */

            if (*lpszText)
                  return matchEnd;
            else
                  return matchValid;
}

02752 void CZipArchive::FindMatches(LPCTSTR lpszPattern, CZipWordArray &ar, bool bFullPath) const
{
      if (IsClosed())
      {
            TRACE(_T("%s(%i) : ZipArchive is closed.\n"),__FILE__,__LINE__);
            return;
      }

      //    ar.RemoveAll(); don't do this
      int iCount = GetCount();
      CWildcard wc(lpszPattern, m_bCaseSensitive);
      for (int i = 0; i < iCount; i++)
      {
            const CZipFileHeader* pHeader = m_centralDir[i];
            CZipString sz =  m_centralDir.GetProperHeaderFileName(pHeader);
            if (!bFullPath)
            {
                  CZipPathComponent::RemoveSeparators(sz);
                  CZipPathComponent zpc(sz);
                  sz = zpc.GetFileName();
            }
            if (wc.IsMatch(sz))
                  ar.Add(i);
      }
}

02778 int CZipArchive::WillBeDuplicated(LPCTSTR lpszFilePath, bool bFullPath, bool bFileNameOnly , int iWhat)
{
      CZipString szFile;
      // we predict with bExactly set to false, because FindFile converts all filanames anyway
      if (bFileNameOnly)
      {
            CZipPathComponent zpc(lpszFilePath);
            szFile = PredictFileNameInZip(zpc.GetFileName(), false, iWhat);
      }
      else
            szFile = PredictFileNameInZip(lpszFilePath, bFullPath, iWhat);
      return FindFile(szFile, ffDefault, bFileNameOnly);
}


// it'll get up to the next file or to the end of file (bad if zip corrupted or not-ordered by offsett or redundant bytes added)
02794 bool CZipArchive::GetFromArchive(CZipArchive& zip, WORD uIndex, int iReplaceIndex, bool bKeepSystComp, CZipActionCallback* pCallback)
{

      if (IsClosed() || zip.IsClosed())
      {
            TRACE(_T("%s(%i) : ZipArchive is closed.\n"),__FILE__,__LINE__);
            return false;
      }

      if (m_iFileOpened || zip.m_iFileOpened)
      {
            TRACE(_T("%s(%i) : You cannot get files from another archive if there is a file opened.\n"),__FILE__,__LINE__);
            return false;
      }

      if (zip.m_storage.IsSpanMode())
      {
            TRACE(_T("%s(%i) : You cannot get files from the disk spannig archive.\n"),__FILE__,__LINE__);
            return false;
      }

      if (m_storage.IsSpanMode() == -1)
      {
            TRACE(_T("%s(%i) : You cannot add files to the existing disk spannig archive.\n"),__FILE__,__LINE__);
            return false;
      }

      ASSERT(m_info.m_pBuffer.GetSize() > 0);

      bool bIsSpan = m_storage.IsSpanMode() == 1;

      CZipFileHeader fh;
      if (!zip.GetFileInfo(fh, uIndex))
            return false;
      CZipAbstractFile* pFile = zip.m_storage.m_pFile;


      DWORD uEndOffset;
      if (uIndex < zip.GetCount() - 1)
      {
            CZipFileHeader fhTemp;
            if (!zip.GetFileInfo(fhTemp, uIndex+1))
                  return false;
            uEndOffset = fhTemp.m_uOffset;
      }
      else
      {
            CZipCentralDir::Info info;
            zip.m_centralDir.GetInfo(info);
            if (info.m_bOnDisk)
                  uEndOffset = info.m_uOffset;
            else
                  uEndOffset = pFile->GetLength();
      }
      uEndOffset += zip.m_centralDir.GetBytesBefore();
      DWORD uStartOffset = zip.m_centralDir.GetBytesBefore() + fh.m_uOffset + fh.GetSize(true);
      DWORD uTotalToMove = uEndOffset - uStartOffset, uTotalMoved = 0;

      DWORD uPredictedSize = fh.m_uComprSize +
            (fh.IsDataDescr() ? ZIPARCHIVE_DATADESCRIPTOR_LEN : 0);
      if (uTotalToMove > uPredictedSize + 4/* may be or may be not a signature*/)
            uTotalToMove = uPredictedSize + 4;
      else if (uTotalToMove < uPredictedSize)
            ThrowError(CZipException::badZipFile);

      // conversion stuff
      CZipString szFileNameConverted, szFileName;
      bool bConvertSystem = !bKeepSystComp && fh.GetSystemCompatibility() != m_iArchiveSystCompatib;

      // GetFileInfo always converts the filename regardless of zip.m_centralDir.m_bConvertAfterOpen value
      szFileNameConverted = fh.GetFileName();
      if (bConvertSystem)
      {
            DWORD uAttr = fh.GetSystemAttr();
            fh.SetSystemCompatibility(m_iArchiveSystCompatib);
            fh.SetSystemAttr(uAttr);
      }

      ZipCompatibility::FileNameUpdate(fh, false);
      szFileName = fh.GetFileName();


      bool bNeedDataDescr = bIsSpan && !fh.IsDataDescr();
      if (bNeedDataDescr)
            fh.m_uFlag |= 8; // data descriptor present


      // needed by InsertFindFastElement
      if (m_centralDir.IsFindFastEnabled())
            fh.SetFileName(szFileNameConverted);


      if (!UpdateReplaceIndex(iReplaceIndex, szFileNameConverted))
            return false;

      bool bReplace = iReplaceIndex >= 0;
      int iCallbackType = 0;
      if (pCallback)
            iCallbackType = pCallback->m_iType;

      // if the same callback is applied to cbReplace, then the previous information about the type will be lost
      CZipFileHeader* pHeader = m_centralDir.AddNewFile(fh, iReplaceIndex); // must be converted when adding because of InsertFastElement
      if (bReplace)
            MakeSpaceForReplace(iReplaceIndex, uTotalToMove + fh.GetSize(true), szFileNameConverted);

      if (pCallback)
      {
            pCallback->m_iType = iCallbackType;
            pCallback->Init(szFileNameConverted, zip.GetArchivePath());
            pCallback->SetTotal(fh.m_uComprSize);
      }

      if (m_centralDir.IsFindFastEnabled())
            pHeader->SetFileName(szFileName);

      // must be written as not converted
      pHeader->WriteLocal(m_storage);

      // made a correction to what was set in WriteLocal
      pHeader->m_uOffset -= m_centralDir.GetBytesBefore();

      // we keep in converted in memory
      if (m_centralDir.m_bConvertAfterOpen)
             pHeader->SetFileName(szFileNameConverted);


      // skip reading the local file header

      pFile->Seek(uStartOffset, CZipAbstractFile::begin);


      DWORD uPack = uTotalToMove > m_info.m_pBuffer.GetSize() ? m_info.m_pBuffer.GetSize() : uTotalToMove;
      char* buf = (char*)m_info.m_pBuffer;

      DWORD size_read;

      int iAborted = 0;
      bool bBreak = false;
      if (uPack)
            do
            {
                  size_read = pFile->Read(buf, uPack);
                  if (!size_read)
                        break;
                  if (uTotalMoved + size_read > uTotalToMove)
                  {
                        size_read = uTotalToMove - uTotalMoved;
                        if (!size_read)  // this is for protection
                              break;
                        bBreak = true;
                  }

                  m_storage.Write(buf, size_read, false);
                  uTotalMoved += size_read;
                  if (pCallback)
                        if (!(*pCallback)(size_read))
                        {
                              if (uTotalToMove != uTotalMoved)
                              {
                                    if (!bIsSpan && !bReplace)
                                    {
                                          m_centralDir.RemoveLastFile();
                                          iAborted = CZipException::abortedSafely;
                                    }
                                    else
                                          iAborted = CZipException::abortedAction;
                              }
                              else
                                    iAborted = CZipException::abortedSafely; // we did it!
                              break;

                        }
            }
            while (!bBreak);

      if (iAborted)
            CZipException::Throw(iAborted); // throw to distuingiush from other return codes

      // copying from non-span to span or from span without data description to span
      // so add the data descriptor

      m_centralDir.m_pOpenedFile = NULL;
      if (bNeedDataDescr && uTotalMoved == uTotalToMove)
      {
            const int iToWrite = ZIPARCHIVE_DATADESCRIPTOR_LEN + 4;
            CZipAutoBuffer buf(iToWrite);
            memcpy(buf, m_storage.m_gszExtHeaderSignat, 4);
            pHeader->GetCrcAndSizes(buf + 4);
            m_storage.Write(buf, iToWrite, true);

      }
      m_storage.Flush();
      if (uTotalMoved < uTotalToMove)
            ThrowError(CZipException::badZipFile);


      if (pCallback)
            pCallback->CallbackEnd();

      return true;
}

02996 bool CZipArchive::GetFromArchive(CZipArchive& zip, CZipWordArray &aIndexes, bool bKeepSystComp)
{
      aIndexes.Sort(true);
      int iFiles = aIndexes.GetSize();
      m_info.Init();
      try
      {
            for (int i = 0; i < iFiles; i++)
            {
                  int iFileIndex = aIndexes[i];
                  if (!m_centralDir.IsValidIndex(iFileIndex))
                  if (!GetFromArchive(zip, iFileIndex, -1, bKeepSystComp, GetCallback(cbGetFromArchive)))
                  {
                        m_info.ReleaseBuf();
                        return false;
                  }
            }
      }
      catch (...)
      {
            m_info.ReleaseBuf();
            throw;
      }
      m_info.ReleaseBuf();
      if (m_bAutoFlush)
            Flush();
      return true;
}
03024 bool CZipArchive::RenameFile(WORD uIndex, LPCTSTR lpszNewName)
{
      if (IsClosed())
      {
            TRACE(_T("%s(%i) : ZipArchive is closed.\n"),__FILE__,__LINE__);
            return false;
      }

      if (m_storage.IsSpanMode())
      {
            TRACE(_T("%s(%i) : You cannot rename files in the disk spannig archive.\n"),__FILE__,__LINE__);
            return false;
      }

      if (m_iFileOpened)
      {
            TRACE(_T("%s(%i) : You cannot rename a file if there is a file opened.\n"),__FILE__,__LINE__);
            return false;
      }
      CZipFileHeader fh, fhNew;
      if (!GetFileInfo(fh, uIndex))
            return false;
      CZipString szNewName(lpszNewName);
      if (fh.IsDirectory())
            CZipPathComponent::AppendSeparator(szNewName);
      else
            CZipPathComponent::RemoveSeparators(szNewName);
      if (fh.GetFileName().Collate(szNewName) == 0)
            return true;
      fhNew.SetSystemCompatibility(m_iArchiveSystCompatib);

      fhNew.SetFileName(szNewName);
      ZipCompatibility::FileNameUpdate(fhNew, false);
      ZipCompatibility::FileNameUpdate(fh, false); // in case the conversion changes the filename size
      WORD uFileNameLen = fh.GetFileNameSize();
      WORD uNewFileNameLen = fhNew.GetFileNameSize();
      int iDelta = uNewFileNameLen - uFileNameLen;
      int iOffset = 0;
      CZipAutoBuffer buf, *pBuf;
      m_centralDir.RemoveFromDisk(); // does m_storage.Flush();
      if (iDelta != 0)
      {
            // we need to make more or less space

            m_info.Init();
            DWORD uStartOffset = fh.m_uOffset + 30 + uFileNameLen;
            DWORD uFileLen = m_storage.m_pFile->GetLength();
            DWORD uEndOffset = uFileLen - m_centralDir.GetBytesBefore();
            CZipActionCallback* pCallback = GetCallback(cbRename);
            if (pCallback)
            {
                  // do it right and sent the notification
                  pCallback->Init(fh.GetFileName(), GetArchivePath());
                  pCallback->SetTotal(uEndOffset - uStartOffset);
            }
            bool bForward = iDelta > 0;
            if (bForward)
                  m_storage.m_pFile->SetLength(uFileLen + iDelta); // ensure the seek is correct

            MovePackedFiles(uStartOffset, uEndOffset, abs(iDelta), pCallback, bForward);
            if (pCallback)
                  pCallback->CallbackEnd();

            if (!bForward)
                  m_storage.m_pFile->SetLength(uFileLen + iDelta); // delta < 0; shrink the file

            m_info.ReleaseBuf();

            int iSize = GetCount();
            for (int i = uIndex + 1; i < iSize; i++)
                  m_centralDir[i]->m_uOffset += iDelta;
            buf.Allocate(4+uNewFileNameLen);
            WORD uExtraFieldSize = fh.GetExtraFieldSize();
            memcpy(buf, &uNewFileNameLen, 2);
            memcpy(buf + 2, &uExtraFieldSize, 2); // to write everything at once
            memcpy(buf + 4, fhNew.m_pszFileName, uNewFileNameLen);
            pBuf = &buf;
            iOffset = -4;
      }
      else
            pBuf = &fhNew.m_pszFileName;

      m_storage.m_pFile->Seek(m_centralDir.GetBytesBefore() + fh.m_uOffset + 30 + iOffset, CZipAbstractFile::begin);
      m_storage.m_pFile->Write(buf, buf.GetSize());
      m_centralDir.RenameFile(uIndex, szNewName);
      if (m_bAutoFlush)
            Flush();

      return true;
}

03115 bool CZipArchive::UpdateReplaceIndex(int& iReplaceIndex, LPCTSTR lpszNewFileName)
{
      if (iReplaceIndex == -2)
            iReplaceIndex = FindFile(lpszNewFileName);
      if (iReplaceIndex < 0)
      {
            if (iReplaceIndex != -1)
                  iReplaceIndex = -1;
            return true;
      }

      if (GetSpanMode()!=0)
      {
            TRACE(_T("%s(%i) : You cannot replace files in a disk-spanning archive.\n"),__FILE__,__LINE__);
            return false;
      }

      if (!m_centralDir.IsValidIndex(iReplaceIndex))
      {
            TRACE(_T("%s(%i) : Not valid replace index.\n"),__FILE__,__LINE__);
            return false;
      }
      if (iReplaceIndex == GetCount() - 1) // replacing last file in the archive
      {
            RemoveLast(true);
            iReplaceIndex = -1;
      }
      return true;
}

03145 void CZipArchive::MakeSpaceForReplace(int iReplaceIndex, DWORD uTotal, LPCTSTR lpszFileName)
{

      ASSERT(iReplaceIndex < GetCount() - 1);
      DWORD uReplaceStart = m_storage.m_pFile->GetPosition() - m_centralDir.GetBytesBefore();
      DWORD uReplaceEnd = m_centralDir.m_headers[iReplaceIndex + 1]->m_uOffset;
      DWORD uReplaceTotal = uReplaceEnd - uReplaceStart;
      int iDelta = uTotal - uReplaceTotal;

      if (iDelta != 0)
      {

            //    m_info.Init(); don't - the calling functions will
            CZipActionCallback* pCallback = GetCallback(CZipArchive::cbReplace);
            DWORD uFileLen = m_storage.m_pFile->GetLength();
            DWORD uUpperLimit = uFileLen - m_centralDir.GetBytesBefore(); // will be added in MovePackedFiles
            if (pCallback)
            {
                  pCallback->Init(lpszFileName, GetArchivePath());
                  pCallback->SetTotal(uUpperLimit - uReplaceEnd);
            }

            bool bForward = iDelta > 0;
            if (bForward)
                  m_storage.m_pFile->SetLength(uFileLen + iDelta); // ensure the seek is correct

            MovePackedFiles(uReplaceEnd, uUpperLimit, abs(iDelta), pCallback, bForward);

            if (!bForward)
                  m_storage.m_pFile->SetLength(uFileLen + iDelta); // delta < 0; shrink the file

            m_storage.m_pFile->Seek(uReplaceStart, CZipAbstractFile::begin);
            int iSize = m_centralDir.m_headers.GetSize();
            for (int i = iReplaceIndex + 1; i < iSize; i++)
                  m_centralDir.m_headers[i]->m_uOffset += iDelta;
            if (pCallback)
                  pCallback->CallbackEnd();
      }
}

03185 void CZipArchive::MovePackedFiles(DWORD uStartOffset, DWORD uEndOffset, DWORD uMoveBy, CZipActionCallback* pCallback, bool bForward)
{
      ASSERT(m_info.m_pBuffer.GetSize() > 0);
      uStartOffset += m_centralDir.GetBytesBefore();
      uEndOffset += m_centralDir.GetBytesBefore();


      DWORD uTotalToMove = uEndOffset - uStartOffset;
      DWORD uPack = uTotalToMove > m_info.m_pBuffer.GetSize() ? m_info.m_pBuffer.GetSize() : uTotalToMove;
      char* buf = (char*)m_info.m_pBuffer;


      DWORD size_read;
      bool bBreak = false;
      do
      {

            if (uEndOffset - uStartOffset < uPack)
            {
                  uPack = uEndOffset - uStartOffset;
                  if (!uPack)
                        break;
                  bBreak = true;

            }
            DWORD uPosition = bForward ? uEndOffset - uPack : uStartOffset;

            m_storage.m_pFile->Seek(uPosition, CZipAbstractFile::begin);
            size_read = m_storage.m_pFile->Read(buf, uPack);
            if (!size_read)
                  break;

            if (bForward)
                  uPosition += uMoveBy;
            else
                  uPosition -= uMoveBy;
            m_storage.m_pFile->Seek(uPosition, CZipAbstractFile::begin);
            m_storage.m_pFile->Write(buf, size_read);
            if (bForward)
                  uEndOffset -= size_read;
            else
                  uStartOffset += size_read;
            if (pCallback)
                  if (!(*pCallback)(size_read))
                        ThrowError(CZipException::abortedAction);
      }
      while (!bBreak);

      if (uEndOffset != uStartOffset)
            ThrowError(CZipException::internal);

}

Generated by  Doxygen 1.6.0   Back to index