• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   UString GetNextName()
75   {
76     UString newName;
77     if (_splitStyle)
78     {
79       int i;
80       int numLetters = _changedPart.Len();
81       for (i = numLetters - 1; i >= 0; i--)
82       {
83         wchar_t c = _changedPart[i];
84         if (c == 'z')
85         {
86           newName.InsertAtFront('a');
87           continue;
88         }
89         else if (c == 'Z')
90         {
91           newName.InsertAtFront('A');
92           continue;
93         }
94         c++;
95         if ((c == 'z' || c == 'Z') && i == 0)
96         {
97           _unchangedPart += c;
98           wchar_t newChar = (c == 'z') ? L'a' : L'A';
99           newName.Empty();
100           numLetters++;
101           for (int k = 0; k < numLetters; k++)
102             newName += newChar;
103           break;
104         }
105         newName.InsertAtFront(c);
106         i--;
107         for (; i >= 0; i--)
108           newName.InsertAtFront(_changedPart[i]);
109         break;
110       }
111     }
112     else
113     {
114       int i;
115       int numLetters = _changedPart.Len();
116       for (i = numLetters - 1; i >= 0; i--)
117       {
118         wchar_t c = _changedPart[i];
119         if (c == '9')
120         {
121           newName.InsertAtFront('0');
122           if (i == 0)
123             newName.InsertAtFront('1');
124           continue;
125         }
126         c++;
127         newName.InsertAtFront(c);
128         i--;
129         for (; i >= 0; i--)
130           newName.InsertAtFront(_changedPart[i]);
131         break;
132       }
133     }
134     _changedPart = newName;
135     return _unchangedPart + _changedPart;
136   }
137 };
138 
Open2(IInStream * stream,IArchiveOpenCallback * callback)139 HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback)
140 {
141   Close();
142   if (!callback)
143     return S_FALSE;
144 
145   CMyComPtr<IArchiveOpenVolumeCallback> volumeCallback;
146   callback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&volumeCallback);
147   if (!volumeCallback)
148     return S_FALSE;
149 
150   UString name;
151   {
152     NCOM::CPropVariant prop;
153     RINOK(volumeCallback->GetProperty(kpidName, &prop));
154     if (prop.vt != VT_BSTR)
155       return S_FALSE;
156     name = prop.bstrVal;
157   }
158 
159   int dotPos = name.ReverseFind('.');
160   const UString prefix = name.Left(dotPos + 1);
161   const UString ext = name.Ptr(dotPos + 1);
162   UString ext2 = ext;
163   ext2.MakeLower_Ascii();
164 
165   CSeqName seqName;
166 
167   unsigned numLetters = 2;
168   bool splitStyle = false;
169 
170   if (ext2.Len() >= 2 && StringsAreEqual_Ascii(ext2.RightPtr(2), "aa"))
171   {
172     splitStyle = true;
173     while (numLetters < ext2.Len())
174     {
175       if (ext2[ext2.Len() - numLetters - 1] != 'a')
176         break;
177       numLetters++;
178     }
179   }
180   else if (ext.Len() >= 2 && StringsAreEqual_Ascii(ext2.RightPtr(2), "01"))
181   {
182     while (numLetters < ext2.Len())
183     {
184       if (ext2[ext2.Len() - numLetters - 1] != '0')
185         break;
186       numLetters++;
187     }
188     if (numLetters != ext.Len())
189       return S_FALSE;
190   }
191   else
192     return S_FALSE;
193 
194   seqName._unchangedPart = prefix + ext.Left(ext2.Len() - numLetters);
195   seqName._changedPart = ext.RightPtr(numLetters);
196   seqName._splitStyle = splitStyle;
197 
198   if (prefix.Len() < 1)
199     _subName = L"file";
200   else
201     _subName.SetFrom(prefix, prefix.Len() - 1);
202 
203   UInt64 size;
204   {
205     NCOM::CPropVariant prop;
206     RINOK(volumeCallback->GetProperty(kpidSize, &prop));
207     if (prop.vt != VT_UI8)
208       return E_INVALIDARG;
209     size = prop.uhVal.QuadPart;
210   }
211 
212   _totalSize += size;
213   _sizes.Add(size);
214   _streams.Add(stream);
215 
216   {
217     UInt64 numFiles = _streams.Size();
218     RINOK(callback->SetCompleted(&numFiles, NULL));
219   }
220 
221   for (;;)
222   {
223     const UString fullName = seqName.GetNextName();
224     CMyComPtr<IInStream> nextStream;
225     HRESULT result = volumeCallback->GetStream(fullName, &nextStream);
226     if (result == S_FALSE)
227       break;
228     if (result != S_OK)
229       return result;
230     if (!stream)
231       break;
232     {
233       NCOM::CPropVariant prop;
234       RINOK(volumeCallback->GetProperty(kpidSize, &prop));
235       if (prop.vt != VT_UI8)
236         return E_INVALIDARG;
237       size = prop.uhVal.QuadPart;
238     }
239     _totalSize += size;
240     _sizes.Add(size);
241     _streams.Add(nextStream);
242     {
243       UInt64 numFiles = _streams.Size();
244       RINOK(callback->SetCompleted(&numFiles, NULL));
245     }
246   }
247 
248   if (_streams.Size() == 1)
249   {
250     if (splitStyle)
251       return S_FALSE;
252   }
253   return S_OK;
254 }
255 
Open(IInStream * stream,const UInt64 *,IArchiveOpenCallback * callback)256 STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *callback)
257 {
258   COM_TRY_BEGIN
259   HRESULT res = Open2(stream, callback);
260   if (res != S_OK)
261     Close();
262   return res;
263   COM_TRY_END
264 }
265 
Close()266 STDMETHODIMP CHandler::Close()
267 {
268   _totalSize = 0;
269   _subName.Empty();
270   _streams.Clear();
271   _sizes.Clear();
272   return S_OK;
273 }
274 
GetNumberOfItems(UInt32 * numItems)275 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
276 {
277   *numItems = _streams.IsEmpty() ? 0 : 1;
278   return S_OK;
279 }
280 
GetProperty(UInt32,PROPID propID,PROPVARIANT * value)281 STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value)
282 {
283   NCOM::CPropVariant prop;
284   switch (propID)
285   {
286     case kpidPath: prop = _subName; break;
287     case kpidSize:
288     case kpidPackSize:
289       prop = _totalSize;
290       break;
291   }
292   prop.Detach(value);
293   return S_OK;
294 }
295 
Extract(const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback)296 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
297     Int32 testMode, IArchiveExtractCallback *extractCallback)
298 {
299   COM_TRY_BEGIN
300   if (numItems == 0)
301     return S_OK;
302   if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0))
303     return E_INVALIDARG;
304 
305   UInt64 currentTotalSize = 0;
306   RINOK(extractCallback->SetTotal(_totalSize));
307   CMyComPtr<ISequentialOutStream> outStream;
308   Int32 askMode = testMode ?
309       NExtract::NAskMode::kTest :
310       NExtract::NAskMode::kExtract;
311   RINOK(extractCallback->GetStream(0, &outStream, askMode));
312   if (!testMode && !outStream)
313     return S_OK;
314   RINOK(extractCallback->PrepareOperation(askMode));
315 
316   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder;
317   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
318 
319   CLocalProgress *lps = new CLocalProgress;
320   CMyComPtr<ICompressProgressInfo> progress = lps;
321   lps->Init(extractCallback, false);
322 
323   FOR_VECTOR (i, _streams)
324   {
325     lps->InSize = lps->OutSize = currentTotalSize;
326     RINOK(lps->SetCur());
327     IInStream *inStream = _streams[i];
328     RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL));
329     RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress));
330     currentTotalSize += copyCoderSpec->TotalSize;
331   }
332   outStream.Release();
333   return extractCallback->SetOperationResult(NExtract::NOperationResult::kOK);
334   COM_TRY_END
335 }
336 
GetStream(UInt32 index,ISequentialInStream ** stream)337 STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
338 {
339   COM_TRY_BEGIN
340   if (index != 0)
341     return E_INVALIDARG;
342   *stream = 0;
343   CMultiStream *streamSpec = new CMultiStream;
344   CMyComPtr<ISequentialInStream> streamTemp = streamSpec;
345   FOR_VECTOR (i, _streams)
346   {
347     CMultiStream::CSubStreamInfo subStreamInfo;
348     subStreamInfo.Stream = _streams[i];
349     subStreamInfo.Size = _sizes[i];
350     streamSpec->Streams.Add(subStreamInfo);
351   }
352   streamSpec->Init();
353   *stream = streamTemp.Detach();
354   return S_OK;
355   COM_TRY_END
356 }
357 
358 IMP_CreateArcIn
359 
360 static CArcInfo g_ArcInfo =
361   { "Split", "001", 0, 0xEA,
362   0, { 0 },
363   0,
364   0,
365   CreateArc };
366 
367 REGISTER_ARC(Split)
368 
369 }}
370