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 STATPROPSTG kProps[] = 23 { 24 { NULL, kpidPath, VT_BSTR}, 25 { NULL, kpidSize, VT_UI8} 26 }; 27 28 STATPROPSTG kArcProps[] = 29 { 30 { NULL, kpidNumVolumes, VT_UI4} 31 }; 32 33 class CHandler: 34 public IInArchive, 35 public IInArchiveGetStream, 36 public CMyUnknownImp 37 { 38 UString _subName; 39 CObjectVector<CMyComPtr<IInStream> > _streams; 40 CRecordVector<UInt64> _sizes; 41 UInt64 _totalSize; 42 public: 43 MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) 44 INTERFACE_IInArchive(;) 45 STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); 46 }; 47 48 IMP_IInArchive_Props 49 IMP_IInArchive_ArcProps 50 51 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) 52 { 53 NCOM::CPropVariant prop; 54 switch(propID) 55 { 56 case kpidMainSubfile: prop = (UInt32)0; break; 57 case kpidNumVolumes: prop = (UInt32)_streams.Size(); break; 58 } 59 prop.Detach(value); 60 return S_OK; 61 } 62 63 struct CSeqName 64 { 65 UString _unchangedPart; 66 UString _changedPart; 67 bool _splitStyle; 68 69 UString GetNextName() 70 { 71 UString newName; 72 if (_splitStyle) 73 { 74 int i; 75 int numLetters = _changedPart.Length(); 76 for (i = numLetters - 1; i >= 0; i--) 77 { 78 wchar_t c = _changedPart[i]; 79 if (c == 'z') 80 { 81 c = 'a'; 82 newName = c + newName; 83 continue; 84 } 85 else if (c == 'Z') 86 { 87 c = 'A'; 88 newName = c + newName; 89 continue; 90 } 91 c++; 92 if ((c == 'z' || c == 'Z') && i == 0) 93 { 94 _unchangedPart += c; 95 wchar_t newChar = (c == 'z') ? L'a' : L'A'; 96 newName.Empty(); 97 numLetters++; 98 for (int k = 0; k < numLetters; k++) 99 newName += newChar; 100 break; 101 } 102 newName = c + newName; 103 i--; 104 for (; i >= 0; i--) 105 newName = _changedPart[i] + newName; 106 break; 107 } 108 } 109 else 110 { 111 int i; 112 int numLetters = _changedPart.Length(); 113 for (i = numLetters - 1; i >= 0; i--) 114 { 115 wchar_t c = _changedPart[i]; 116 if (c == L'9') 117 { 118 c = L'0'; 119 newName = c + newName; 120 if (i == 0) 121 newName = UString(L'1') + newName; 122 continue; 123 } 124 c++; 125 newName = c + newName; 126 i--; 127 for (; i >= 0; i--) 128 newName = _changedPart[i] + newName; 129 break; 130 } 131 } 132 _changedPart = newName; 133 return _unchangedPart + _changedPart; 134 } 135 }; 136 137 STDMETHODIMP CHandler::Open(IInStream *stream, 138 const UInt64 * /* maxCheckStartPosition */, 139 IArchiveOpenCallback *openArchiveCallback) 140 { 141 COM_TRY_BEGIN 142 Close(); 143 if (openArchiveCallback == 0) 144 return S_FALSE; 145 // try 146 { 147 CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback; 148 CMyComPtr<IArchiveOpenCallback> openArchiveCallbackWrap = openArchiveCallback; 149 if (openArchiveCallbackWrap.QueryInterface(IID_IArchiveOpenVolumeCallback, 150 &openVolumeCallback) != S_OK) 151 return S_FALSE; 152 153 UString name; 154 { 155 NCOM::CPropVariant prop; 156 RINOK(openVolumeCallback->GetProperty(kpidName, &prop)); 157 if (prop.vt != VT_BSTR) 158 return S_FALSE; 159 name = prop.bstrVal; 160 } 161 162 int dotPos = name.ReverseFind('.'); 163 UString prefix, ext; 164 if (dotPos >= 0) 165 { 166 prefix = name.Left(dotPos + 1); 167 ext = name.Mid(dotPos + 1); 168 } 169 else 170 ext = name; 171 UString extBig = ext; 172 extBig.MakeUpper(); 173 174 CSeqName seqName; 175 176 int numLetters = 2; 177 bool splitStyle = false; 178 if (extBig.Right(2) == L"AA") 179 { 180 splitStyle = true; 181 while (numLetters < extBig.Length()) 182 { 183 if (extBig[extBig.Length() - numLetters - 1] != 'A') 184 break; 185 numLetters++; 186 } 187 } 188 else if (ext.Right(2) == L"01") 189 { 190 while (numLetters < extBig.Length()) 191 { 192 if (extBig[extBig.Length() - numLetters - 1] != '0') 193 break; 194 numLetters++; 195 } 196 if (numLetters != ext.Length()) 197 return S_FALSE; 198 } 199 else 200 return S_FALSE; 201 202 _streams.Add(stream); 203 204 seqName._unchangedPart = prefix + ext.Left(extBig.Length() - numLetters); 205 seqName._changedPart = ext.Right(numLetters); 206 seqName._splitStyle = splitStyle; 207 208 if (prefix.Length() < 1) 209 _subName = L"file"; 210 else 211 _subName = prefix.Left(prefix.Length() - 1); 212 213 _totalSize = 0; 214 UInt64 size; 215 { 216 NCOM::CPropVariant prop; 217 RINOK(openVolumeCallback->GetProperty(kpidSize, &prop)); 218 if (prop.vt != VT_UI8) 219 return E_INVALIDARG; 220 size = prop.uhVal.QuadPart; 221 } 222 _totalSize += size; 223 _sizes.Add(size); 224 225 if (openArchiveCallback != NULL) 226 { 227 UInt64 numFiles = _streams.Size(); 228 RINOK(openArchiveCallback->SetCompleted(&numFiles, NULL)); 229 } 230 231 for (;;) 232 { 233 UString fullName = seqName.GetNextName(); 234 CMyComPtr<IInStream> nextStream; 235 HRESULT result = openVolumeCallback->GetStream(fullName, &nextStream); 236 if (result == S_FALSE) 237 break; 238 if (result != S_OK) 239 return result; 240 if (!stream) 241 break; 242 { 243 NCOM::CPropVariant prop; 244 RINOK(openVolumeCallback->GetProperty(kpidSize, &prop)); 245 if (prop.vt != VT_UI8) 246 return E_INVALIDARG; 247 size = prop.uhVal.QuadPart; 248 } 249 _totalSize += size; 250 _sizes.Add(size); 251 _streams.Add(nextStream); 252 if (openArchiveCallback != NULL) 253 { 254 UInt64 numFiles = _streams.Size(); 255 RINOK(openArchiveCallback->SetCompleted(&numFiles, NULL)); 256 } 257 } 258 } 259 /* 260 catch(...) 261 { 262 return S_FALSE; 263 } 264 */ 265 return S_OK; 266 COM_TRY_END 267 } 268 269 STDMETHODIMP CHandler::Close() 270 { 271 _sizes.Clear(); 272 _streams.Clear(); 273 return S_OK; 274 } 275 276 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) 277 { 278 *numItems = _streams.IsEmpty() ? 0 : 1; 279 return S_OK; 280 } 281 282 STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value) 283 { 284 NWindows::NCOM::CPropVariant prop; 285 switch(propID) 286 { 287 case kpidPath: prop = _subName; break; 288 case kpidSize: 289 case kpidPackSize: 290 prop = _totalSize; 291 break; 292 } 293 prop.Detach(value); 294 return S_OK; 295 } 296 297 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, 298 Int32 testMode, IArchiveExtractCallback *extractCallback) 299 { 300 COM_TRY_BEGIN 301 if (numItems == 0) 302 return S_OK; 303 if (numItems != (UInt32)-1 && (numItems != 1 || indices[0] != 0)) 304 return E_INVALIDARG; 305 306 UInt64 currentTotalSize = 0; 307 RINOK(extractCallback->SetTotal(_totalSize)); 308 CMyComPtr<ISequentialOutStream> outStream; 309 Int32 askMode = testMode ? 310 NExtract::NAskMode::kTest : 311 NExtract::NAskMode::kExtract; 312 RINOK(extractCallback->GetStream(0, &outStream, askMode)); 313 if (!testMode && !outStream) 314 return S_OK; 315 RINOK(extractCallback->PrepareOperation(askMode)); 316 317 NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder; 318 CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; 319 320 CLocalProgress *lps = new CLocalProgress; 321 CMyComPtr<ICompressProgressInfo> progress = lps; 322 lps->Init(extractCallback, false); 323 324 for (int i = 0; i < _streams.Size(); i++) 325 { 326 lps->InSize = lps->OutSize = currentTotalSize; 327 RINOK(lps->SetCur()); 328 IInStream *inStream = _streams[i]; 329 RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL)); 330 RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress)); 331 currentTotalSize += copyCoderSpec->TotalSize; 332 } 333 outStream.Release(); 334 return extractCallback->SetOperationResult(NExtract::NOperationResult::kOK); 335 COM_TRY_END 336 } 337 338 STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) 339 { 340 COM_TRY_BEGIN 341 if (index != 0) 342 return E_INVALIDARG; 343 *stream = 0; 344 CMultiStream *streamSpec = new CMultiStream; 345 CMyComPtr<ISequentialInStream> streamTemp = streamSpec; 346 for (int i = 0; i < _streams.Size(); i++) 347 { 348 CMultiStream::CSubStreamInfo subStreamInfo; 349 subStreamInfo.Stream = _streams[i]; 350 subStreamInfo.Size = _sizes[i]; 351 streamSpec->Streams.Add(subStreamInfo); 352 } 353 streamSpec->Init(); 354 *stream = streamTemp.Detach(); 355 return S_OK; 356 COM_TRY_END 357 } 358 359 static IInArchive *CreateArc() { return new CHandler; } 360 361 static CArcInfo g_ArcInfo = 362 { L"Split", L"001", 0, 0xEA, { 0 }, 0, false, CreateArc, 0 }; 363 364 REGISTER_ARC(Split) 365 366 }} 367