5 #include "../../../../C/CpuArch.h"
\r
7 #include "../../Common/LimitedStreams.h"
\r
8 #include "../../Common/ProgressUtils.h"
\r
10 #include "../../Common/CreateCoder.h"
\r
12 #include "../../Compress/CopyCoder.h"
\r
14 #include "../Common/ItemNameUtils.h"
\r
15 #include "../Common/OutStreamWithCRC.h"
\r
17 #include "7zDecode.h"
\r
18 #include "7zEncode.h"
\r
19 #include "7zFolderInStream.h"
\r
20 #include "7zHandler.h"
\r
22 #include "7zUpdate.h"
\r
24 namespace NArchive {
\r
27 static const UInt64 k_LZMA = 0x030101;
\r
28 static const UInt64 k_BCJ = 0x03030103;
\r
29 static const UInt64 k_BCJ2 = 0x0303011B;
\r
31 static const wchar_t *kMatchFinderForBCJ2_LZMA = L"BT2";
\r
32 static const UInt32 kDictionaryForBCJ2_LZMA = 1 << 20;
\r
33 static const UInt32 kAlgorithmForBCJ2_LZMA = 1;
\r
34 static const UInt32 kNumFastBytesForBCJ2_LZMA = 64;
\r
36 #ifdef MY_CPU_X86_OR_AMD64
\r
37 #define USE_86_FILTER
\r
40 static HRESULT WriteRange(IInStream *inStream, ISequentialOutStream *outStream,
\r
41 UInt64 position, UInt64 size, ICompressProgressInfo *progress)
\r
43 RINOK(inStream->Seek(position, STREAM_SEEK_SET, 0));
\r
44 CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
\r
45 CMyComPtr<CLimitedSequentialInStream> inStreamLimited(streamSpec);
\r
46 streamSpec->SetStream(inStream);
\r
47 streamSpec->Init(size);
\r
49 NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder;
\r
50 CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
\r
51 RINOK(copyCoder->Code(inStreamLimited, outStream, NULL, NULL, progress));
\r
52 return (copyCoderSpec->TotalSize == size ? S_OK : E_FAIL);
\r
55 static int GetReverseSlashPos(const UString &name)
\r
57 int slashPos = name.ReverseFind(L'/');
\r
59 int slash1Pos = name.ReverseFind(L'\\');
\r
60 slashPos = MyMax(slashPos, slash1Pos);
\r
65 int CUpdateItem::GetExtensionPos() const
\r
67 int slashPos = GetReverseSlashPos(Name);
\r
68 int dotPos = Name.ReverseFind(L'.');
\r
69 if (dotPos < 0 || (dotPos < slashPos && slashPos >= 0))
\r
70 return Name.Length();
\r
74 UString CUpdateItem::GetExtension() const
\r
76 return Name.Mid(GetExtensionPos());
\r
79 #define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; }
\r
81 #define RINOZ_COMP(a, b) RINOZ(MyCompare(a, b))
\r
83 static int CompareBuffers(const CByteBuffer &a1, const CByteBuffer &a2)
\r
85 size_t c1 = a1.GetCapacity();
\r
86 size_t c2 = a2.GetCapacity();
\r
88 for (size_t i = 0; i < c1; i++)
\r
89 RINOZ_COMP(a1[i], a2[i]);
\r
93 static int CompareCoders(const CCoderInfo &c1, const CCoderInfo &c2)
\r
95 RINOZ_COMP(c1.NumInStreams, c2.NumInStreams);
\r
96 RINOZ_COMP(c1.NumOutStreams, c2.NumOutStreams);
\r
97 RINOZ_COMP(c1.MethodID, c2.MethodID);
\r
98 return CompareBuffers(c1.Props, c2.Props);
\r
101 static int CompareBindPairs(const CBindPair &b1, const CBindPair &b2)
\r
103 RINOZ_COMP(b1.InIndex, b2.InIndex);
\r
104 return MyCompare(b1.OutIndex, b2.OutIndex);
\r
107 static int CompareFolders(const CFolder &f1, const CFolder &f2)
\r
109 int s1 = f1.Coders.Size();
\r
110 int s2 = f2.Coders.Size();
\r
111 RINOZ_COMP(s1, s2);
\r
113 for (i = 0; i < s1; i++)
\r
114 RINOZ(CompareCoders(f1.Coders[i], f2.Coders[i]));
\r
115 s1 = f1.BindPairs.Size();
\r
116 s2 = f2.BindPairs.Size();
\r
117 RINOZ_COMP(s1, s2);
\r
118 for (i = 0; i < s1; i++)
\r
119 RINOZ(CompareBindPairs(f1.BindPairs[i], f2.BindPairs[i]));
\r
124 static int CompareFiles(const CFileItem &f1, const CFileItem &f2)
\r
126 return MyStringCompareNoCase(f1.Name, f2.Name);
\r
130 struct CFolderRepack
\r
137 static int CompareFolderRepacks(const CFolderRepack *p1, const CFolderRepack *p2, void *param)
\r
139 RINOZ_COMP(p1->Group, p2->Group);
\r
140 int i1 = p1->FolderIndex;
\r
141 int i2 = p2->FolderIndex;
\r
142 const CArchiveDatabaseEx &db = *(const CArchiveDatabaseEx *)param;
\r
143 RINOZ(CompareFolders(
\r
146 return MyCompare(i1, i2);
\r
149 db.NumUnpackStreamsVector[i1],
\r
150 db.NumUnpackStreamsVector[i2]);
\r
151 if (db.NumUnpackStreamsVector[i1] == 0)
\r
153 return CompareFiles(
\r
154 db.Files[db.FolderStartFileIndex[i1]],
\r
155 db.Files[db.FolderStartFileIndex[i2]]);
\r
159 ////////////////////////////////////////////////////////////
\r
161 static int CompareEmptyItems(const int *p1, const int *p2, void *param)
\r
163 const CObjectVector<CUpdateItem> &updateItems = *(const CObjectVector<CUpdateItem> *)param;
\r
164 const CUpdateItem &u1 = updateItems[*p1];
\r
165 const CUpdateItem &u2 = updateItems[*p2];
\r
166 if (u1.IsDir != u2.IsDir)
\r
167 return (u1.IsDir) ? 1 : -1;
\r
170 if (u1.IsAnti != u2.IsAnti)
\r
171 return (u1.IsAnti ? 1 : -1);
\r
172 int n = MyStringCompareNoCase(u1.Name, u2.Name);
\r
175 if (u1.IsAnti != u2.IsAnti)
\r
176 return (u1.IsAnti ? 1 : -1);
\r
177 return MyStringCompareNoCase(u1.Name, u2.Name);
\r
180 static const char *g_Exts =
\r
181 " lzma 7z ace arc arj bz bz2 deb lzo lzx gz pak rpm sit tgz tbz tbz2 tgz cab ha lha lzh rar zoo"
\r
182 " zip jar ear war msi"
\r
183 " 3gp avi mov mpeg mpg mpe wmv"
\r
184 " aac ape fla flac la mp3 m4a mp4 ofr ogg pac ra rm rka shn swa tta wv wma wav"
\r
187 " gif jpeg jpg jp2 png tiff bmp ico psd psp"
\r
188 " awg ps eps cgm dxf svg vrml wmf emf ai md"
\r
189 " cad dwg pps key sxi"
\r
191 " iso bin nrg mdf img pdi tar cpio xpi"
\r
192 " vfd vhd vud vmc vsv"
\r
193 " vmdk dsk nvram vmem vmsd vmsn vmss vmtm"
\r
194 " inl inc idl acf asa h hpp hxx c cpp cxx rc java cs pas bas vb cls ctl frm dlg def"
\r
196 " asm sql manifest dep "
\r
197 " mak clw csproj vcproj sln dsp dsw "
\r
200 " xml xsd xsl xslt hxk hxc htm html xhtml xht mht mhtml htw asp aspx css cgi jsp shtml"
\r
201 " awk sed hta js php php3 php4 php5 phptml pl pm py pyo rb sh tcl vbs"
\r
202 " text txt tex ans asc srt reg ini doc docx mcw dot rtf hlp xls xlr xlt xlw ppt pdf"
\r
203 " sxc sxd sxi sxg sxw stc sti stw stm odt ott odg otg odp otp ods ots odf"
\r
204 " abw afp cwk lwp wpd wps wpt wrf wri"
\r
205 " abf afm bdf fon mgf otf pcf pfa snf ttf"
\r
206 " dbf mdb nsf ntf wdb db fdb gdb"
\r
207 " exe dll ocx vbx sfx sys tlb awx com obj lib out o so "
\r
208 " pdb pch idb ncb opt";
\r
210 int GetExtIndex(const char *ext)
\r
213 const char *p = g_Exts;
\r
224 char c2 = ext[pos++];
\r
225 if (c2 == 0 && (c == 0 || c == ' '))
\r
245 const CUpdateItem *UpdateItem;
\r
247 UInt32 ExtensionPos;
\r
249 int ExtensionIndex;
\r
250 CRefItem(UInt32 index, const CUpdateItem &ui, bool sortByType):
\r
259 int slashPos = GetReverseSlashPos(ui.Name);
\r
260 NamePos = ((slashPos >= 0) ? (slashPos + 1) : 0);
\r
261 int dotPos = ui.Name.ReverseFind(L'.');
\r
262 if (dotPos < 0 || (dotPos < slashPos && slashPos >= 0))
\r
263 ExtensionPos = ui.Name.Length();
\r
266 ExtensionPos = dotPos + 1;
\r
267 UString us = ui.Name.Mid(ExtensionPos);
\r
273 for (i = 0; i < us.Length(); i++)
\r
280 if (i == us.Length())
\r
281 ExtensionIndex = GetExtIndex(s);
\r
283 ExtensionIndex = 0;
\r
290 static int CompareUpdateItems(const CRefItem *p1, const CRefItem *p2, void *param)
\r
292 const CRefItem &a1 = *p1;
\r
293 const CRefItem &a2 = *p2;
\r
294 const CUpdateItem &u1 = *a1.UpdateItem;
\r
295 const CUpdateItem &u2 = *a2.UpdateItem;
\r
297 if (u1.IsDir != u2.IsDir)
\r
298 return (u1.IsDir) ? 1 : -1;
\r
301 if (u1.IsAnti != u2.IsAnti)
\r
302 return (u1.IsAnti ? 1 : -1);
\r
303 n = MyStringCompareNoCase(u1.Name, u2.Name);
\r
306 bool sortByType = *(bool *)param;
\r
309 RINOZ_COMP(a1.ExtensionIndex, a2.ExtensionIndex);
\r
310 RINOZ(MyStringCompareNoCase(u1.Name + a1.ExtensionPos, u2.Name + a2.ExtensionPos));
\r
311 RINOZ(MyStringCompareNoCase(u1.Name + a1.NamePos, u2.Name + a2.NamePos));
\r
312 if (!u1.MTimeDefined && u2.MTimeDefined) return 1;
\r
313 if (u1.MTimeDefined && !u2.MTimeDefined) return -1;
\r
314 if (u1.MTimeDefined && u2.MTimeDefined) RINOZ_COMP(u1.MTime, u2.MTime);
\r
315 RINOZ_COMP(u1.Size, u2.Size);
\r
317 return MyStringCompareNoCase(u1.Name, u2.Name);
\r
322 CRecordVector<UInt32> Indices;
\r
325 static wchar_t *g_ExeExts[] =
\r
334 static bool IsExeExt(const UString &ext)
\r
336 for (int i = 0; i < sizeof(g_ExeExts) / sizeof(g_ExeExts[0]); i++)
\r
337 if (ext.CompareNoCase(g_ExeExts[i]) == 0)
\r
342 #ifdef USE_86_FILTER
\r
344 static inline void GetMethodFull(UInt64 methodID, UInt32 numInStreams, CMethodFull &methodResult)
\r
346 methodResult.Id = methodID;
\r
347 methodResult.NumInStreams = numInStreams;
\r
348 methodResult.NumOutStreams = 1;
\r
351 static void MakeExeMethod(const CCompressionMethodMode &method,
\r
352 bool bcj2Filter, CCompressionMethodMode &exeMethod)
\r
354 exeMethod = method;
\r
357 CMethodFull methodFull;
\r
358 GetMethodFull(k_BCJ2, 4, methodFull);
\r
359 exeMethod.Methods.Insert(0, methodFull);
\r
360 GetMethodFull(k_LZMA, 1, methodFull);
\r
363 prop.Id = NCoderPropID::kAlgorithm;
\r
364 prop.Value = kAlgorithmForBCJ2_LZMA;
\r
365 methodFull.Props.Add(prop);
\r
369 prop.Id = NCoderPropID::kMatchFinder;
\r
370 prop.Value = kMatchFinderForBCJ2_LZMA;
\r
371 methodFull.Props.Add(prop);
\r
375 prop.Id = NCoderPropID::kDictionarySize;
\r
376 prop.Value = kDictionaryForBCJ2_LZMA;
\r
377 methodFull.Props.Add(prop);
\r
381 prop.Id = NCoderPropID::kNumFastBytes;
\r
382 prop.Value = kNumFastBytesForBCJ2_LZMA;
\r
383 methodFull.Props.Add(prop);
\r
387 prop.Id = NCoderPropID::kNumThreads;
\r
388 prop.Value = (UInt32)1;
\r
389 methodFull.Props.Add(prop);
\r
392 exeMethod.Methods.Add(methodFull);
\r
393 exeMethod.Methods.Add(methodFull);
\r
400 bind.OutStream = 0;
\r
401 exeMethod.Binds.Add(bind);
\r
404 bind.OutStream = 1;
\r
405 exeMethod.Binds.Add(bind);
\r
408 bind.OutStream = 2;
\r
409 exeMethod.Binds.Add(bind);
\r
413 CMethodFull methodFull;
\r
414 GetMethodFull(k_BCJ, 1, methodFull);
\r
415 exeMethod.Methods.Insert(0, methodFull);
\r
420 bind.OutStream = 0;
\r
421 exeMethod.Binds.Add(bind);
\r
427 static void FromUpdateItemToFileItem(const CUpdateItem &ui,
\r
428 CFileItem &file, CFileItem2 &file2)
\r
430 file.Name = NItemName::MakeLegalName(ui.Name);
\r
431 if (ui.AttribDefined)
\r
432 file.SetAttrib(ui.Attrib);
\r
434 file2.CTime = ui.CTime; file2.CTimeDefined = ui.CTimeDefined;
\r
435 file2.ATime = ui.ATime; file2.ATimeDefined = ui.ATimeDefined;
\r
436 file2.MTime = ui.MTime; file2.MTimeDefined = ui.MTimeDefined;
\r
437 file2.IsAnti = ui.IsAnti;
\r
438 file2.StartPosDefined = false;
\r
440 file.Size = ui.Size;
\r
441 file.IsDir = ui.IsDir;
\r
442 file.HasStream = ui.HasStream();
\r
445 class CFolderOutStream2:
\r
446 public ISequentialOutStream,
\r
447 public CMyUnknownImp
\r
449 COutStreamWithCRC *_crcStreamSpec;
\r
450 CMyComPtr<ISequentialOutStream> _crcStream;
\r
451 const CArchiveDatabaseEx *_db;
\r
452 const CBoolVector *_extractStatuses;
\r
453 CMyComPtr<ISequentialOutStream> _outStream;
\r
454 UInt32 _startIndex;
\r
461 HRESULT CloseFileAndSetResult();
\r
462 HRESULT ProcessEmptyFiles();
\r
466 CFolderOutStream2()
\r
468 _crcStreamSpec = new COutStreamWithCRC;
\r
469 _crcStream = _crcStreamSpec;
\r
472 HRESULT Init(const CArchiveDatabaseEx *db, UInt32 startIndex,
\r
473 const CBoolVector *extractStatuses, ISequentialOutStream *outStream);
\r
474 void ReleaseOutStream();
\r
475 HRESULT CheckFinishedState() const { return (_currentIndex == _extractStatuses->Size()) ? S_OK: E_FAIL; }
\r
477 STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
\r
480 HRESULT CFolderOutStream2::Init(const CArchiveDatabaseEx *db, UInt32 startIndex,
\r
481 const CBoolVector *extractStatuses, ISequentialOutStream *outStream)
\r
484 _startIndex = startIndex;
\r
485 _extractStatuses = extractStatuses;
\r
486 _outStream = outStream;
\r
489 _fileIsOpen = false;
\r
490 return ProcessEmptyFiles();
\r
493 void CFolderOutStream2::ReleaseOutStream()
\r
495 _outStream.Release();
\r
496 _crcStreamSpec->ReleaseStream();
\r
499 void CFolderOutStream2::OpenFile()
\r
501 _crcStreamSpec->SetStream((*_extractStatuses)[_currentIndex] ? _outStream : NULL);
\r
502 _crcStreamSpec->Init(true);
\r
503 _fileIsOpen = true;
\r
504 _rem = _db->Files[_startIndex + _currentIndex].Size;
\r
507 void CFolderOutStream2::CloseFile()
\r
509 _crcStreamSpec->ReleaseStream();
\r
510 _fileIsOpen = false;
\r
514 HRESULT CFolderOutStream2::CloseFileAndSetResult()
\r
516 const CFileItem &file = _db->Files[_startIndex + _currentIndex];
\r
518 return (file.IsDir || !file.CrcDefined || file.Crc == _crcStreamSpec->GetCRC()) ? S_OK: S_FALSE;
\r
521 HRESULT CFolderOutStream2::ProcessEmptyFiles()
\r
523 while (_currentIndex < _extractStatuses->Size() && _db->Files[_startIndex + _currentIndex].Size == 0)
\r
526 RINOK(CloseFileAndSetResult());
\r
531 STDMETHODIMP CFolderOutStream2::Write(const void *data, UInt32 size, UInt32 *processedSize)
\r
533 if (processedSize != NULL)
\r
534 *processedSize = 0;
\r
539 UInt32 cur = size < _rem ? size : (UInt32)_rem;
\r
540 RINOK(_crcStream->Write(data, cur, &cur));
\r
543 data = (const Byte *)data + cur;
\r
546 if (processedSize != NULL)
\r
547 *processedSize += cur;
\r
550 RINOK(CloseFileAndSetResult());
\r
551 RINOK(ProcessEmptyFiles());
\r
557 RINOK(ProcessEmptyFiles());
\r
558 if (_currentIndex == _extractStatuses->Size())
\r
560 // we don't support partial extracting
\r
569 class CThreadDecoder: public CVirtThread
\r
573 CMyComPtr<IInStream> InStream;
\r
575 CFolderOutStream2 *FosSpec;
\r
576 CMyComPtr<ISequentialOutStream> Fos;
\r
579 const UInt64 *PackSizes;
\r
580 const CFolder *Folder;
\r
582 CMyComPtr<ICryptoGetTextPassword> GetTextPassword;
\r
585 DECL_EXTERNAL_CODECS_VARS
\r
600 FosSpec = new CFolderOutStream2;
\r
604 virtual void Execute();
\r
607 void CThreadDecoder::Execute()
\r
612 bool passwordIsDefined;
\r
614 Result = Decoder.Decode(
\r
615 EXTERNAL_CODECS_VARS
\r
623 , GetTextPassword, passwordIsDefined
\r
626 , MtMode, NumThreads
\r
634 if (Result == S_OK)
\r
635 Result = FosSpec->CheckFinishedState();
\r
636 FosSpec->ReleaseOutStream();
\r
639 bool static Is86FilteredFolder(const CFolder &f)
\r
641 for (int i = 0; i < f.Coders.Size(); i++)
\r
643 CMethodId m = f.Coders[i].MethodID;
\r
644 if (m == k_BCJ || m == k_BCJ2)
\r
652 class CCryptoGetTextPassword:
\r
653 public ICryptoGetTextPassword,
\r
654 public CMyUnknownImp
\r
660 STDMETHOD(CryptoGetTextPassword)(BSTR *password);
\r
663 STDMETHODIMP CCryptoGetTextPassword::CryptoGetTextPassword(BSTR *password)
\r
665 return StringToBstr(Password, password);
\r
670 static const int kNumGroupsMax = 4;
\r
672 #ifdef USE_86_FILTER
\r
673 static bool Is86Group(int group) { return (group & 1) != 0; }
\r
675 static bool IsEncryptedGroup(int group) { return (group & 2) != 0; }
\r
676 static int GetGroupIndex(bool encrypted, int bcjFiltered)
\r
677 { return (encrypted ? 2 : 0) + (bcjFiltered ? 1 : 0); }
\r
680 DECL_EXTERNAL_CODECS_LOC_VARS
\r
681 IInStream *inStream,
\r
682 const CArchiveDatabaseEx *db,
\r
683 const CObjectVector<CUpdateItem> &updateItems,
\r
684 COutArchive &archive,
\r
685 CArchiveDatabase &newDatabase,
\r
686 ISequentialOutStream *seqOutStream,
\r
687 IArchiveUpdateCallback *updateCallback,
\r
688 const CUpdateOptions &options
\r
690 , ICryptoGetTextPassword *getDecoderPassword
\r
694 UInt64 numSolidFiles = options.NumSolidFiles;
\r
695 if (numSolidFiles == 0)
\r
698 CMyComPtr<IOutStream> outStream;
\r
699 RINOK(seqOutStream->QueryInterface(IID_IOutStream, (void **)&outStream));
\r
704 UInt64 startBlockSize = db != 0 ? db->ArchiveInfo.StartPosition: 0;
\r
705 if (startBlockSize > 0 && !options.RemoveSfxBlock)
\r
707 RINOK(WriteRange(inStream, seqOutStream, 0, startBlockSize, NULL));
\r
710 CRecordVector<int> fileIndexToUpdateIndexMap;
\r
711 CRecordVector<CFolderRepack> folderRefs;
\r
712 UInt64 complexity = 0;
\r
713 UInt64 inSizeForReduce2 = 0;
\r
714 bool needEncryptedRepack = false;
\r
717 fileIndexToUpdateIndexMap.Reserve(db->Files.Size());
\r
719 for (i = 0; i < db->Files.Size(); i++)
\r
720 fileIndexToUpdateIndexMap.Add(-1);
\r
722 for (i = 0; i < updateItems.Size(); i++)
\r
724 int index = updateItems[i].IndexInArchive;
\r
726 fileIndexToUpdateIndexMap[index] = i;
\r
729 for (i = 0; i < db->Folders.Size(); i++)
\r
731 CNum indexInFolder = 0;
\r
732 CNum numCopyItems = 0;
\r
733 CNum numUnpackStreams = db->NumUnpackStreamsVector[i];
\r
734 UInt64 repackSize = 0;
\r
735 for (CNum fi = db->FolderStartFileIndex[i]; indexInFolder < numUnpackStreams; fi++)
\r
737 const CFileItem &file = db->Files[fi];
\r
738 if (file.HasStream)
\r
741 int updateIndex = fileIndexToUpdateIndexMap[fi];
\r
742 if (updateIndex >= 0 && !updateItems[updateIndex].NewData)
\r
745 repackSize += file.Size;
\r
750 if (numCopyItems == 0)
\r
754 rep.FolderIndex = i;
\r
755 rep.NumCopyFiles = numCopyItems;
\r
756 const CFolder &f = db->Folders[i];
\r
757 bool isEncrypted = f.IsEncrypted();
\r
758 rep.Group = GetGroupIndex(isEncrypted, Is86FilteredFolder(f));
\r
759 folderRefs.Add(rep);
\r
760 if (numCopyItems == numUnpackStreams)
\r
761 complexity += db->GetFolderFullPackSize(i);
\r
764 complexity += repackSize;
\r
765 if (repackSize > inSizeForReduce2)
\r
766 inSizeForReduce2 = repackSize;
\r
768 needEncryptedRepack = true;
\r
771 folderRefs.Sort(CompareFolderRepacks, (void *)db);
\r
774 UInt64 inSizeForReduce = 0;
\r
776 for (i = 0; i < updateItems.Size(); i++)
\r
778 const CUpdateItem &ui = updateItems[i];
\r
781 complexity += ui.Size;
\r
782 if (numSolidFiles != 1)
\r
783 inSizeForReduce += ui.Size;
\r
784 else if (ui.Size > inSizeForReduce)
\r
785 inSizeForReduce = ui.Size;
\r
789 if (inSizeForReduce2 > inSizeForReduce)
\r
790 inSizeForReduce = inSizeForReduce2;
\r
792 const UInt32 kMinReduceSize = (1 << 16);
\r
793 if (inSizeForReduce < kMinReduceSize)
\r
794 inSizeForReduce = kMinReduceSize;
\r
796 RINOK(updateCallback->SetTotal(complexity));
\r
798 CLocalProgress *lps = new CLocalProgress;
\r
799 CMyComPtr<ICompressProgressInfo> progress = lps;
\r
800 lps->Init(updateCallback, true);
\r
802 CThreadDecoder threadDecoder;
\r
803 if (!folderRefs.IsEmpty())
\r
805 #ifdef EXTERNAL_CODECS
\r
806 threadDecoder._codecsInfo = codecsInfo;
\r
807 threadDecoder._externalCodecs = *externalCodecs;
\r
809 RINOK(threadDecoder.Create());
\r
812 CObjectVector<CSolidGroup> groups;
\r
813 for (i = 0; i < kNumGroupsMax; i++)
\r
814 groups.Add(CSolidGroup());
\r
817 // ---------- Split files to 2 groups ----------
\r
819 bool useFilters = options.UseFilters;
\r
820 const CCompressionMethodMode &method = *options.Method;
\r
821 if (method.Methods.Size() != 1 || method.Binds.Size() != 0)
\r
822 useFilters = false;
\r
823 for (i = 0; i < updateItems.Size(); i++)
\r
825 const CUpdateItem &ui = updateItems[i];
\r
826 if (!ui.NewData || !ui.HasStream())
\r
828 bool filteredGroup = false;
\r
831 int dotPos = ui.Name.ReverseFind(L'.');
\r
833 filteredGroup = IsExeExt(ui.Name.Mid(dotPos + 1));
\r
835 groups[GetGroupIndex(method.PasswordIsDefined, filteredGroup)].Indices.Add(i);
\r
841 CCryptoGetTextPassword *getPasswordSpec = NULL;
\r
842 if (needEncryptedRepack)
\r
844 getPasswordSpec = new CCryptoGetTextPassword;
\r
845 threadDecoder.GetTextPassword = getPasswordSpec;
\r
847 if (options.Method->PasswordIsDefined)
\r
848 getPasswordSpec->Password = options.Method->Password;
\r
851 if (!getDecoderPassword)
\r
853 CMyComBSTR password;
\r
854 RINOK(getDecoderPassword->CryptoGetTextPassword(&password));
\r
855 getPasswordSpec->Password = password;
\r
861 // ---------- Compress ----------
\r
863 RINOK(archive.Create(seqOutStream, false));
\r
864 RINOK(archive.SkipPrefixArchiveHeader());
\r
866 int folderRefIndex = 0;
\r
867 lps->ProgressOffset = 0;
\r
869 for (int groupIndex = 0; groupIndex < kNumGroupsMax; groupIndex++)
\r
871 const CSolidGroup &group = groups[groupIndex];
\r
873 CCompressionMethodMode method;
\r
874 #ifdef USE_86_FILTER
\r
875 if (Is86Group(groupIndex))
\r
876 MakeExeMethod(*options.Method, options.MaxFilter, method);
\r
879 method = *options.Method;
\r
881 if (IsEncryptedGroup(groupIndex))
\r
883 if (!method.PasswordIsDefined)
\r
886 if (getPasswordSpec)
\r
887 method.Password = getPasswordSpec->Password;
\r
889 method.PasswordIsDefined = true;
\r
894 method.PasswordIsDefined = false;
\r
895 method.Password.Empty();
\r
898 CEncoder encoder(method);
\r
900 for (; folderRefIndex < folderRefs.Size(); folderRefIndex++)
\r
902 const CFolderRepack &rep = folderRefs[folderRefIndex];
\r
903 if (rep.Group != groupIndex)
\r
905 int folderIndex = rep.FolderIndex;
\r
907 if (rep.NumCopyFiles == db->NumUnpackStreamsVector[folderIndex])
\r
909 UInt64 packSize = db->GetFolderFullPackSize(folderIndex);
\r
910 RINOK(WriteRange(inStream, archive.SeqStream,
\r
911 db->GetFolderStreamPos(folderIndex, 0), packSize, progress));
\r
912 lps->ProgressOffset += packSize;
\r
914 const CFolder &folder = db->Folders[folderIndex];
\r
915 CNum startIndex = db->FolderStartPackStreamIndex[folderIndex];
\r
916 for (int j = 0; j < folder.PackStreams.Size(); j++)
\r
918 newDatabase.PackSizes.Add(db->PackSizes[startIndex + j]);
\r
919 // newDatabase.PackCRCsDefined.Add(db.PackCRCsDefined[startIndex + j]);
\r
920 // newDatabase.PackCRCs.Add(db.PackCRCs[startIndex + j]);
\r
922 newDatabase.Folders.Add(folder);
\r
927 RINOK(sb.CreateEvents());
\r
928 CMyComPtr<ISequentialOutStream> sbOutStream;
\r
929 CMyComPtr<ISequentialInStream> sbInStream;
\r
930 sb.CreateStreams(&sbInStream, &sbOutStream);
\r
931 CBoolVector extractStatuses;
\r
933 CNum numUnpackStreams = db->NumUnpackStreamsVector[folderIndex];
\r
934 CNum indexInFolder = 0;
\r
936 for (CNum fi = db->FolderStartFileIndex[folderIndex]; indexInFolder < numUnpackStreams; fi++)
\r
938 bool needExtract = false;
\r
939 if (db->Files[fi].HasStream)
\r
942 int updateIndex = fileIndexToUpdateIndexMap[fi];
\r
943 if (updateIndex >= 0 && !updateItems[updateIndex].NewData)
\r
944 needExtract = true;
\r
946 extractStatuses.Add(needExtract);
\r
949 RINOK(threadDecoder.FosSpec->Init(db, db->FolderStartFileIndex[folderIndex], &extractStatuses, sbOutStream));
\r
950 sbOutStream.Release();
\r
952 threadDecoder.InStream = inStream;
\r
953 threadDecoder.Folder = &db->Folders[folderIndex];
\r
954 threadDecoder.StartPos = db->GetFolderStreamPos(folderIndex, 0);
\r
955 threadDecoder.PackSizes = &db->PackSizes[db->FolderStartPackStreamIndex[folderIndex]];
\r
957 threadDecoder.Start();
\r
959 int startPackIndex = newDatabase.PackSizes.Size();
\r
961 RINOK(encoder.Encode(
\r
962 EXTERNAL_CODECS_LOC_VARS
\r
963 sbInStream, NULL, &inSizeForReduce, newFolder,
\r
964 archive.SeqStream, newDatabase.PackSizes, progress));
\r
966 threadDecoder.WaitFinish();
\r
968 RINOK(threadDecoder.Result);
\r
970 for (; startPackIndex < newDatabase.PackSizes.Size(); startPackIndex++)
\r
971 lps->OutSize += newDatabase.PackSizes[startPackIndex];
\r
972 lps->InSize += newFolder.GetUnpackSize();
\r
974 newDatabase.Folders.Add(newFolder);
\r
977 newDatabase.NumUnpackStreamsVector.Add(rep.NumCopyFiles);
\r
979 CNum numUnpackStreams = db->NumUnpackStreamsVector[folderIndex];
\r
981 CNum indexInFolder = 0;
\r
982 for (CNum fi = db->FolderStartFileIndex[folderIndex]; indexInFolder < numUnpackStreams; fi++)
\r
986 db->GetFile(fi, file, file2);
\r
987 if (file.HasStream)
\r
990 int updateIndex = fileIndexToUpdateIndexMap[fi];
\r
991 if (updateIndex >= 0)
\r
993 const CUpdateItem &ui = updateItems[updateIndex];
\r
999 FromUpdateItemToFileItem(ui, uf, file2);
\r
1000 uf.Size = file.Size;
\r
1001 uf.Crc = file.Crc;
\r
1002 uf.CrcDefined = file.CrcDefined;
\r
1003 uf.HasStream = file.HasStream;
\r
1006 newDatabase.AddFile(file, file2);
\r
1012 int numFiles = group.Indices.Size();
\r
1013 if (numFiles == 0)
\r
1015 CRecordVector<CRefItem> refItems;
\r
1016 refItems.Reserve(numFiles);
\r
1017 bool sortByType = (numSolidFiles > 1);
\r
1018 for (i = 0; i < numFiles; i++)
\r
1019 refItems.Add(CRefItem(group.Indices[i], updateItems[group.Indices[i]], sortByType));
\r
1020 refItems.Sort(CompareUpdateItems, (void *)&sortByType);
\r
1022 CRecordVector<UInt32> indices;
\r
1023 indices.Reserve(numFiles);
\r
1025 for (i = 0; i < numFiles; i++)
\r
1027 UInt32 index = refItems[i].Index;
\r
1028 indices.Add(index);
\r
1030 const CUpdateItem &ui = updateItems[index];
\r
1033 FromUpdateItemToFileItem(ui, file);
\r
1035 file = db.Files[ui.IndexInArchive];
\r
1036 if (file.IsAnti || file.IsDir)
\r
1038 newDatabase.Files.Add(file);
\r
1042 for (i = 0; i < numFiles;)
\r
1044 UInt64 totalSize = 0;
\r
1046 UString prevExtension;
\r
1047 for (numSubFiles = 0; i + numSubFiles < numFiles &&
\r
1048 numSubFiles < numSolidFiles; numSubFiles++)
\r
1050 const CUpdateItem &ui = updateItems[indices[i + numSubFiles]];
\r
1051 totalSize += ui.Size;
\r
1052 if (totalSize > options.NumSolidBytes)
\r
1054 if (options.SolidExtension)
\r
1056 UString ext = ui.GetExtension();
\r
1057 if (numSubFiles == 0)
\r
1058 prevExtension = ext;
\r
1060 if (ext.CompareNoCase(prevExtension) != 0)
\r
1064 if (numSubFiles < 1)
\r
1067 CFolderInStream *inStreamSpec = new CFolderInStream;
\r
1068 CMyComPtr<ISequentialInStream> solidInStream(inStreamSpec);
\r
1069 inStreamSpec->Init(updateCallback, &indices[i], numSubFiles);
\r
1071 CFolder folderItem;
\r
1073 int startPackIndex = newDatabase.PackSizes.Size();
\r
1074 RINOK(encoder.Encode(
\r
1075 EXTERNAL_CODECS_LOC_VARS
\r
1076 solidInStream, NULL, &inSizeForReduce, folderItem,
\r
1077 archive.SeqStream, newDatabase.PackSizes, progress));
\r
1079 for (; startPackIndex < newDatabase.PackSizes.Size(); startPackIndex++)
\r
1080 lps->OutSize += newDatabase.PackSizes[startPackIndex];
\r
1082 lps->InSize += folderItem.GetUnpackSize();
\r
1084 // newDatabase.PackCRCsDefined.Add(false);
\r
1085 // newDatabase.PackCRCs.Add(0);
\r
1087 newDatabase.Folders.Add(folderItem);
\r
1089 CNum numUnpackStreams = 0;
\r
1090 for (int subIndex = 0; subIndex < numSubFiles; subIndex++)
\r
1092 const CUpdateItem &ui = updateItems[indices[i + subIndex]];
\r
1096 FromUpdateItemToFileItem(ui, file, file2);
\r
1098 db->GetFile(ui.IndexInArchive, file, file2);
\r
1099 if (file2.IsAnti || file.IsDir)
\r
1103 CFileItem &file = newDatabase.Files[
\r
1104 startFileIndexInDatabase + i + subIndex];
\r
1106 if (!inStreamSpec->Processed[subIndex])
\r
1109 // file.Name += L".locked";
\r
1112 file.Crc = inStreamSpec->CRCs[subIndex];
\r
1113 file.Size = inStreamSpec->Sizes[subIndex];
\r
1114 if (file.Size != 0)
\r
1116 file.CrcDefined = true;
\r
1117 file.HasStream = true;
\r
1118 numUnpackStreams++;
\r
1122 file.CrcDefined = false;
\r
1123 file.HasStream = false;
\r
1125 newDatabase.AddFile(file, file2);
\r
1127 // numUnpackStreams = 0 is very bad case for locked files
\r
1128 // v3.13 doesn't understand it.
\r
1129 newDatabase.NumUnpackStreamsVector.Add(numUnpackStreams);
\r
1134 if (folderRefIndex != folderRefs.Size())
\r
1138 folderRefs.ClearAndFree();
\r
1139 fileIndexToUpdateIndexMap.ClearAndFree();
\r
1140 groups.ClearAndFree();
\r
1144 // ---------- Write Folders & Empty Files ----------
\r
1146 CRecordVector<int> emptyRefs;
\r
1147 for (i = 0; i < updateItems.Size(); i++)
\r
1149 const CUpdateItem &ui = updateItems[i];
\r
1152 if (ui.HasStream())
\r
1155 else if (ui.IndexInArchive != -1 && db->Files[ui.IndexInArchive].HasStream)
\r
1159 emptyRefs.Sort(CompareEmptyItems, (void *)&updateItems);
\r
1160 for (i = 0; i < emptyRefs.Size(); i++)
\r
1162 const CUpdateItem &ui = updateItems[emptyRefs[i]];
\r
1166 FromUpdateItemToFileItem(ui, file, file2);
\r
1168 db->GetFile(ui.IndexInArchive, file, file2);
\r
1169 newDatabase.AddFile(file, file2);
\r
1173 newDatabase.ReserveDown();
\r