1// SplitHandler.cpp
2
3#include "StdAfx.h"
4
5#include "Common/ComTry.h"
6#include "Common/MyString.h"
7
8#include "Windows/PropVariant.h"
9
10#include "../Common/ProgressUtils.h"
11#include "../Common/RegisterArc.h"
12
13#include "../Compress/CopyCoder.h"
14
15#include "Common/MultiStream.h"
16
17using namespace NWindows;
18
19namespace NArchive {
20namespace NSplit {
21
22STATPROPSTG kProps[] =
23{
24  { NULL, kpidPath, VT_BSTR},
25  { NULL, kpidSize, VT_UI8}
26};
27
28STATPROPSTG kArcProps[] =
29{
30  { NULL, kpidNumVolumes, VT_UI4}
31};
32
33class CHandler:
34  public IInArchive,
35  public IInArchiveGetStream,
36  public CMyUnknownImp
37{
38  UString _subName;
39  CObjectVector<CMyComPtr<IInStream> > _streams;
40  CRecordVector<UInt64> _sizes;
41  UInt64 _totalSize;
42public:
43  MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream)
44  INTERFACE_IInArchive(;)
45  STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
46};
47
48IMP_IInArchive_Props
49IMP_IInArchive_ArcProps
50
51STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
52{
53  NCOM::CPropVariant prop;
54  switch(propID)
55  {
56    case kpidMainSubfile: prop = (UInt32)0; break;
57    case kpidNumVolumes: prop = (UInt32)_streams.Size(); break;
58  }
59  prop.Detach(value);
60  return S_OK;
61}
62
63struct CSeqName
64{
65  UString _unchangedPart;
66  UString _changedPart;
67  bool _splitStyle;
68
69  UString GetNextName()
70  {
71    UString newName;
72    if (_splitStyle)
73    {
74      int i;
75      int numLetters = _changedPart.Length();
76      for (i = numLetters - 1; i >= 0; i--)
77      {
78        wchar_t c = _changedPart[i];
79        if (c == 'z')
80        {
81          c = 'a';
82          newName = c + newName;
83          continue;
84        }
85        else if (c == 'Z')
86        {
87          c = 'A';
88          newName = c + newName;
89          continue;
90        }
91        c++;
92        if ((c == 'z' || c == 'Z') && i == 0)
93        {
94          _unchangedPart += c;
95          wchar_t newChar = (c == 'z') ? L'a' : L'A';
96          newName.Empty();
97          numLetters++;
98          for (int k = 0; k < numLetters; k++)
99            newName += newChar;
100          break;
101        }
102        newName = c + newName;
103        i--;
104        for (; i >= 0; i--)
105          newName = _changedPart[i] + newName;
106        break;
107      }
108    }
109    else
110    {
111      int i;
112      int numLetters = _changedPart.Length();
113      for (i = numLetters - 1; i >= 0; i--)
114      {
115        wchar_t c = _changedPart[i];
116        if (c == L'9')
117        {
118          c = L'0';
119          newName = c + newName;
120          if (i == 0)
121            newName = UString(L'1') + newName;
122          continue;
123        }
124        c++;
125        newName = c + newName;
126        i--;
127        for (; i >= 0; i--)
128          newName = _changedPart[i] + newName;
129        break;
130      }
131    }
132    _changedPart = newName;
133    return _unchangedPart + _changedPart;
134  }
135};
136
137STDMETHODIMP CHandler::Open(IInStream *stream,
138    const UInt64 * /* maxCheckStartPosition */,
139    IArchiveOpenCallback *openArchiveCallback)
140{
141  COM_TRY_BEGIN
142  Close();
143  if (openArchiveCallback == 0)
144    return S_FALSE;
145  // try
146  {
147    CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback;
148    CMyComPtr<IArchiveOpenCallback> openArchiveCallbackWrap = openArchiveCallback;
149    if (openArchiveCallbackWrap.QueryInterface(IID_IArchiveOpenVolumeCallback,
150        &openVolumeCallback) != S_OK)
151      return S_FALSE;
152
153    UString name;
154    {
155      NCOM::CPropVariant prop;
156      RINOK(openVolumeCallback->GetProperty(kpidName, &prop));
157      if (prop.vt != VT_BSTR)
158        return S_FALSE;
159      name = prop.bstrVal;
160    }
161
162    int dotPos = name.ReverseFind('.');
163    UString prefix, ext;
164    if (dotPos >= 0)
165    {
166      prefix = name.Left(dotPos + 1);
167      ext = name.Mid(dotPos + 1);
168    }
169    else
170      ext = name;
171    UString extBig = ext;
172    extBig.MakeUpper();
173
174    CSeqName seqName;
175
176    int numLetters = 2;
177    bool splitStyle = false;
178    if (extBig.Right(2) == L"AA")
179    {
180      splitStyle = true;
181      while (numLetters < extBig.Length())
182      {
183        if (extBig[extBig.Length() - numLetters - 1] != 'A')
184          break;
185        numLetters++;
186      }
187    }
188    else if (ext.Right(2) == L"01")
189    {
190      while (numLetters < extBig.Length())
191      {
192        if (extBig[extBig.Length() - numLetters - 1] != '0')
193          break;
194        numLetters++;
195      }
196      if (numLetters != ext.Length())
197        return S_FALSE;
198    }
199    else
200      return S_FALSE;
201
202    _streams.Add(stream);
203
204    seqName._unchangedPart = prefix + ext.Left(extBig.Length() - numLetters);
205    seqName._changedPart = ext.Right(numLetters);
206    seqName._splitStyle = splitStyle;
207
208    if (prefix.Length() < 1)
209      _subName = L"file";
210    else
211      _subName = prefix.Left(prefix.Length() - 1);
212
213    _totalSize = 0;
214    UInt64 size;
215    {
216      NCOM::CPropVariant prop;
217      RINOK(openVolumeCallback->GetProperty(kpidSize, &prop));
218      if (prop.vt != VT_UI8)
219        return E_INVALIDARG;
220      size = prop.uhVal.QuadPart;
221    }
222    _totalSize += size;
223    _sizes.Add(size);
224
225    if (openArchiveCallback != NULL)
226    {
227      UInt64 numFiles = _streams.Size();
228      RINOK(openArchiveCallback->SetCompleted(&numFiles, NULL));
229    }
230
231    for (;;)
232    {
233      UString fullName = seqName.GetNextName();
234      CMyComPtr<IInStream> nextStream;
235      HRESULT result = openVolumeCallback->GetStream(fullName, &nextStream);
236      if (result == S_FALSE)
237        break;
238      if (result != S_OK)
239        return result;
240      if (!stream)
241        break;
242      {
243        NCOM::CPropVariant prop;
244        RINOK(openVolumeCallback->GetProperty(kpidSize, &prop));
245        if (prop.vt != VT_UI8)
246          return E_INVALIDARG;
247        size = prop.uhVal.QuadPart;
248      }
249      _totalSize += size;
250      _sizes.Add(size);
251      _streams.Add(nextStream);
252      if (openArchiveCallback != NULL)
253      {
254        UInt64 numFiles = _streams.Size();
255        RINOK(openArchiveCallback->SetCompleted(&numFiles, NULL));
256      }
257    }
258  }
259  /*
260  catch(...)
261  {
262    return S_FALSE;
263  }
264  */
265  return S_OK;
266  COM_TRY_END
267}
268
269STDMETHODIMP CHandler::Close()
270{
271  _sizes.Clear();
272  _streams.Clear();
273  return S_OK;
274}
275
276STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
277{
278  *numItems = _streams.IsEmpty() ? 0 : 1;
279  return S_OK;
280}
281
282STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value)
283{
284  NWindows::NCOM::CPropVariant prop;
285  switch(propID)
286  {
287    case kpidPath: prop = _subName; break;
288    case kpidSize:
289    case kpidPackSize:
290      prop = _totalSize;
291      break;
292  }
293  prop.Detach(value);
294  return S_OK;
295}
296
297STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
298    Int32 testMode, IArchiveExtractCallback *extractCallback)
299{
300  COM_TRY_BEGIN
301  if (numItems == 0)
302    return S_OK;
303  if (numItems != (UInt32)-1 && (numItems != 1 || indices[0] != 0))
304    return E_INVALIDARG;
305
306  UInt64 currentTotalSize = 0;
307  RINOK(extractCallback->SetTotal(_totalSize));
308  CMyComPtr<ISequentialOutStream> outStream;
309  Int32 askMode = testMode ?
310      NExtract::NAskMode::kTest :
311      NExtract::NAskMode::kExtract;
312  RINOK(extractCallback->GetStream(0, &outStream, askMode));
313  if (!testMode && !outStream)
314    return S_OK;
315  RINOK(extractCallback->PrepareOperation(askMode));
316
317  NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder;
318  CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
319
320  CLocalProgress *lps = new CLocalProgress;
321  CMyComPtr<ICompressProgressInfo> progress = lps;
322  lps->Init(extractCallback, false);
323
324  for (int i = 0; i < _streams.Size(); i++)
325  {
326    lps->InSize = lps->OutSize = currentTotalSize;
327    RINOK(lps->SetCur());
328    IInStream *inStream = _streams[i];
329    RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL));
330    RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress));
331    currentTotalSize += copyCoderSpec->TotalSize;
332  }
333  outStream.Release();
334  return extractCallback->SetOperationResult(NExtract::NOperationResult::kOK);
335  COM_TRY_END
336}
337
338STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
339{
340  COM_TRY_BEGIN
341  if (index != 0)
342    return E_INVALIDARG;
343  *stream = 0;
344  CMultiStream *streamSpec = new CMultiStream;
345  CMyComPtr<ISequentialInStream> streamTemp = streamSpec;
346  for (int i = 0; i < _streams.Size(); i++)
347  {
348    CMultiStream::CSubStreamInfo subStreamInfo;
349    subStreamInfo.Stream = _streams[i];
350    subStreamInfo.Size = _sizes[i];
351    streamSpec->Streams.Add(subStreamInfo);
352  }
353  streamSpec->Init();
354  *stream = streamTemp.Detach();
355  return S_OK;
356  COM_TRY_END
357}
358
359static IInArchive *CreateArc() { return new CHandler; }
360
361static CArcInfo g_ArcInfo =
362{ L"Split", L"001", 0, 0xEA, { 0 }, 0, false, CreateArc, 0 };
363
364REGISTER_ARC(Split)
365
366}}
367