• 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 STATPROPSTG kProps[] =
23 {
24   { NULL, kpidPath, VT_BSTR},
25   { NULL, kpidSize, VT_UI8}
26 };
27 
28 STATPROPSTG kArcProps[] =
29 {
30   { NULL, kpidNumVolumes, VT_UI4}
31 };
32 
33 class 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;
42 public:
43   MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream)
44   INTERFACE_IInArchive(;)
45   STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
46 };
47 
48 IMP_IInArchive_Props
49 IMP_IInArchive_ArcProps
50 
GetArchiveProperty(PROPID propID,PROPVARIANT * value)51 STDMETHODIMP 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 
63 struct CSeqName
64 {
65   UString _unchangedPart;
66   UString _changedPart;
67   bool _splitStyle;
68 
GetNextNameNArchive::NSplit::CSeqName69   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 
Open(IInStream * stream,const UInt64 *,IArchiveOpenCallback * openArchiveCallback)137 STDMETHODIMP 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 
Close()269 STDMETHODIMP CHandler::Close()
270 {
271   _sizes.Clear();
272   _streams.Clear();
273   return S_OK;
274 }
275 
GetNumberOfItems(UInt32 * numItems)276 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
277 {
278   *numItems = _streams.IsEmpty() ? 0 : 1;
279   return S_OK;
280 }
281 
GetProperty(UInt32,PROPID propID,PROPVARIANT * value)282 STDMETHODIMP 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 
Extract(const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback)297 STDMETHODIMP 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 
GetStream(UInt32 index,ISequentialInStream ** stream)338 STDMETHODIMP 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 
CreateArc()359 static IInArchive *CreateArc() { return new CHandler; }
360 
361 static CArcInfo g_ArcInfo =
362 { L"Split", L"001", 0, 0xEA, { 0 }, 0, false, CreateArc, 0 };
363 
364 REGISTER_ARC(Split)
365 
366 }}
367