787 lines
41 KiB
C++
787 lines
41 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
||
// This source file is part of the ZipArchive library source distribution and
|
||
// is Copyrighted 2000 - 2012 by Artpol Software - Tadeusz Dracz
|
||
//
|
||
// 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 refer to the License.txt file.
|
||
//
|
||
// Web Site: http://www.artpol-software.com
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
#include "stdafx.h"
|
||
#include "ZipStorage.h"
|
||
#include "ZipArchive.h"
|
||
#include "ZipPlatform.h"
|
||
|
||
char CZipStorage::m_gszExtHeaderSignat[] = {0x50, 0x4b, 0x07, 0x08};
|
||
const ZIP_FILE_USIZE CZipStorage::SignatureNotFound = ZIP_FILE_USIZE(-1);
|
||
#define SIGNATURE_SIZE 4
|
||
|
||
using namespace ZipArchiveLib;
|
||
|
||
|
||
CZipStorage::CZipStorage()
|
||
{
|
||
Initialize();
|
||
}
|
||
|
||
void CZipStorage::Initialize()
|
||
{
|
||
m_pSplitChangeVolumeFunc = m_pSpanChangeVolumeFunc = m_pChangeVolumeFunc = NULL;
|
||
m_iWriteBufferSize = 65536;
|
||
m_pFile = NULL;
|
||
m_iLocateBufferSize = 32768;
|
||
m_uBytesBeforeZip = 0;
|
||
m_uCurrentVolume = ZIP_VOLUME_NUMBER_UNSPECIFIED;
|
||
m_szArchiveName.Empty();
|
||
m_pSplitNames = NULL;
|
||
m_pCachedSizes = NULL;
|
||
m_bAutoDeleteSplitNames = false;
|
||
m_state = 0;
|
||
}
|
||
|
||
CZipStorage::~CZipStorage()
|
||
{
|
||
ClearSplitNames();
|
||
ClearCachedSizes();
|
||
}
|
||
|
||
DWORD CZipStorage::Read(void *pBuf, DWORD iSize, bool bAtOnce)
|
||
{
|
||
if (iSize == 0)
|
||
return 0;
|
||
DWORD iRead;
|
||
for(;;)
|
||
{
|
||
iRead = m_pFile->Read(pBuf, iSize);
|
||
if (!iRead)
|
||
{
|
||
if (IsSegmented())
|
||
ChangeVolume();
|
||
else
|
||
ThrowError(CZipException::badZipFile);
|
||
}
|
||
else
|
||
break;
|
||
}
|
||
|
||
if (iRead == iSize)
|
||
return iRead;
|
||
else if ((bAtOnce && !IsBinarySplit()) || !IsSegmented())
|
||
ThrowError(CZipException::badZipFile);
|
||
|
||
while (iRead < iSize)
|
||
{
|
||
ChangeVolume();
|
||
UINT iNewRead = m_pFile->Read((char*)pBuf + iRead, iSize - iRead);
|
||
if (!iNewRead && iRead < iSize)
|
||
ThrowError(CZipException::badZipFile);
|
||
iRead += iNewRead;
|
||
}
|
||
|
||
return iRead;
|
||
}
|
||
|
||
void CZipStorage::Open(LPCTSTR lpszPathName, int iMode, ZIP_SIZE_TYPE uVolumeSize)
|
||
{
|
||
m_uCurrentVolume = ZIP_VOLUME_NUMBER_UNSPECIFIED;
|
||
m_pWriteBuffer.Allocate(m_iWriteBufferSize);
|
||
m_uBytesInWriteBuffer = 0;
|
||
m_state.Set(stateOpened | stateAutoClose);
|
||
m_pFile = &m_internalfile;
|
||
m_szArchiveName = lpszPathName;
|
||
m_pChangeVolumeFunc = NULL;
|
||
|
||
CBitFlag mode(iMode);
|
||
|
||
if (mode.IsSetAny(CZipArchive::zipCreate)) // create new archive
|
||
{
|
||
m_uCurrentVolume = 0;
|
||
if (!mode.IsSetAny(CZipArchive::zipModeSegmented))
|
||
{
|
||
OpenFile(lpszPathName, (mode.IsSetAll(CZipArchive::zipCreateAppend) ? CZipFile::modeNoTruncate : CZipFile::modeCreate) | CZipFile::modeReadWrite);
|
||
}
|
||
else // create a segmented archive
|
||
{
|
||
m_uBytesWritten = 0;
|
||
if (mode.IsSetAny(CZipArchive::zipModeSpan))
|
||
{
|
||
if (!m_pSpanChangeVolumeFunc)
|
||
ThrowError(CZipException::noCallback);
|
||
if (!ZipPlatform::IsDriveRemovable(lpszPathName))
|
||
ThrowError(CZipException::nonRemovable);
|
||
m_state.Set(stateSpan);
|
||
m_pChangeVolumeFunc = m_pSpanChangeVolumeFunc;
|
||
}
|
||
else if (uVolumeSize <= 0)
|
||
ThrowError(CZipException::noVolumeSize);
|
||
else
|
||
{
|
||
m_uSplitData = uVolumeSize;
|
||
if (mode.IsSetAll(CZipArchive::zipModeBinSplit))
|
||
{
|
||
m_state.Set(stateBinarySplit);
|
||
ClearCachedSizes(); // just in case
|
||
m_pCachedSizes = new CZipArray<ZIP_FILE_USIZE>();
|
||
}
|
||
else
|
||
m_state.Set(stateSplit);
|
||
EnsureSplitNames();
|
||
m_pChangeVolumeFunc = m_pSplitChangeVolumeFunc;
|
||
}
|
||
|
||
NextVolume(4);
|
||
Write(m_gszExtHeaderSignat, 4, true);
|
||
}
|
||
}
|
||
else // open existing
|
||
{
|
||
m_state.Set(stateExisting);
|
||
bool readOnly = mode.IsSetAll(CZipArchive::zipOpenReadOnly);
|
||
if (readOnly)
|
||
m_state.Set(stateReadOnly);
|
||
OpenFile(lpszPathName, CZipFile::modeNoTruncate |
|
||
(readOnly ? CZipFile::modeRead : CZipFile::modeReadWrite));
|
||
// this will be revised in UpdateSegmMode
|
||
if (mode.IsSetAny(CZipArchive::zipModeSpan))
|
||
m_state.Set(stateSpan);
|
||
else if (mode.IsSetAll(CZipArchive::zipModeBinSplit))
|
||
{
|
||
m_state.Set(stateBinarySplit);
|
||
EnsureSplitNames();
|
||
m_uCurrentVolume = m_pSplitNames->GetVolumeNumber(m_szArchiveName);
|
||
if (m_uCurrentVolume == 0)
|
||
ThrowError(CZipException::badZipFile);
|
||
m_uCurrentVolume -= 1;
|
||
if (m_uCurrentVolume > 0)
|
||
{
|
||
m_uSplitData = m_uCurrentVolume;
|
||
CacheSizes();
|
||
}
|
||
else
|
||
{
|
||
ClearSplitNames();
|
||
m_state.Clear(stateBinarySplit);
|
||
}
|
||
}
|
||
else if (mode.IsSetAny(CZipArchive::zipModeSplit))
|
||
m_state.Set(stateSplit);
|
||
}
|
||
}
|
||
|
||
void CZipStorage::CacheSizes()
|
||
{
|
||
ClearCachedSizes(); // just in case
|
||
m_pCachedSizes = new CZipArray<ZIP_FILE_USIZE>();
|
||
m_pCachedSizes->SetSize((ZIP_ARRAY_SIZE_TYPE)(m_uCurrentVolume + 1));
|
||
ZIP_VOLUME_TYPE lastVolume = m_uCurrentVolume;
|
||
for(;;)
|
||
{
|
||
m_pCachedSizes->SetAt((ZIP_ARRAY_SIZE_TYPE)m_uCurrentVolume, m_pFile->GetLength());
|
||
if (m_uCurrentVolume == 0)
|
||
break;
|
||
ChangeVolume((ZIP_VOLUME_TYPE)(m_uCurrentVolume - 1));
|
||
}
|
||
ChangeVolume(lastVolume);
|
||
}
|
||
|
||
void CZipStorage::Open(CZipAbstractFile& af, int iMode, bool bAutoClose)
|
||
{
|
||
m_pWriteBuffer.Allocate(m_iWriteBufferSize);
|
||
m_uBytesInWriteBuffer = 0;
|
||
m_state.Set(stateOpened);
|
||
if (bAutoClose)
|
||
m_state.Set(stateAutoClose);
|
||
m_pFile = ⁡
|
||
|
||
CBitFlag mode(iMode);
|
||
|
||
if (mode.IsSetAny(CZipArchive::zipCreate))
|
||
{
|
||
m_uCurrentVolume = 0;
|
||
if (mode.IsSetAll(CZipArchive::zipCreateAppend))
|
||
af.SeekToEnd();
|
||
else
|
||
af.SetLength(0);
|
||
}
|
||
else // open existing
|
||
{
|
||
m_state.Set(stateExisting);
|
||
if (mode.IsSetAll(CZipArchive::zipOpenReadOnly))
|
||
m_state.Set(stateReadOnly);
|
||
af.SeekToBegin();
|
||
}
|
||
}
|
||
|
||
|
||
void CZipStorage::ChangeVolume(ZIP_VOLUME_TYPE uNumber)
|
||
{
|
||
if (uNumber == m_uCurrentVolume || !IsSegmented()) // the second condition may happen in some bad archives
|
||
return;
|
||
|
||
m_uCurrentVolume = uNumber;
|
||
OpenFile(IsSpanned() ? ChangeSpannedRead() : ChangeSplitRead(),
|
||
CZipFile::modeNoTruncate | CZipFile::modeRead);
|
||
}
|
||
|
||
void CZipStorage::ThrowError(int err) const
|
||
{
|
||
CZipException::Throw(err, m_pFile->GetFilePath());
|
||
}
|
||
|
||
bool CZipStorage::OpenFile(LPCTSTR lpszName, UINT uFlags, bool bThrow)
|
||
{
|
||
return m_pFile->Open(lpszName, uFlags | CZipFile::shareDenyWrite, bThrow);
|
||
}
|
||
|
||
|
||
CZipString CZipStorage::ChangeSpannedRead()
|
||
{
|
||
CZipString szTemp = m_pFile->GetFilePath();
|
||
m_pFile->Close();
|
||
CallCallback(0, CZipSegmCallback::scVolumeNeededForRead, szTemp);
|
||
return m_pChangeVolumeFunc->m_szExternalFile;
|
||
}
|
||
|
||
CZipString CZipStorage::ChangeSplitRead()
|
||
{
|
||
bool lastPart = (ZIP_SIZE_TYPE)m_uCurrentVolume == m_uSplitData;
|
||
CZipString szVolumeName = GetSplitVolumeName(lastPart);
|
||
if (m_pChangeVolumeFunc)
|
||
{
|
||
int iCode = CZipSegmCallback::scVolumeNeededForRead;
|
||
for(;;)
|
||
{
|
||
CallCallback(lastPart ? ZIP_SPLIT_LAST_VOLUME : 0, iCode, szVolumeName);
|
||
if (ZipPlatform::FileExists(m_pChangeVolumeFunc->m_szExternalFile))
|
||
{
|
||
szVolumeName = m_pChangeVolumeFunc->m_szExternalFile;
|
||
break;
|
||
}
|
||
else
|
||
iCode = CZipSegmCallback::scFileNotFound;
|
||
}
|
||
}
|
||
else if (!ZipPlatform::FileExists(szVolumeName))
|
||
{
|
||
CZipException::Throw(CZipException::notFound, szVolumeName);
|
||
}
|
||
m_pFile->Close();
|
||
return szVolumeName;
|
||
}
|
||
|
||
CZipString CZipStorage::RenameLastFileInSplitArchive()
|
||
{
|
||
ASSERT(IsSplit());
|
||
|
||
// give to the last volume the zip extension
|
||
CZipString szFileName = m_pFile->GetFilePath();
|
||
CZipString szNewFileName = GetSplitVolumeName(true);
|
||
if (m_pChangeVolumeFunc)
|
||
{
|
||
int code = CZipSegmCallback::scVolumeNeededForWrite;
|
||
for(;;)
|
||
{
|
||
CallCallback(ZIP_SPLIT_LAST_VOLUME, code, szNewFileName);
|
||
szNewFileName = m_pChangeVolumeFunc->m_szExternalFile;
|
||
if (ZipPlatform::FileExists(szNewFileName))
|
||
code = CZipSegmCallback::scFileNameDuplicated;
|
||
else
|
||
break;
|
||
}
|
||
}
|
||
|
||
m_pFile->Flush();
|
||
m_pFile->Close();
|
||
|
||
ZIPSTRINGCOMPARE pCompare = GetCZipStrCompFunc(ZipPlatform::GetSystemCaseSensitivity());
|
||
if ((szFileName.*(pCompare))(szNewFileName) == 0)
|
||
{
|
||
return szNewFileName;
|
||
}
|
||
|
||
if (!m_pChangeVolumeFunc && ZipPlatform::FileExists(szNewFileName))
|
||
{
|
||
ZipPlatform::RemoveFile(szNewFileName);
|
||
}
|
||
ZipPlatform::RenameFile(szFileName, szNewFileName);
|
||
return szNewFileName;
|
||
}
|
||
|
||
CZipString CZipStorage::Close(bool bWrite, bool bGetLastVolumeName)
|
||
{
|
||
bool bClose = true;
|
||
CZipString sz;
|
||
if (bWrite)
|
||
{
|
||
Flush();
|
||
if (IsSplit() && !IsExisting())
|
||
{
|
||
sz = RenameLastFileInSplitArchive();
|
||
bClose = false;// already closed in RenameLastFileInSplitArchive
|
||
}
|
||
}
|
||
|
||
if (bGetLastVolumeName && sz.IsEmpty())
|
||
{
|
||
if (IsSplit() && IsExisting())
|
||
sz = m_pSplitNames->GetVolumeName(m_szArchiveName, (ZIP_VOLUME_TYPE)(m_uSplitData + 1), CZipSplitNamesHandler::flLast | CZipSplitNamesHandler::flExisting);
|
||
else
|
||
sz = m_pFile->GetFilePath();
|
||
}
|
||
|
||
if (bClose)
|
||
{
|
||
if (bWrite)
|
||
FlushFile();
|
||
if (m_state.IsSetAny(stateAutoClose))
|
||
m_pFile->Close();
|
||
}
|
||
|
||
m_pWriteBuffer.Release();
|
||
m_uCurrentVolume = ZIP_VOLUME_NUMBER_UNSPECIFIED;
|
||
m_state = 0;
|
||
m_pFile = NULL;
|
||
m_uBytesBeforeZip = 0;
|
||
ClearSplitNames();
|
||
ClearCachedSizes();
|
||
return sz;
|
||
}
|
||
|
||
void CZipStorage::NextVolume(ZIP_SIZE_TYPE uNeeded)
|
||
{
|
||
Flush();
|
||
ASSERT(IsSegmented());
|
||
bool bSpan = IsSpanned();
|
||
if (m_uBytesWritten)
|
||
{
|
||
m_uBytesWritten = 0;
|
||
m_uCurrentVolume++;
|
||
ZIP_VOLUME_TYPE uMaxVolumes = (ZIP_VOLUME_TYPE)(bSpan ? 999 : 0xFFFF);
|
||
if (m_uCurrentVolume >= uMaxVolumes) // m_uCurrentVolume is a zero-based index
|
||
ThrowError(CZipException::tooManyVolumes);
|
||
}
|
||
|
||
CZipString szFileName;
|
||
|
||
if (bSpan)
|
||
szFileName = m_szArchiveName;
|
||
else
|
||
szFileName = GetSplitVolumeName(false);
|
||
|
||
if (!m_pFile->IsClosed())
|
||
{
|
||
m_pFile->Flush();
|
||
if (IsBinarySplit())
|
||
m_pCachedSizes->Add(m_pFile->GetLength());
|
||
m_pFile->Close();
|
||
}
|
||
|
||
if (m_pChangeVolumeFunc)
|
||
{
|
||
int iCode = CZipSegmCallback::scVolumeNeededForWrite;
|
||
for(;;)
|
||
{
|
||
CallCallback(uNeeded, iCode, szFileName);
|
||
// allow changing of the filename
|
||
szFileName = m_pChangeVolumeFunc->m_szExternalFile;
|
||
|
||
if (ZipPlatform::FileExists(szFileName))
|
||
iCode = CZipSegmCallback::scFileNameDuplicated;
|
||
else
|
||
{
|
||
if (bSpan)
|
||
{
|
||
CZipString label;
|
||
label.Format(_T("pkback# %.3d"), m_uCurrentVolume + 1);
|
||
if (!ZipPlatform::SetVolLabel(szFileName, label))
|
||
{
|
||
iCode = CZipSegmCallback::scCannotSetVolLabel;
|
||
continue;
|
||
}
|
||
}
|
||
|
||
if (OpenFile(szFileName, CZipFile::modeCreate | CZipFile::modeReadWrite, false))
|
||
break;
|
||
else
|
||
iCode = CZipSegmCallback::scFileCreationFailure;
|
||
}
|
||
|
||
}
|
||
m_uCurrentVolSize = bSpan ? GetFreeVolumeSpace() : m_uSplitData;
|
||
}
|
||
else
|
||
{
|
||
if (bSpan)
|
||
ThrowError(CZipException::internalError);
|
||
m_uCurrentVolSize = m_uSplitData;
|
||
OpenFile(szFileName, CZipFile::modeCreate | CZipFile::modeReadWrite);
|
||
}
|
||
}
|
||
|
||
void CZipStorage::CallCallback(ZIP_SIZE_TYPE uNeeded, int iCode, CZipString szTemp)
|
||
{
|
||
if (!m_pChangeVolumeFunc)
|
||
ThrowError(CZipException::internalError);
|
||
m_pChangeVolumeFunc->m_szExternalFile = szTemp;
|
||
m_pChangeVolumeFunc->m_uVolumeNeeded = (ZIP_VOLUME_TYPE)(m_uCurrentVolume + 1);
|
||
m_pChangeVolumeFunc->m_iCode = iCode;
|
||
if (!m_pChangeVolumeFunc->Callback(uNeeded))
|
||
CZipException::Throw(CZipException::aborted, szTemp);
|
||
}
|
||
|
||
ZIP_SIZE_TYPE CZipStorage::GetFreeVolumeSpace() const
|
||
{
|
||
ASSERT (IsSpanned());
|
||
CZipString szTemp = m_pFile->GetFilePath();
|
||
if (szTemp.IsEmpty()) // called once when creating a segmented archive
|
||
return 0;
|
||
else
|
||
{
|
||
CZipPathComponent zpc(szTemp);
|
||
ULONGLONG ret = ZipPlatform::GetDeviceFreeSpace(zpc.GetFilePath());
|
||
if (ret > (ZIP_SIZE_TYPE)(-1))
|
||
return (ZIP_SIZE_TYPE)(-1);
|
||
else
|
||
return (ZIP_SIZE_TYPE)ret;
|
||
}
|
||
}
|
||
|
||
|
||
void CZipStorage::UpdateSegmMode(ZIP_VOLUME_TYPE uLastDisk)
|
||
{
|
||
bool binarySplit = IsBinarySplit();
|
||
if (!binarySplit)
|
||
m_uCurrentVolume = uLastDisk;
|
||
|
||
if (uLastDisk > 0 || binarySplit)
|
||
{
|
||
// segmentation detected
|
||
CZipString szFilePath = m_pFile->GetFilePath();
|
||
if (!m_state.IsSetAny(stateSegmented))
|
||
m_state.Set(ZipPlatform::IsDriveRemovable(szFilePath) ? stateSpan : stateSplit);
|
||
|
||
if (IsSpanned())
|
||
{
|
||
if (!m_pSpanChangeVolumeFunc)
|
||
ThrowError(CZipException::noCallback);
|
||
m_pChangeVolumeFunc = m_pSpanChangeVolumeFunc;
|
||
}
|
||
else /*if (IsSplit())*/
|
||
{
|
||
EnsureSplitNames();
|
||
if (!binarySplit)
|
||
m_uSplitData = uLastDisk; // the last volume
|
||
m_pChangeVolumeFunc = m_pSplitChangeVolumeFunc;
|
||
}
|
||
m_pWriteBuffer.Release(); // no need for this in this case
|
||
}
|
||
else
|
||
// it will clear all segmented flags
|
||
m_state.Clear(stateSpan | stateBinarySplit);
|
||
}
|
||
|
||
ZIP_SIZE_TYPE CZipStorage::AssureFree(ZIP_SIZE_TYPE uNeeded)
|
||
{
|
||
ZIP_SIZE_TYPE uFree;
|
||
while ((uFree = VolumeLeft()) < uNeeded)
|
||
{
|
||
if (IsSplit() && !m_uBytesWritten && !m_uBytesInWriteBuffer)
|
||
// in the splitArchive mode, if the size of the archive is less
|
||
// than the size of the packet to be written at once,
|
||
// increase once the size of the volume
|
||
m_uCurrentVolSize = uNeeded;
|
||
else
|
||
NextVolume(uNeeded);
|
||
}
|
||
return uFree;
|
||
}
|
||
|
||
void CZipStorage::Write(const void *pBuf, DWORD iSize, bool bAtOnce)
|
||
{
|
||
if (!IsSegmented())
|
||
WriteInternalBuffer((char*)pBuf, iSize);
|
||
else
|
||
{
|
||
bool atOnce = bAtOnce && !IsBinarySplit();
|
||
// if not at once, one byte is enough of free space
|
||
DWORD iNeeded = atOnce ? iSize : 1;
|
||
DWORD uTotal = 0;
|
||
|
||
while (uTotal < iSize)
|
||
{
|
||
ZIP_SIZE_TYPE uFree = AssureFree(iNeeded);
|
||
DWORD uLeftToWrite = iSize - uTotal;
|
||
DWORD uToWrite = uFree < uLeftToWrite ? (DWORD)uFree : uLeftToWrite;
|
||
WriteInternalBuffer((char*)pBuf + uTotal, uToWrite);
|
||
if (atOnce)
|
||
return;
|
||
else
|
||
uTotal += uToWrite;
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
|
||
void CZipStorage::WriteInternalBuffer(const char *pBuf, DWORD uSize)
|
||
{
|
||
DWORD uWritten = 0;
|
||
while (uWritten < uSize)
|
||
{
|
||
DWORD uFreeInBuffer = GetFreeInBuffer();
|
||
if (uFreeInBuffer == 0)
|
||
{
|
||
Flush();
|
||
uFreeInBuffer = m_pWriteBuffer.GetSize();
|
||
}
|
||
DWORD uLeftToWrite = uSize - uWritten;
|
||
DWORD uToCopy = uLeftToWrite < uFreeInBuffer ? uLeftToWrite : uFreeInBuffer;
|
||
memcpy((char*)m_pWriteBuffer + m_uBytesInWriteBuffer, pBuf + uWritten, uToCopy);
|
||
uWritten += uToCopy;
|
||
m_uBytesInWriteBuffer += uToCopy;
|
||
}
|
||
}
|
||
|
||
ZIP_SIZE_TYPE CZipStorage::VolumeLeft() const
|
||
{
|
||
// for spanned archives m_uCurrentVolSize is updated after each flush()
|
||
ZIP_SIZE_TYPE uBytes = m_uBytesInWriteBuffer + (IsSpanned() ? 0 : m_uBytesWritten);
|
||
return uBytes > m_uCurrentVolSize ? 0 : m_uCurrentVolSize - uBytes;
|
||
}
|
||
|
||
void CZipStorage::Flush()
|
||
{
|
||
if (m_uBytesInWriteBuffer)
|
||
{
|
||
m_pFile->Write(m_pWriteBuffer, m_uBytesInWriteBuffer);
|
||
if (IsSegmented())
|
||
m_uBytesWritten += m_uBytesInWriteBuffer;
|
||
m_uBytesInWriteBuffer = 0;
|
||
}
|
||
if (IsSpanned())
|
||
// after writing it is difficult to predict the free space due to
|
||
// not completely written clusters, write operation may start from a new cluster
|
||
m_uCurrentVolSize = GetFreeVolumeSpace();
|
||
}
|
||
|
||
ZIP_FILE_USIZE CZipStorage::LocateSignature(char* szSignature, ZIP_SIZE_TYPE uMaxDepth)
|
||
{
|
||
m_pFile->SeekToEnd();
|
||
int leftToFind = SIGNATURE_SIZE - 1;
|
||
bool found = false; // for fast checking if leftToFind needs resetting
|
||
if (!IsBinarySplit())
|
||
{
|
||
return LocateSignature(szSignature, uMaxDepth, leftToFind, found, m_pFile->GetLength());
|
||
}
|
||
else
|
||
{
|
||
for(;;)
|
||
{
|
||
ZIP_FILE_USIZE uFileLength = GetCachedSize(m_uCurrentVolume);
|
||
ZIP_FILE_USIZE position = LocateSignature(szSignature, uMaxDepth, leftToFind, found, uFileLength);
|
||
if (position != SignatureNotFound || uMaxDepth <= uFileLength)
|
||
return position;
|
||
uMaxDepth -= (ZIP_SIZE_TYPE)uFileLength;
|
||
if (m_uCurrentVolume == 0)
|
||
return SignatureNotFound;
|
||
ChangeVolumeDec();
|
||
m_pFile->SeekToEnd();
|
||
}
|
||
}
|
||
}
|
||
|
||
ZIP_FILE_USIZE CZipStorage::LocateSignature(char* szSignature, ZIP_SIZE_TYPE uMaxDepth, int& leftToFind, bool& found, ZIP_FILE_USIZE uFileLength)
|
||
{
|
||
CZipAutoBuffer buffer(m_iLocateBufferSize);
|
||
|
||
ZIP_SIZE_TYPE max = (ZIP_SIZE_TYPE)(uFileLength < uMaxDepth ? uFileLength : uMaxDepth);
|
||
ZIP_SIZE_TYPE position = (ZIP_SIZE_TYPE)(uFileLength - m_pFile->GetPosition());
|
||
int offset = 0;
|
||
|
||
int toRead = m_iLocateBufferSize;
|
||
|
||
while ( position < max )
|
||
{
|
||
position += toRead;
|
||
if ( position > max )
|
||
{
|
||
int diff = (int) ( position - max );
|
||
toRead -= diff;
|
||
offset = diff;
|
||
position = max;
|
||
}
|
||
Seek(position, seekFromEnd);
|
||
int actuallyRead = m_pFile->Read((char*)buffer + offset, toRead);
|
||
if (actuallyRead != toRead)
|
||
ThrowError(CZipException::badZipFile);
|
||
int pos = m_iLocateBufferSize - 1;
|
||
while ( pos >= offset )
|
||
{
|
||
if ( buffer[pos] == szSignature[leftToFind] )
|
||
{
|
||
if ( leftToFind == 0 )
|
||
return (ZIP_FILE_USIZE)(uFileLength - ( position - ( pos - offset ) ));
|
||
if ( !found )
|
||
found = true;
|
||
leftToFind--;
|
||
pos--;
|
||
}
|
||
else if ( found )
|
||
{
|
||
leftToFind = SIGNATURE_SIZE - 1;
|
||
found = false;
|
||
// do not decrease position, the current pos may be the first to find
|
||
}
|
||
else
|
||
pos--;
|
||
}
|
||
}
|
||
return SignatureNotFound;
|
||
}
|
||
|
||
void CZipStorage::SeekInBinary(ZIP_FILE_SIZE lOff, bool bSeekToBegin)
|
||
{
|
||
ASSERT(IsBinarySplit());
|
||
if (bSeekToBegin)
|
||
m_pFile->SeekToBegin();
|
||
|
||
if (lOff == 0)
|
||
return;
|
||
|
||
if (lOff > 0)
|
||
{
|
||
ZIP_SIZE_TYPE uPosition = (ZIP_SIZE_TYPE)m_pFile->GetPosition();
|
||
ZIP_FILE_USIZE uLength = GetCachedSize(m_uCurrentVolume);
|
||
if ((ZIP_FILE_USIZE)(uPosition + lOff) < uLength)
|
||
{
|
||
m_pFile->Seek(lOff, CZipAbstractFile::current);
|
||
return;
|
||
}
|
||
ZIP_VOLUME_TYPE uVolume = (ZIP_VOLUME_TYPE)(m_uCurrentVolume + 1);
|
||
lOff -= uLength - uPosition;
|
||
for(;;)
|
||
{
|
||
uLength = GetCachedSize(uVolume);
|
||
if ((ZIP_FILE_USIZE)lOff < uLength)
|
||
{
|
||
ChangeVolume(uVolume);
|
||
if (lOff > 0)
|
||
{
|
||
m_pFile->Seek(lOff, CZipAbstractFile::current);
|
||
}
|
||
return;
|
||
}
|
||
else
|
||
lOff -= uLength;
|
||
uVolume++;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
ZIP_SIZE_TYPE uPosition = (ZIP_SIZE_TYPE)m_pFile->GetPosition();
|
||
if (uPosition >= (ZIP_SIZE_TYPE)(-lOff))
|
||
{
|
||
m_pFile->Seek(lOff, CZipAbstractFile::current);
|
||
return;
|
||
}
|
||
lOff += uPosition;
|
||
ZIP_VOLUME_TYPE uVolume = (ZIP_VOLUME_TYPE)(m_uCurrentVolume - 1);
|
||
for(;;)
|
||
{
|
||
ZIP_FILE_USIZE uLength = GetCachedSize(uVolume);
|
||
if (uLength >= (ZIP_SIZE_TYPE)(-lOff))
|
||
{
|
||
ChangeVolume(uVolume);
|
||
if (lOff < 0)
|
||
{
|
||
m_pFile->Seek(lOff, CZipAbstractFile::end);
|
||
}
|
||
return;
|
||
}
|
||
else
|
||
lOff += uLength;
|
||
if (uVolume == 0)
|
||
ThrowError(CZipException::genericError);
|
||
uVolume--;
|
||
}
|
||
}
|
||
}
|
||
|
||
ULONGLONG CZipStorage::Seek(ULONGLONG lOff, SeekType iSeekType)
|
||
{
|
||
if (iSeekType == seekCurrent)
|
||
{
|
||
if (IsExistingSegmented())
|
||
{
|
||
ZIP_SIZE_TYPE uPosition = (ZIP_SIZE_TYPE)m_pFile->GetPosition();
|
||
ZIP_FILE_USIZE uLength = m_pFile->GetLength();
|
||
while (uPosition + lOff >= uLength)
|
||
{
|
||
ZIP_SIZE_TYPE uCanSeek = (ZIP_SIZE_TYPE)(uLength - uPosition);
|
||
lOff -= uCanSeek;
|
||
ChangeVolume();
|
||
uPosition = 0;
|
||
uLength = m_pFile->GetLength();
|
||
}
|
||
return lOff > 0 ? m_pFile->SafeSeek((ZIP_FILE_USIZE)lOff) : 0;
|
||
}
|
||
else
|
||
return m_pFile->Seek((ZIP_FILE_SIZE)lOff, CZipAbstractFile::current);
|
||
}
|
||
else
|
||
{
|
||
if (m_uCurrentVolume == 0 && m_uBytesBeforeZip > 0)
|
||
lOff += m_uBytesBeforeZip;
|
||
return m_pFile->SafeSeek((ZIP_FILE_USIZE)lOff, iSeekType == seekFromBeginning);
|
||
}
|
||
}
|
||
|
||
void CZipStorage::FinalizeSegm()
|
||
{
|
||
ASSERT(IsNewSegmented()); // spanned archive in creation
|
||
|
||
CZipString szFileName;
|
||
if (IsSplit())
|
||
{
|
||
// the file is already closed
|
||
szFileName = RenameLastFileInSplitArchive();
|
||
if (IsBinarySplit() && m_uCurrentVolume != 0)
|
||
{
|
||
ZIP_SIZE_TYPE size;
|
||
ZipPlatform::GetFileSize(szFileName, size);
|
||
m_pCachedSizes->Add(ZIP_FILE_USIZE(size));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
szFileName = m_pFile->GetFilePath();
|
||
m_pFile->Close();
|
||
}
|
||
m_state.Set(stateExisting);
|
||
if (m_uCurrentVolume == 0) // one-volume segmented archive was converted to a normal archive
|
||
{
|
||
if (IsSplit())
|
||
{
|
||
ClearSplitNames();
|
||
if (IsBinarySplit())
|
||
ClearCachedSizes();
|
||
}
|
||
m_state.Clear(stateSplit | stateBinarySplit | stateSpan);
|
||
}
|
||
else
|
||
{
|
||
m_uSplitData = m_uCurrentVolume;
|
||
if (IsSplit())
|
||
{
|
||
m_szArchiveName = szFileName;
|
||
}
|
||
}
|
||
|
||
OpenFile(szFileName, CZipFile::modeNoTruncate | (IsSegmented() ? CZipFile::modeReadWrite : CZipFile::modeRead));
|
||
}
|
||
|