360 lines
8.1 KiB
C++
360 lines
8.1 KiB
C++
|
// SplitHandler.cpp
|
||
|
|
||
|
#include "StdAfx.h"
|
||
|
|
||
|
#include "../../Common/ComTry.h"
|
||
|
#include "../../Common/MyString.h"
|
||
|
|
||
|
#include "../../Windows/PropVariant.h"
|
||
|
|
||
|
#include "../Common/ProgressUtils.h"
|
||
|
#include "../Common/RegisterArc.h"
|
||
|
|
||
|
#include "../Compress/CopyCoder.h"
|
||
|
|
||
|
#include "Common/MultiStream.h"
|
||
|
|
||
|
using namespace NWindows;
|
||
|
|
||
|
namespace NArchive {
|
||
|
namespace NSplit {
|
||
|
|
||
|
static const Byte kProps[] =
|
||
|
{
|
||
|
kpidPath,
|
||
|
kpidSize
|
||
|
};
|
||
|
|
||
|
static const Byte kArcProps[] =
|
||
|
{
|
||
|
kpidNumVolumes,
|
||
|
kpidTotalPhySize
|
||
|
};
|
||
|
|
||
|
class CHandler:
|
||
|
public IInArchive,
|
||
|
public IInArchiveGetStream,
|
||
|
public CMyUnknownImp
|
||
|
{
|
||
|
CObjectVector<CMyComPtr<IInStream> > _streams;
|
||
|
CRecordVector<UInt64> _sizes;
|
||
|
UString _subName;
|
||
|
UInt64 _totalSize;
|
||
|
|
||
|
HRESULT Open2(IInStream *stream, IArchiveOpenCallback *callback);
|
||
|
public:
|
||
|
MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream)
|
||
|
INTERFACE_IInArchive(;)
|
||
|
STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
|
||
|
};
|
||
|
|
||
|
IMP_IInArchive_Props
|
||
|
IMP_IInArchive_ArcProps
|
||
|
|
||
|
STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
|
||
|
{
|
||
|
NCOM::CPropVariant prop;
|
||
|
switch (propID)
|
||
|
{
|
||
|
case kpidMainSubfile: prop = (UInt32)0; break;
|
||
|
case kpidPhySize: if (!_sizes.IsEmpty()) prop = _sizes[0]; break;
|
||
|
case kpidTotalPhySize: prop = _totalSize; break;
|
||
|
case kpidNumVolumes: prop = (UInt32)_streams.Size(); break;
|
||
|
}
|
||
|
prop.Detach(value);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
struct CSeqName
|
||
|
{
|
||
|
UString _unchangedPart;
|
||
|
UString _changedPart;
|
||
|
bool _splitStyle;
|
||
|
|
||
|
bool GetNextName(UString &s)
|
||
|
{
|
||
|
{
|
||
|
unsigned i = _changedPart.Len();
|
||
|
for (;;)
|
||
|
{
|
||
|
wchar_t c = _changedPart[--i];
|
||
|
|
||
|
if (_splitStyle)
|
||
|
{
|
||
|
if (c == 'z')
|
||
|
{
|
||
|
_changedPart.ReplaceOneCharAtPos(i, L'a');
|
||
|
if (i == 0)
|
||
|
return false;
|
||
|
continue;
|
||
|
}
|
||
|
else if (c == 'Z')
|
||
|
{
|
||
|
_changedPart.ReplaceOneCharAtPos(i, L'A');
|
||
|
if (i == 0)
|
||
|
return false;
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (c == '9')
|
||
|
{
|
||
|
_changedPart.ReplaceOneCharAtPos(i, L'0');
|
||
|
if (i == 0)
|
||
|
{
|
||
|
_changedPart.InsertAtFront(L'1');
|
||
|
break;
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
c++;
|
||
|
_changedPart.ReplaceOneCharAtPos(i, c);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
s = _unchangedPart + _changedPart;
|
||
|
return true;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback)
|
||
|
{
|
||
|
Close();
|
||
|
if (!callback)
|
||
|
return S_FALSE;
|
||
|
|
||
|
CMyComPtr<IArchiveOpenVolumeCallback> volumeCallback;
|
||
|
callback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&volumeCallback);
|
||
|
if (!volumeCallback)
|
||
|
return S_FALSE;
|
||
|
|
||
|
UString name;
|
||
|
{
|
||
|
NCOM::CPropVariant prop;
|
||
|
RINOK(volumeCallback->GetProperty(kpidName, &prop));
|
||
|
if (prop.vt != VT_BSTR)
|
||
|
return S_FALSE;
|
||
|
name = prop.bstrVal;
|
||
|
}
|
||
|
|
||
|
int dotPos = name.ReverseFind_Dot();
|
||
|
const UString prefix = name.Left(dotPos + 1);
|
||
|
const UString ext = name.Ptr(dotPos + 1);
|
||
|
UString ext2 = ext;
|
||
|
ext2.MakeLower_Ascii();
|
||
|
|
||
|
CSeqName seqName;
|
||
|
|
||
|
unsigned numLetters = 2;
|
||
|
bool splitStyle = false;
|
||
|
|
||
|
if (ext2.Len() >= 2 && StringsAreEqual_Ascii(ext2.RightPtr(2), "aa"))
|
||
|
{
|
||
|
splitStyle = true;
|
||
|
while (numLetters < ext2.Len())
|
||
|
{
|
||
|
if (ext2[ext2.Len() - numLetters - 1] != 'a')
|
||
|
break;
|
||
|
numLetters++;
|
||
|
}
|
||
|
}
|
||
|
else if (ext.Len() >= 2 && StringsAreEqual_Ascii(ext2.RightPtr(2), "01"))
|
||
|
{
|
||
|
while (numLetters < ext2.Len())
|
||
|
{
|
||
|
if (ext2[ext2.Len() - numLetters - 1] != '0')
|
||
|
break;
|
||
|
numLetters++;
|
||
|
}
|
||
|
if (numLetters != ext.Len())
|
||
|
return S_FALSE;
|
||
|
}
|
||
|
else
|
||
|
return S_FALSE;
|
||
|
|
||
|
seqName._unchangedPart = prefix + ext.Left(ext2.Len() - numLetters);
|
||
|
seqName._changedPart = ext.RightPtr(numLetters);
|
||
|
seqName._splitStyle = splitStyle;
|
||
|
|
||
|
if (prefix.Len() < 1)
|
||
|
_subName.SetFromAscii("file");
|
||
|
else
|
||
|
_subName.SetFrom(prefix, prefix.Len() - 1);
|
||
|
|
||
|
UInt64 size;
|
||
|
{
|
||
|
/*
|
||
|
NCOM::CPropVariant prop;
|
||
|
RINOK(volumeCallback->GetProperty(kpidSize, &prop));
|
||
|
if (prop.vt != VT_UI8)
|
||
|
return E_INVALIDARG;
|
||
|
size = prop.uhVal.QuadPart;
|
||
|
*/
|
||
|
RINOK(stream->Seek(0, STREAM_SEEK_END, &size));
|
||
|
RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL));
|
||
|
}
|
||
|
|
||
|
_totalSize += size;
|
||
|
_sizes.Add(size);
|
||
|
_streams.Add(stream);
|
||
|
|
||
|
{
|
||
|
const UInt64 numFiles = _streams.Size();
|
||
|
RINOK(callback->SetCompleted(&numFiles, NULL));
|
||
|
}
|
||
|
|
||
|
for (;;)
|
||
|
{
|
||
|
UString fullName;
|
||
|
if (!seqName.GetNextName(fullName))
|
||
|
break;
|
||
|
CMyComPtr<IInStream> nextStream;
|
||
|
HRESULT result = volumeCallback->GetStream(fullName, &nextStream);
|
||
|
if (result == S_FALSE)
|
||
|
break;
|
||
|
if (result != S_OK)
|
||
|
return result;
|
||
|
if (!nextStream)
|
||
|
break;
|
||
|
{
|
||
|
/*
|
||
|
NCOM::CPropVariant prop;
|
||
|
RINOK(volumeCallback->GetProperty(kpidSize, &prop));
|
||
|
if (prop.vt != VT_UI8)
|
||
|
return E_INVALIDARG;
|
||
|
size = prop.uhVal.QuadPart;
|
||
|
*/
|
||
|
RINOK(nextStream->Seek(0, STREAM_SEEK_END, &size));
|
||
|
RINOK(nextStream->Seek(0, STREAM_SEEK_SET, NULL));
|
||
|
}
|
||
|
_totalSize += size;
|
||
|
_sizes.Add(size);
|
||
|
_streams.Add(nextStream);
|
||
|
{
|
||
|
const UInt64 numFiles = _streams.Size();
|
||
|
RINOK(callback->SetCompleted(&numFiles, NULL));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (_streams.Size() == 1)
|
||
|
{
|
||
|
if (splitStyle)
|
||
|
return S_FALSE;
|
||
|
}
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *callback)
|
||
|
{
|
||
|
COM_TRY_BEGIN
|
||
|
HRESULT res = Open2(stream, callback);
|
||
|
if (res != S_OK)
|
||
|
Close();
|
||
|
return res;
|
||
|
COM_TRY_END
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CHandler::Close()
|
||
|
{
|
||
|
_totalSize = 0;
|
||
|
_subName.Empty();
|
||
|
_streams.Clear();
|
||
|
_sizes.Clear();
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
|
||
|
{
|
||
|
*numItems = _streams.IsEmpty() ? 0 : 1;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value)
|
||
|
{
|
||
|
NCOM::CPropVariant prop;
|
||
|
switch (propID)
|
||
|
{
|
||
|
case kpidPath: prop = _subName; break;
|
||
|
case kpidSize:
|
||
|
case kpidPackSize:
|
||
|
prop = _totalSize;
|
||
|
break;
|
||
|
}
|
||
|
prop.Detach(value);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
|
||
|
Int32 testMode, IArchiveExtractCallback *extractCallback)
|
||
|
{
|
||
|
COM_TRY_BEGIN
|
||
|
if (numItems == 0)
|
||
|
return S_OK;
|
||
|
if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0))
|
||
|
return E_INVALIDARG;
|
||
|
|
||
|
UInt64 currentTotalSize = 0;
|
||
|
RINOK(extractCallback->SetTotal(_totalSize));
|
||
|
CMyComPtr<ISequentialOutStream> outStream;
|
||
|
Int32 askMode = testMode ?
|
||
|
NExtract::NAskMode::kTest :
|
||
|
NExtract::NAskMode::kExtract;
|
||
|
RINOK(extractCallback->GetStream(0, &outStream, askMode));
|
||
|
if (!testMode && !outStream)
|
||
|
return S_OK;
|
||
|
RINOK(extractCallback->PrepareOperation(askMode));
|
||
|
|
||
|
NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder;
|
||
|
CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
|
||
|
|
||
|
CLocalProgress *lps = new CLocalProgress;
|
||
|
CMyComPtr<ICompressProgressInfo> progress = lps;
|
||
|
lps->Init(extractCallback, false);
|
||
|
|
||
|
FOR_VECTOR (i, _streams)
|
||
|
{
|
||
|
lps->InSize = lps->OutSize = currentTotalSize;
|
||
|
RINOK(lps->SetCur());
|
||
|
IInStream *inStream = _streams[i];
|
||
|
RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL));
|
||
|
RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress));
|
||
|
currentTotalSize += copyCoderSpec->TotalSize;
|
||
|
}
|
||
|
outStream.Release();
|
||
|
return extractCallback->SetOperationResult(NExtract::NOperationResult::kOK);
|
||
|
COM_TRY_END
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
|
||
|
{
|
||
|
COM_TRY_BEGIN
|
||
|
if (index != 0)
|
||
|
return E_INVALIDARG;
|
||
|
*stream = 0;
|
||
|
CMultiStream *streamSpec = new CMultiStream;
|
||
|
CMyComPtr<ISequentialInStream> streamTemp = streamSpec;
|
||
|
FOR_VECTOR (i, _streams)
|
||
|
{
|
||
|
CMultiStream::CSubStreamInfo subStreamInfo;
|
||
|
subStreamInfo.Stream = _streams[i];
|
||
|
subStreamInfo.Size = _sizes[i];
|
||
|
streamSpec->Streams.Add(subStreamInfo);
|
||
|
}
|
||
|
streamSpec->Init();
|
||
|
*stream = streamTemp.Detach();
|
||
|
return S_OK;
|
||
|
COM_TRY_END
|
||
|
}
|
||
|
|
||
|
REGISTER_ARC_I_NO_SIG(
|
||
|
"Split", "001", 0, 0xEA,
|
||
|
0,
|
||
|
0,
|
||
|
NULL)
|
||
|
|
||
|
}}
|