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 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 74 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 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 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 261 STDMETHODIMP CHandler::Close() 262 { 263 _totalSize = 0; 264 _subName.Empty(); 265 _streams.Clear(); 266 _sizes.Clear(); 267 return S_OK; 268 } 269 270 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) 271 { 272 *numItems = _streams.IsEmpty() ? 0 : 1; 273 return S_OK; 274 } 275 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 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 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