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