#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 {
STATPROPSTG kProps[] =
{
{ NULL, kpidPath, VT_BSTR},
{ NULL, kpidSize, VT_UI8}
};
STATPROPSTG kArcProps[] =
{
{ NULL, kpidNumVolumes, VT_UI4}
};
class
CHandler:
public
IInArchive,
public
IInArchiveGetStream,
public
CMyUnknownImp
{
UString _subName;
CObjectVector<CMyComPtr<IInStream> > _streams;
CRecordVector<UInt64> _sizes;
UInt64 _totalSize;
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
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.Length();
for
(i = numLetters - 1; i >= 0; i--)
{
wchar_t
c = _changedPart[i];
if
(c ==
'z'
)
{
c =
'a'
;
newName = c + newName;
continue
;
}
else
if
(c ==
'Z'
)
{
c =
'A'
;
newName = c + newName;
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 = c + newName;
i--;
for
(; i >= 0; i--)
newName = _changedPart[i] + newName;
break
;
}
}
else
{
int
i;
int
numLetters = _changedPart.Length();
for
(i = numLetters - 1; i >= 0; i--)
{
wchar_t
c = _changedPart[i];
if
(c == L
'9'
)
{
c = L
'0'
;
newName = c + newName;
if
(i == 0)
newName = UString(L
'1'
) + newName;
continue
;
}
c++;
newName = c + newName;
i--;
for
(; i >= 0; i--)
newName = _changedPart[i] + newName;
break
;
}
}
_changedPart = newName;
return
_unchangedPart + _changedPart;
}
};
STDMETHODIMP CHandler::Open(IInStream *stream,
const
UInt64 *
,
IArchiveOpenCallback *openArchiveCallback)
{
COM_TRY_BEGIN
Close();
if
(openArchiveCallback == 0)
return
S_FALSE;
{
CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback;
CMyComPtr<IArchiveOpenCallback> openArchiveCallbackWrap = openArchiveCallback;
if
(openArchiveCallbackWrap.QueryInterface(IID_IArchiveOpenVolumeCallback,
&openVolumeCallback) != S_OK)
return
S_FALSE;
UString name;
{
NCOM::CPropVariant prop;
RINOK(openVolumeCallback->GetProperty(kpidName, &prop));
if
(prop.vt != VT_BSTR)
return
S_FALSE;
name = prop.bstrVal;
}
int
dotPos = name.ReverseFind(
'.'
);
UString prefix, ext;
if
(dotPos >= 0)
{
prefix = name.Left(dotPos + 1);
ext = name.Mid(dotPos + 1);
}
else
ext = name;
UString extBig = ext;
extBig.MakeUpper();
CSeqName seqName;
int
numLetters = 2;
bool
splitStyle =
false
;
if
(extBig.Right(2) == L
"AA"
)
{
splitStyle =
true
;
while
(numLetters < extBig.Length())
{
if
(extBig[extBig.Length() - numLetters - 1] !=
'A'
)
break
;
numLetters++;
}
}
else
if
(ext.Right(2) == L
"01"
)
{
while
(numLetters < extBig.Length())
{
if
(extBig[extBig.Length() - numLetters - 1] !=
'0'
)
break
;
numLetters++;
}
if
(numLetters != ext.Length())
return
S_FALSE;
}
else
return
S_FALSE;
_streams.Add(stream);
seqName._unchangedPart = prefix + ext.Left(extBig.Length() - numLetters);
seqName._changedPart = ext.Right(numLetters);
seqName._splitStyle = splitStyle;
if
(prefix.Length() < 1)
_subName = L
"file"
;
else
_subName = prefix.Left(prefix.Length() - 1);
_totalSize = 0;
UInt64 size;
{
NCOM::CPropVariant prop;
RINOK(openVolumeCallback->GetProperty(kpidSize, &prop));
if
(prop.vt != VT_UI8)
return
E_INVALIDARG;
size = prop.uhVal.QuadPart;
}
_totalSize += size;
_sizes.Add(size);
if
(openArchiveCallback != NULL)
{
UInt64 numFiles = _streams.Size();
RINOK(openArchiveCallback->SetCompleted(&numFiles, NULL));
}
for
(;;)
{
UString fullName = seqName.GetNextName();
CMyComPtr<IInStream> nextStream;
HRESULT
result = openVolumeCallback->GetStream(fullName, &nextStream);
if
(result == S_FALSE)
break
;
if
(result != S_OK)
return
result;
if
(!stream)
break
;
{
NCOM::CPropVariant prop;
RINOK(openVolumeCallback->GetProperty(kpidSize, &prop));
if
(prop.vt != VT_UI8)
return
E_INVALIDARG;
size = prop.uhVal.QuadPart;
}
_totalSize += size;
_sizes.Add(size);
_streams.Add(nextStream);
if
(openArchiveCallback != NULL)
{
UInt64 numFiles = _streams.Size();
RINOK(openArchiveCallback->SetCompleted(&numFiles, NULL));
}
}
}
return
S_OK;
COM_TRY_END
}
STDMETHODIMP CHandler::Close()
{
_sizes.Clear();
_streams.Clear();
return
S_OK;
}
STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
{
*numItems = _streams.IsEmpty() ? 0 : 1;
return
S_OK;
}
STDMETHODIMP CHandler::GetProperty(UInt32
, PROPID propID, PROPVARIANT *value)
{
NWindows::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)-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
(
int
i = 0; i < _streams.Size(); i++)
{
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
(
int
i = 0; i < _streams.Size(); i++)
{
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
}
static
IInArchive *CreateArc() {
return
new
CHandler; }
static
CArcInfo g_ArcInfo =
{ L
"Split"
, L
"001"
, 0, 0xEA, { 0 }, 0,
false
, CreateArc, 0 };
REGISTER_ARC(Split)
}}