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