1 // 7zHandlerOut.cpp 2 3 #include "StdAfx.h" 4 5 #include "../../../Common/ComTry.h" 6 #include "../../../Common/StringToInt.h" 7 #include "../../../Common/Wildcard.h" 8 9 #include "../Common/ItemNameUtils.h" 10 #include "../Common/ParseProperties.h" 11 12 #include "7zHandler.h" 13 #include "7zOut.h" 14 #include "7zUpdate.h" 15 16 using namespace NWindows; 17 18 namespace NArchive { 19 namespace N7z { 20 21 static const char *k_LZMA_Name = "LZMA"; 22 static const char *kDefaultMethodName = "LZMA2"; 23 static const char *k_Copy_Name = "Copy"; 24 25 static const char *k_MatchFinder_ForHeaders = "BT2"; 26 static const UInt32 k_NumFastBytes_ForHeaders = 273; 27 static const UInt32 k_Level_ForHeaders = 5; 28 static const UInt32 k_Dictionary_ForHeaders = 29 #ifdef UNDER_CE 30 1 << 18; 31 #else 32 1 << 20; 33 #endif 34 35 STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type) 36 { 37 *type = NFileTimeType::kWindows; 38 return S_OK; 39 } 40 41 HRESULT CHandler::PropsMethod_To_FullMethod(CMethodFull &dest, const COneMethodInfo &m) 42 { 43 if (!FindMethod( 44 EXTERNAL_CODECS_VARS 45 m.MethodName, dest.Id, dest.NumStreams)) 46 return E_INVALIDARG; 47 (CProps &)dest = (CProps &)m; 48 return S_OK; 49 } 50 51 HRESULT CHandler::SetHeaderMethod(CCompressionMethodMode &headerMethod) 52 { 53 if (!_compressHeaders) 54 return S_OK; 55 COneMethodInfo m; 56 m.MethodName = k_LZMA_Name; 57 m.AddProp_Ascii(NCoderPropID::kMatchFinder, k_MatchFinder_ForHeaders); 58 m.AddProp_Level(k_Level_ForHeaders); 59 m.AddProp32(NCoderPropID::kNumFastBytes, k_NumFastBytes_ForHeaders); 60 m.AddProp32(NCoderPropID::kDictionarySize, k_Dictionary_ForHeaders); 61 m.AddProp_NumThreads(1); 62 63 CMethodFull &methodFull = headerMethod.Methods.AddNew(); 64 return PropsMethod_To_FullMethod(methodFull, m); 65 } 66 67 HRESULT CHandler::SetMainMethod( 68 CCompressionMethodMode &methodMode 69 #ifndef _7ZIP_ST 70 , UInt32 numThreads 71 #endif 72 ) 73 { 74 methodMode.Bonds = _bonds; 75 76 CObjectVector<COneMethodInfo> methods = _methods; 77 78 { 79 FOR_VECTOR (i, methods) 80 { 81 AString &methodName = methods[i].MethodName; 82 if (methodName.IsEmpty()) 83 methodName = kDefaultMethodName; 84 } 85 if (methods.IsEmpty()) 86 { 87 COneMethodInfo &m = methods.AddNew(); 88 m.MethodName = (GetLevel() == 0 ? k_Copy_Name : kDefaultMethodName); 89 methodMode.DefaultMethod_was_Inserted = true; 90 } 91 } 92 93 if (!_filterMethod.MethodName.IsEmpty()) 94 { 95 // if (methodMode.Bonds.IsEmpty()) 96 { 97 FOR_VECTOR (k, methodMode.Bonds) 98 { 99 CBond2 &bond = methodMode.Bonds[k]; 100 bond.InCoder++; 101 bond.OutCoder++; 102 } 103 methods.Insert(0, _filterMethod); 104 methodMode.Filter_was_Inserted = true; 105 } 106 } 107 108 const UInt64 kSolidBytes_Min = (1 << 24); 109 const UInt64 kSolidBytes_Max = ((UInt64)1 << 32) - 1; 110 111 bool needSolid = false; 112 113 FOR_VECTOR (i, methods) 114 { 115 COneMethodInfo &oneMethodInfo = methods[i]; 116 SetGlobalLevelAndThreads(oneMethodInfo 117 #ifndef _7ZIP_ST 118 , numThreads 119 #endif 120 ); 121 122 CMethodFull &methodFull = methodMode.Methods.AddNew(); 123 RINOK(PropsMethod_To_FullMethod(methodFull, oneMethodInfo)); 124 125 if (methodFull.Id != k_Copy) 126 needSolid = true; 127 128 if (_numSolidBytesDefined) 129 continue; 130 131 UInt32 dicSize; 132 switch (methodFull.Id) 133 { 134 case k_LZMA: 135 case k_LZMA2: dicSize = oneMethodInfo.Get_Lzma_DicSize(); break; 136 case k_PPMD: dicSize = oneMethodInfo.Get_Ppmd_MemSize(); break; 137 case k_Deflate: dicSize = (UInt32)1 << 15; break; 138 case k_BZip2: dicSize = oneMethodInfo.Get_BZip2_BlockSize(); break; 139 default: continue; 140 } 141 142 _numSolidBytes = (UInt64)dicSize << 7; 143 if (_numSolidBytes < kSolidBytes_Min) _numSolidBytes = kSolidBytes_Min; 144 if (_numSolidBytes > kSolidBytes_Max) _numSolidBytes = kSolidBytes_Max; 145 _numSolidBytesDefined = true; 146 } 147 148 if (!_numSolidBytesDefined) 149 if (needSolid) 150 _numSolidBytes = kSolidBytes_Max; 151 else 152 _numSolidBytes = 0; 153 _numSolidBytesDefined = true; 154 return S_OK; 155 } 156 157 static HRESULT GetTime(IArchiveUpdateCallback *updateCallback, int index, PROPID propID, UInt64 &ft, bool &ftDefined) 158 { 159 // ft = 0; 160 // ftDefined = false; 161 NCOM::CPropVariant prop; 162 RINOK(updateCallback->GetProperty(index, propID, &prop)); 163 if (prop.vt == VT_FILETIME) 164 { 165 ft = prop.filetime.dwLowDateTime | ((UInt64)prop.filetime.dwHighDateTime << 32); 166 ftDefined = true; 167 } 168 else if (prop.vt != VT_EMPTY) 169 return E_INVALIDARG; 170 else 171 { 172 ft = 0; 173 ftDefined = false; 174 } 175 return S_OK; 176 } 177 178 /* 179 180 #ifdef _WIN32 181 static const wchar_t kDirDelimiter1 = L'\\'; 182 #endif 183 static const wchar_t kDirDelimiter2 = L'/'; 184 185 static inline bool IsCharDirLimiter(wchar_t c) 186 { 187 return ( 188 #ifdef _WIN32 189 c == kDirDelimiter1 || 190 #endif 191 c == kDirDelimiter2); 192 } 193 194 static int FillSortIndex(CObjectVector<CTreeFolder> &treeFolders, int cur, int curSortIndex) 195 { 196 CTreeFolder &tf = treeFolders[cur]; 197 tf.SortIndex = curSortIndex++; 198 for (int i = 0; i < tf.SubFolders.Size(); i++) 199 curSortIndex = FillSortIndex(treeFolders, tf.SubFolders[i], curSortIndex); 200 tf.SortIndexEnd = curSortIndex; 201 return curSortIndex; 202 } 203 204 static int FindSubFolder(const CObjectVector<CTreeFolder> &treeFolders, int cur, const UString &name, int &insertPos) 205 { 206 const CIntVector &subFolders = treeFolders[cur].SubFolders; 207 int left = 0, right = subFolders.Size(); 208 insertPos = -1; 209 for (;;) 210 { 211 if (left == right) 212 { 213 insertPos = left; 214 return -1; 215 } 216 int mid = (left + right) / 2; 217 int midFolder = subFolders[mid]; 218 int compare = CompareFileNames(name, treeFolders[midFolder].Name); 219 if (compare == 0) 220 return midFolder; 221 if (compare < 0) 222 right = mid; 223 else 224 left = mid + 1; 225 } 226 } 227 228 static int AddFolder(CObjectVector<CTreeFolder> &treeFolders, int cur, const UString &name) 229 { 230 int insertPos; 231 int folderIndex = FindSubFolder(treeFolders, cur, name, insertPos); 232 if (folderIndex < 0) 233 { 234 folderIndex = treeFolders.Size(); 235 CTreeFolder &newFolder = treeFolders.AddNew(); 236 newFolder.Parent = cur; 237 newFolder.Name = name; 238 treeFolders[cur].SubFolders.Insert(insertPos, folderIndex); 239 } 240 // else if (treeFolders[folderIndex].IsAltStreamFolder != isAltStreamFolder) throw 1123234234; 241 return folderIndex; 242 } 243 */ 244 245 STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems, 246 IArchiveUpdateCallback *updateCallback) 247 { 248 COM_TRY_BEGIN 249 250 const CDbEx *db = 0; 251 #ifdef _7Z_VOL 252 if (_volumes.Size() > 1) 253 return E_FAIL; 254 const CVolume *volume = 0; 255 if (_volumes.Size() == 1) 256 { 257 volume = &_volumes.Front(); 258 db = &volume->Database; 259 } 260 #else 261 if (_inStream != 0) 262 db = &_db; 263 #endif 264 265 /* 266 CMyComPtr<IArchiveGetRawProps> getRawProps; 267 updateCallback->QueryInterface(IID_IArchiveGetRawProps, (void **)&getRawProps); 268 269 CUniqBlocks secureBlocks; 270 secureBlocks.AddUniq(NULL, 0); 271 272 CObjectVector<CTreeFolder> treeFolders; 273 { 274 CTreeFolder folder; 275 folder.Parent = -1; 276 treeFolders.Add(folder); 277 } 278 */ 279 280 CObjectVector<CUpdateItem> updateItems; 281 282 bool need_CTime = (Write_CTime.Def && Write_CTime.Val); 283 bool need_ATime = (Write_ATime.Def && Write_ATime.Val); 284 bool need_MTime = (Write_MTime.Def && Write_MTime.Val || !Write_MTime.Def); 285 286 if (db && !db->Files.IsEmpty()) 287 { 288 if (!Write_CTime.Def) need_CTime = !db->CTime.Defs.IsEmpty(); 289 if (!Write_ATime.Def) need_ATime = !db->ATime.Defs.IsEmpty(); 290 if (!Write_MTime.Def) need_MTime = !db->MTime.Defs.IsEmpty(); 291 } 292 293 UString s; 294 295 for (UInt32 i = 0; i < numItems; i++) 296 { 297 Int32 newData, newProps; 298 UInt32 indexInArchive; 299 if (!updateCallback) 300 return E_FAIL; 301 RINOK(updateCallback->GetUpdateItemInfo(i, &newData, &newProps, &indexInArchive)); 302 CUpdateItem ui; 303 ui.NewProps = IntToBool(newProps); 304 ui.NewData = IntToBool(newData); 305 ui.IndexInArchive = indexInArchive; 306 ui.IndexInClient = i; 307 ui.IsAnti = false; 308 ui.Size = 0; 309 310 UString name; 311 // bool isAltStream = false; 312 if (ui.IndexInArchive != -1) 313 { 314 if (db == 0 || (unsigned)ui.IndexInArchive >= db->Files.Size()) 315 return E_INVALIDARG; 316 const CFileItem &fi = db->Files[ui.IndexInArchive]; 317 if (!ui.NewProps) 318 { 319 _db.GetPath(ui.IndexInArchive, name); 320 } 321 ui.IsDir = fi.IsDir; 322 ui.Size = fi.Size; 323 // isAltStream = fi.IsAltStream; 324 ui.IsAnti = db->IsItemAnti(ui.IndexInArchive); 325 326 if (!ui.NewProps) 327 { 328 ui.CTimeDefined = db->CTime.GetItem(ui.IndexInArchive, ui.CTime); 329 ui.ATimeDefined = db->ATime.GetItem(ui.IndexInArchive, ui.ATime); 330 ui.MTimeDefined = db->MTime.GetItem(ui.IndexInArchive, ui.MTime); 331 } 332 } 333 334 if (ui.NewProps) 335 { 336 bool folderStatusIsDefined; 337 { 338 NCOM::CPropVariant prop; 339 RINOK(updateCallback->GetProperty(i, kpidAttrib, &prop)); 340 if (prop.vt == VT_EMPTY) 341 ui.AttribDefined = false; 342 else if (prop.vt != VT_UI4) 343 return E_INVALIDARG; 344 else 345 { 346 ui.Attrib = prop.ulVal; 347 ui.AttribDefined = true; 348 } 349 } 350 351 // we need MTime to sort files. 352 if (need_CTime) RINOK(GetTime(updateCallback, i, kpidCTime, ui.CTime, ui.CTimeDefined)); 353 if (need_ATime) RINOK(GetTime(updateCallback, i, kpidATime, ui.ATime, ui.ATimeDefined)); 354 if (need_MTime) RINOK(GetTime(updateCallback, i, kpidMTime, ui.MTime, ui.MTimeDefined)); 355 356 /* 357 if (getRawProps) 358 { 359 const void *data; 360 UInt32 dataSize; 361 UInt32 propType; 362 363 getRawProps->GetRawProp(i, kpidNtSecure, &data, &dataSize, &propType); 364 if (dataSize != 0 && propType != NPropDataType::kRaw) 365 return E_FAIL; 366 ui.SecureIndex = secureBlocks.AddUniq((const Byte *)data, dataSize); 367 } 368 */ 369 370 { 371 NCOM::CPropVariant prop; 372 RINOK(updateCallback->GetProperty(i, kpidPath, &prop)); 373 if (prop.vt == VT_EMPTY) 374 { 375 } 376 else if (prop.vt != VT_BSTR) 377 return E_INVALIDARG; 378 else 379 { 380 name = NItemName::MakeLegalName(prop.bstrVal); 381 } 382 } 383 { 384 NCOM::CPropVariant prop; 385 RINOK(updateCallback->GetProperty(i, kpidIsDir, &prop)); 386 if (prop.vt == VT_EMPTY) 387 folderStatusIsDefined = false; 388 else if (prop.vt != VT_BOOL) 389 return E_INVALIDARG; 390 else 391 { 392 ui.IsDir = (prop.boolVal != VARIANT_FALSE); 393 folderStatusIsDefined = true; 394 } 395 } 396 397 { 398 NCOM::CPropVariant prop; 399 RINOK(updateCallback->GetProperty(i, kpidIsAnti, &prop)); 400 if (prop.vt == VT_EMPTY) 401 ui.IsAnti = false; 402 else if (prop.vt != VT_BOOL) 403 return E_INVALIDARG; 404 else 405 ui.IsAnti = (prop.boolVal != VARIANT_FALSE); 406 } 407 408 /* 409 { 410 NCOM::CPropVariant prop; 411 RINOK(updateCallback->GetProperty(i, kpidIsAltStream, &prop)); 412 if (prop.vt == VT_EMPTY) 413 isAltStream = false; 414 else if (prop.vt != VT_BOOL) 415 return E_INVALIDARG; 416 else 417 isAltStream = (prop.boolVal != VARIANT_FALSE); 418 } 419 */ 420 421 if (ui.IsAnti) 422 { 423 ui.AttribDefined = false; 424 425 ui.CTimeDefined = false; 426 ui.ATimeDefined = false; 427 ui.MTimeDefined = false; 428 429 ui.Size = 0; 430 } 431 432 if (!folderStatusIsDefined && ui.AttribDefined) 433 ui.SetDirStatusFromAttrib(); 434 } 435 else 436 { 437 /* 438 if (_db.SecureIDs.IsEmpty()) 439 ui.SecureIndex = secureBlocks.AddUniq(NULL, 0); 440 else 441 { 442 int id = _db.SecureIDs[ui.IndexInArchive]; 443 size_t offs = _db.SecureOffsets[id]; 444 size_t size = _db.SecureOffsets[id + 1] - offs; 445 ui.SecureIndex = secureBlocks.AddUniq(_db.SecureBuf + offs, size); 446 } 447 */ 448 } 449 450 /* 451 { 452 int folderIndex = 0; 453 if (_useParents) 454 { 455 int j; 456 s.Empty(); 457 for (j = 0; j < name.Len(); j++) 458 { 459 wchar_t c = name[j]; 460 if (IsCharDirLimiter(c)) 461 { 462 folderIndex = AddFolder(treeFolders, folderIndex, s); 463 s.Empty(); 464 continue; 465 } 466 s += c; 467 } 468 if (isAltStream) 469 { 470 int colonPos = s.Find(':'); 471 if (colonPos < 0) 472 { 473 // isAltStream = false; 474 return E_INVALIDARG; 475 } 476 UString mainName = s.Left(colonPos); 477 int newFolderIndex = AddFolder(treeFolders, folderIndex, mainName); 478 if (treeFolders[newFolderIndex].UpdateItemIndex < 0) 479 { 480 for (int j = updateItems.Size() - 1; j >= 0; j--) 481 { 482 CUpdateItem &ui2 = updateItems[j]; 483 if (ui2.ParentFolderIndex == folderIndex 484 && ui2.Name == mainName) 485 { 486 ui2.TreeFolderIndex = newFolderIndex; 487 treeFolders[newFolderIndex].UpdateItemIndex = j; 488 } 489 } 490 } 491 folderIndex = newFolderIndex; 492 s.Delete(0, colonPos + 1); 493 } 494 ui.Name = s; 495 } 496 else 497 ui.Name = name; 498 ui.IsAltStream = isAltStream; 499 ui.ParentFolderIndex = folderIndex; 500 ui.TreeFolderIndex = -1; 501 if (ui.IsDir && !s.IsEmpty()) 502 { 503 ui.TreeFolderIndex = AddFolder(treeFolders, folderIndex, s); 504 treeFolders[ui.TreeFolderIndex].UpdateItemIndex = updateItems.Size(); 505 } 506 } 507 */ 508 ui.Name = name; 509 510 if (ui.NewData) 511 { 512 ui.Size = 0; 513 if (!ui.IsDir) 514 { 515 NCOM::CPropVariant prop; 516 RINOK(updateCallback->GetProperty(i, kpidSize, &prop)); 517 if (prop.vt != VT_UI8) 518 return E_INVALIDARG; 519 ui.Size = (UInt64)prop.uhVal.QuadPart; 520 if (ui.Size != 0 && ui.IsAnti) 521 return E_INVALIDARG; 522 } 523 } 524 525 updateItems.Add(ui); 526 } 527 528 /* 529 FillSortIndex(treeFolders, 0, 0); 530 for (i = 0; i < (UInt32)updateItems.Size(); i++) 531 { 532 CUpdateItem &ui = updateItems[i]; 533 ui.ParentSortIndex = treeFolders[ui.ParentFolderIndex].SortIndex; 534 ui.ParentSortIndexEnd = treeFolders[ui.ParentFolderIndex].SortIndexEnd; 535 } 536 */ 537 538 CCompressionMethodMode methodMode, headerMethod; 539 540 HRESULT res = SetMainMethod(methodMode 541 #ifndef _7ZIP_ST 542 , _numThreads 543 #endif 544 ); 545 RINOK(res); 546 547 RINOK(SetHeaderMethod(headerMethod)); 548 549 #ifndef _7ZIP_ST 550 methodMode.NumThreads = _numThreads; 551 methodMode.MultiThreadMixer = _useMultiThreadMixer; 552 headerMethod.NumThreads = 1; 553 headerMethod.MultiThreadMixer = _useMultiThreadMixer; 554 #endif 555 556 CMyComPtr<ICryptoGetTextPassword2> getPassword2; 557 updateCallback->QueryInterface(IID_ICryptoGetTextPassword2, (void **)&getPassword2); 558 559 methodMode.PasswordIsDefined = false; 560 methodMode.Password.Empty(); 561 if (getPassword2) 562 { 563 CMyComBSTR password; 564 Int32 passwordIsDefined; 565 RINOK(getPassword2->CryptoGetTextPassword2(&passwordIsDefined, &password)); 566 methodMode.PasswordIsDefined = IntToBool(passwordIsDefined); 567 if (methodMode.PasswordIsDefined && password) 568 methodMode.Password = password; 569 } 570 571 bool compressMainHeader = _compressHeaders; // check it 572 573 bool encryptHeaders = false; 574 575 #ifndef _NO_CRYPTO 576 if (!methodMode.PasswordIsDefined && _passwordIsDefined) 577 { 578 // if header is compressed, we use that password for updated archive 579 methodMode.PasswordIsDefined = true; 580 methodMode.Password = _password; 581 } 582 #endif 583 584 if (methodMode.PasswordIsDefined) 585 { 586 if (_encryptHeadersSpecified) 587 encryptHeaders = _encryptHeaders; 588 #ifndef _NO_CRYPTO 589 else 590 encryptHeaders = _passwordIsDefined; 591 #endif 592 compressMainHeader = true; 593 if (encryptHeaders) 594 { 595 headerMethod.PasswordIsDefined = methodMode.PasswordIsDefined; 596 headerMethod.Password = methodMode.Password; 597 } 598 } 599 600 if (numItems < 2) 601 compressMainHeader = false; 602 603 int level = GetLevel(); 604 605 CUpdateOptions options; 606 options.Method = &methodMode; 607 options.HeaderMethod = (_compressHeaders || encryptHeaders) ? &headerMethod : NULL; 608 options.UseFilters = (level != 0 && _autoFilter && !methodMode.Filter_was_Inserted); 609 options.MaxFilter = (level >= 8); 610 options.AnalysisLevel = GetAnalysisLevel(); 611 612 options.HeaderOptions.CompressMainHeader = compressMainHeader; 613 /* 614 options.HeaderOptions.WriteCTime = Write_CTime; 615 options.HeaderOptions.WriteATime = Write_ATime; 616 options.HeaderOptions.WriteMTime = Write_MTime; 617 */ 618 619 options.NumSolidFiles = _numSolidFiles; 620 options.NumSolidBytes = _numSolidBytes; 621 options.SolidExtension = _solidExtension; 622 options.UseTypeSorting = _useTypeSorting; 623 624 options.RemoveSfxBlock = _removeSfxBlock; 625 // options.VolumeMode = _volumeMode; 626 627 options.MultiThreadMixer = _useMultiThreadMixer; 628 629 COutArchive archive; 630 CArchiveDatabaseOut newDatabase; 631 632 CMyComPtr<ICryptoGetTextPassword> getPassword; 633 updateCallback->QueryInterface(IID_ICryptoGetTextPassword, (void **)&getPassword); 634 635 /* 636 if (secureBlocks.Sorted.Size() > 1) 637 { 638 secureBlocks.GetReverseMap(); 639 for (int i = 0; i < updateItems.Size(); i++) 640 { 641 int &secureIndex = updateItems[i].SecureIndex; 642 secureIndex = secureBlocks.BufIndexToSortedIndex[secureIndex]; 643 } 644 } 645 */ 646 647 res = Update( 648 EXTERNAL_CODECS_VARS 649 #ifdef _7Z_VOL 650 volume ? volume->Stream: 0, 651 volume ? db : 0, 652 #else 653 _inStream, 654 db, 655 #endif 656 updateItems, 657 // treeFolders, 658 // secureBlocks, 659 archive, newDatabase, outStream, updateCallback, options 660 #ifndef _NO_CRYPTO 661 , getPassword 662 #endif 663 ); 664 665 RINOK(res); 666 667 updateItems.ClearAndFree(); 668 669 return archive.WriteDatabase(EXTERNAL_CODECS_VARS 670 newDatabase, options.HeaderMethod, options.HeaderOptions); 671 672 COM_TRY_END 673 } 674 675 static HRESULT ParseBond(UString &srcString, UInt32 &coder, UInt32 &stream) 676 { 677 stream = 0; 678 { 679 unsigned index = ParseStringToUInt32(srcString, coder); 680 if (index == 0) 681 return E_INVALIDARG; 682 srcString.DeleteFrontal(index); 683 } 684 if (srcString[0] == 's') 685 { 686 srcString.Delete(0); 687 unsigned index = ParseStringToUInt32(srcString, stream); 688 if (index == 0) 689 return E_INVALIDARG; 690 srcString.DeleteFrontal(index); 691 } 692 return S_OK; 693 } 694 695 void COutHandler::InitProps() 696 { 697 CMultiMethodProps::Init(); 698 699 _removeSfxBlock = false; 700 _compressHeaders = true; 701 _encryptHeadersSpecified = false; 702 _encryptHeaders = false; 703 // _useParents = false; 704 705 Write_CTime.Init(); 706 Write_ATime.Init(); 707 Write_MTime.Init(); 708 709 _useMultiThreadMixer = true; 710 711 // _volumeMode = false; 712 713 InitSolid(); 714 _useTypeSorting = false; 715 } 716 717 HRESULT COutHandler::SetSolidFromString(const UString &s) 718 { 719 UString s2 = s; 720 s2.MakeLower_Ascii(); 721 for (unsigned i = 0; i < s2.Len();) 722 { 723 const wchar_t *start = ((const wchar_t *)s2) + i; 724 const wchar_t *end; 725 UInt64 v = ConvertStringToUInt64(start, &end); 726 if (start == end) 727 { 728 if (s2[i++] != 'e') 729 return E_INVALIDARG; 730 _solidExtension = true; 731 continue; 732 } 733 i += (int)(end - start); 734 if (i == s2.Len()) 735 return E_INVALIDARG; 736 wchar_t c = s2[i++]; 737 if (c == 'f') 738 { 739 if (v < 1) 740 v = 1; 741 _numSolidFiles = v; 742 } 743 else 744 { 745 unsigned numBits; 746 switch (c) 747 { 748 case 'b': numBits = 0; break; 749 case 'k': numBits = 10; break; 750 case 'm': numBits = 20; break; 751 case 'g': numBits = 30; break; 752 case 't': numBits = 40; break; 753 default: return E_INVALIDARG; 754 } 755 _numSolidBytes = (v << numBits); 756 _numSolidBytesDefined = true; 757 } 758 } 759 return S_OK; 760 } 761 762 HRESULT COutHandler::SetSolidFromPROPVARIANT(const PROPVARIANT &value) 763 { 764 bool isSolid; 765 switch (value.vt) 766 { 767 case VT_EMPTY: isSolid = true; break; 768 case VT_BOOL: isSolid = (value.boolVal != VARIANT_FALSE); break; 769 case VT_BSTR: 770 if (StringToBool(value.bstrVal, isSolid)) 771 break; 772 return SetSolidFromString(value.bstrVal); 773 default: return E_INVALIDARG; 774 } 775 if (isSolid) 776 InitSolid(); 777 else 778 _numSolidFiles = 1; 779 return S_OK; 780 } 781 782 static HRESULT PROPVARIANT_to_BoolPair(const PROPVARIANT &prop, CBoolPair &dest) 783 { 784 RINOK(PROPVARIANT_to_bool(prop, dest.Val)); 785 dest.Def = true; 786 return S_OK; 787 } 788 789 HRESULT COutHandler::SetProperty(const wchar_t *nameSpec, const PROPVARIANT &value) 790 { 791 UString name = nameSpec; 792 name.MakeLower_Ascii(); 793 if (name.IsEmpty()) 794 return E_INVALIDARG; 795 796 if (name[0] == L's') 797 { 798 name.Delete(0); 799 if (name.IsEmpty()) 800 return SetSolidFromPROPVARIANT(value); 801 if (value.vt != VT_EMPTY) 802 return E_INVALIDARG; 803 return SetSolidFromString(name); 804 } 805 806 UInt32 number; 807 int index = ParseStringToUInt32(name, number); 808 // UString realName = name.Ptr(index); 809 if (index == 0) 810 { 811 if (name.IsEqualTo("rsfx")) return PROPVARIANT_to_bool(value, _removeSfxBlock); 812 if (name.IsEqualTo("hc")) return PROPVARIANT_to_bool(value, _compressHeaders); 813 // if (name.IsEqualToNoCase(L"HS")) return PROPVARIANT_to_bool(value, _useParents); 814 815 if (name.IsEqualTo("hcf")) 816 { 817 bool compressHeadersFull = true; 818 RINOK(PROPVARIANT_to_bool(value, compressHeadersFull)); 819 return compressHeadersFull ? S_OK: E_INVALIDARG; 820 } 821 822 if (name.IsEqualTo("he")) 823 { 824 RINOK(PROPVARIANT_to_bool(value, _encryptHeaders)); 825 _encryptHeadersSpecified = true; 826 return S_OK; 827 } 828 829 if (name.IsEqualTo("tc")) return PROPVARIANT_to_BoolPair(value, Write_CTime); 830 if (name.IsEqualTo("ta")) return PROPVARIANT_to_BoolPair(value, Write_ATime); 831 if (name.IsEqualTo("tm")) return PROPVARIANT_to_BoolPair(value, Write_MTime); 832 833 if (name.IsEqualTo("mtf")) return PROPVARIANT_to_bool(value, _useMultiThreadMixer); 834 835 if (name.IsEqualTo("qs")) return PROPVARIANT_to_bool(value, _useTypeSorting); 836 837 // if (name.IsEqualTo("v")) return PROPVARIANT_to_bool(value, _volumeMode); 838 } 839 return CMultiMethodProps::SetProperty(name, value); 840 } 841 842 STDMETHODIMP CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps) 843 { 844 COM_TRY_BEGIN 845 _bonds.Clear(); 846 InitProps(); 847 848 for (UInt32 i = 0; i < numProps; i++) 849 { 850 UString name = names[i]; 851 name.MakeLower_Ascii(); 852 if (name.IsEmpty()) 853 return E_INVALIDARG; 854 855 const PROPVARIANT &value = values[i]; 856 857 if (name[0] == 'b') 858 { 859 if (value.vt != VT_EMPTY) 860 return E_INVALIDARG; 861 name.Delete(0); 862 863 CBond2 bond; 864 RINOK(ParseBond(name, bond.OutCoder, bond.OutStream)); 865 if (name[0] != ':') 866 return E_INVALIDARG; 867 name.Delete(0); 868 UInt32 inStream = 0; 869 RINOK(ParseBond(name, bond.InCoder, inStream)); 870 if (inStream != 0) 871 return E_INVALIDARG; 872 if (!name.IsEmpty()) 873 return E_INVALIDARG; 874 _bonds.Add(bond); 875 continue; 876 } 877 878 RINOK(SetProperty(name, value)); 879 } 880 881 unsigned numEmptyMethods = GetNumEmptyMethods(); 882 if (numEmptyMethods > 0) 883 { 884 unsigned k; 885 for (k = 0; k < _bonds.Size(); k++) 886 { 887 const CBond2 &bond = _bonds[k]; 888 if (bond.InCoder < (UInt32)numEmptyMethods || 889 bond.OutCoder < (UInt32)numEmptyMethods) 890 return E_INVALIDARG; 891 } 892 for (k = 0; k < _bonds.Size(); k++) 893 { 894 CBond2 &bond = _bonds[k]; 895 bond.InCoder -= (UInt32)numEmptyMethods; 896 bond.OutCoder -= (UInt32)numEmptyMethods; 897 } 898 _methods.DeleteFrontal(numEmptyMethods); 899 } 900 901 FOR_VECTOR (k, _bonds) 902 { 903 const CBond2 &bond = _bonds[k]; 904 if (bond.InCoder >= (UInt32)_methods.Size() || 905 bond.OutCoder >= (UInt32)_methods.Size()) 906 return E_INVALIDARG; 907 } 908 909 return S_OK; 910 COM_TRY_END 911 } 912 913 }} 914