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