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