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