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