////////////////////////////////////////////////////////////////////////////////
|
// This source file is part of the ZipArchive library source distribution and
|
// is Copyrighted 2000 - 2011 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;
|
}
|
}
|
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));
|
}
|