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