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