// 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; | |
UString GetNextName() | |
{ | |
UString newName; | |
if (_splitStyle) | |
{ | |
int i; | |
int numLetters = _changedPart.Len(); | |
for (i = numLetters - 1; i >= 0; i--) | |
{ | |
wchar_t c = _changedPart[i]; | |
if (c == 'z') | |
{ | |
newName.InsertAtFront('a'); | |
continue; | |
} | |
else if (c == 'Z') | |
{ | |
newName.InsertAtFront('A'); | |
continue; | |
} | |
c++; | |
if ((c == 'z' || c == 'Z') && i == 0) | |
{ | |
_unchangedPart += c; | |
wchar_t newChar = (c == 'z') ? L'a' : L'A'; | |
newName.Empty(); | |
numLetters++; | |
for (int k = 0; k < numLetters; k++) | |
newName += newChar; | |
break; | |
} | |
newName.InsertAtFront(c); | |
i--; | |
for (; i >= 0; i--) | |
newName.InsertAtFront(_changedPart[i]); | |
break; | |
} | |
} | |
else | |
{ | |
int i; | |
int numLetters = _changedPart.Len(); | |
for (i = numLetters - 1; i >= 0; i--) | |
{ | |
wchar_t c = _changedPart[i]; | |
if (c == '9') | |
{ | |
newName.InsertAtFront('0'); | |
if (i == 0) | |
newName.InsertAtFront('1'); | |
continue; | |
} | |
c++; | |
newName.InsertAtFront(c); | |
i--; | |
for (; i >= 0; i--) | |
newName.InsertAtFront(_changedPart[i]); | |
break; | |
} | |
} | |
_changedPart = newName; | |
return _unchangedPart + _changedPart; | |
} | |
}; | |
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('.'); | |
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 = L"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; | |
} | |
_totalSize += size; | |
_sizes.Add(size); | |
_streams.Add(stream); | |
{ | |
UInt64 numFiles = _streams.Size(); | |
RINOK(callback->SetCompleted(&numFiles, NULL)); | |
} | |
for (;;) | |
{ | |
const UString fullName = seqName.GetNextName(); | |
CMyComPtr<IInStream> nextStream; | |
HRESULT result = volumeCallback->GetStream(fullName, &nextStream); | |
if (result == S_FALSE) | |
break; | |
if (result != S_OK) | |
return result; | |
if (!stream) | |
break; | |
{ | |
NCOM::CPropVariant prop; | |
RINOK(volumeCallback->GetProperty(kpidSize, &prop)); | |
if (prop.vt != VT_UI8) | |
return E_INVALIDARG; | |
size = prop.uhVal.QuadPart; | |
} | |
_totalSize += size; | |
_sizes.Add(size); | |
_streams.Add(nextStream); | |
{ | |
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 | |
} | |
IMP_CreateArcIn | |
static CArcInfo g_ArcInfo = | |
{ "Split", "001", 0, 0xEA, | |
0, { 0 }, | |
0, | |
0, | |
CreateArc }; | |
REGISTER_ARC(Split) | |
}} |