Imported Upstream version 9.20
[platform/upstream/7zip.git] / CPP / 7zip / Archive / 7z / 7zUpdate.cpp
1 // 7zUpdate.cpp\r
2 \r
3 #include "StdAfx.h"\r
4 \r
5 #include "../../../../C/CpuArch.h"\r
6 \r
7 #include "../../Common/LimitedStreams.h"\r
8 #include "../../Common/ProgressUtils.h"\r
9 \r
10 #include "../../Common/CreateCoder.h"\r
11 \r
12 #include "../../Compress/CopyCoder.h"\r
13 \r
14 #include "../Common/ItemNameUtils.h"\r
15 #include "../Common/OutStreamWithCRC.h"\r
16 \r
17 #include "7zDecode.h"\r
18 #include "7zEncode.h"\r
19 #include "7zFolderInStream.h"\r
20 #include "7zHandler.h"\r
21 #include "7zOut.h"\r
22 #include "7zUpdate.h"\r
23 \r
24 namespace NArchive {\r
25 namespace N7z {\r
26 \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
30 \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
35 \r
36 #ifdef MY_CPU_X86_OR_AMD64\r
37 #define USE_86_FILTER\r
38 #endif\r
39 \r
40 static HRESULT WriteRange(IInStream *inStream, ISequentialOutStream *outStream,\r
41     UInt64 position, UInt64 size, ICompressProgressInfo *progress)\r
42 {\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
48 \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
53 }\r
54 \r
55 static int GetReverseSlashPos(const UString &name)\r
56 {\r
57   int slashPos = name.ReverseFind(L'/');\r
58   #ifdef _WIN32\r
59   int slash1Pos = name.ReverseFind(L'\\');\r
60   slashPos = MyMax(slashPos, slash1Pos);\r
61   #endif\r
62   return slashPos;\r
63 }\r
64 \r
65 int CUpdateItem::GetExtensionPos() const\r
66 {\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
71   return dotPos + 1;\r
72 }\r
73 \r
74 UString CUpdateItem::GetExtension() const\r
75 {\r
76   return Name.Mid(GetExtensionPos());\r
77 }\r
78 \r
79 #define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; }\r
80 \r
81 #define RINOZ_COMP(a, b) RINOZ(MyCompare(a, b))\r
82 \r
83 static int CompareBuffers(const CByteBuffer &a1, const CByteBuffer &a2)\r
84 {\r
85   size_t c1 = a1.GetCapacity();\r
86   size_t c2 = a2.GetCapacity();\r
87   RINOZ_COMP(c1, c2);\r
88   for (size_t i = 0; i < c1; i++)\r
89     RINOZ_COMP(a1[i], a2[i]);\r
90   return 0;\r
91 }\r
92 \r
93 static int CompareCoders(const CCoderInfo &c1, const CCoderInfo &c2)\r
94 {\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
99 }\r
100 \r
101 static int CompareBindPairs(const CBindPair &b1, const CBindPair &b2)\r
102 {\r
103   RINOZ_COMP(b1.InIndex, b2.InIndex);\r
104   return MyCompare(b1.OutIndex, b2.OutIndex);\r
105 }\r
106 \r
107 static int CompareFolders(const CFolder &f1, const CFolder &f2)\r
108 {\r
109   int s1 = f1.Coders.Size();\r
110   int s2 = f2.Coders.Size();\r
111   RINOZ_COMP(s1, s2);\r
112   int i;\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
120   return 0;\r
121 }\r
122 \r
123 /*\r
124 static int CompareFiles(const CFileItem &f1, const CFileItem &f2)\r
125 {\r
126   return MyStringCompareNoCase(f1.Name, f2.Name);\r
127 }\r
128 */\r
129 \r
130 struct CFolderRepack\r
131 {\r
132   int FolderIndex;\r
133   int Group;\r
134   CNum NumCopyFiles;\r
135 };\r
136 \r
137 static int CompareFolderRepacks(const CFolderRepack *p1, const CFolderRepack *p2, void *param)\r
138 {\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
144       db.Folders[i1],\r
145       db.Folders[i2]));\r
146   return MyCompare(i1, i2);\r
147   /*\r
148   RINOZ_COMP(\r
149       db.NumUnpackStreamsVector[i1],\r
150       db.NumUnpackStreamsVector[i2]);\r
151   if (db.NumUnpackStreamsVector[i1] == 0)\r
152     return 0;\r
153   return CompareFiles(\r
154       db.Files[db.FolderStartFileIndex[i1]],\r
155       db.Files[db.FolderStartFileIndex[i2]]);\r
156   */\r
157 }\r
158 \r
159 ////////////////////////////////////////////////////////////\r
160 \r
161 static int CompareEmptyItems(const int *p1, const int *p2, void *param)\r
162 {\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
168   if (u1.IsDir)\r
169   {\r
170     if (u1.IsAnti != u2.IsAnti)\r
171       return (u1.IsAnti ? 1 : -1);\r
172     int n = MyStringCompareNoCase(u1.Name, u2.Name);\r
173     return -n;\r
174   }\r
175   if (u1.IsAnti != u2.IsAnti)\r
176     return (u1.IsAnti ? 1 : -1);\r
177   return MyStringCompareNoCase(u1.Name, u2.Name);\r
178 }\r
179 \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
185   " swf "\r
186   " chm hxi hxs"\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
190   " max 3ds"\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
195   " f77 f f90 f95"\r
196   " asm sql manifest dep "\r
197   " mak clw csproj vcproj sln dsp dsw "\r
198   " class "\r
199   " bat cmd"\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
209 \r
210 int GetExtIndex(const char *ext)\r
211 {\r
212   int extIndex = 1;\r
213   const char *p = g_Exts;\r
214   for (;;)\r
215   {\r
216     char c = *p++;\r
217     if (c == 0)\r
218       return extIndex;\r
219     if (c == ' ')\r
220       continue;\r
221     int pos = 0;\r
222     for (;;)\r
223     {\r
224       char c2 = ext[pos++];\r
225       if (c2 == 0 && (c == 0 || c == ' '))\r
226         return extIndex;\r
227       if (c != c2)\r
228         break;\r
229       c = *p++;\r
230     }\r
231     extIndex++;\r
232     for (;;)\r
233     {\r
234       if (c == 0)\r
235         return extIndex;\r
236       if (c == ' ')\r
237         break;\r
238       c = *p++;\r
239     }\r
240   }\r
241 }\r
242 \r
243 struct CRefItem\r
244 {\r
245   const CUpdateItem *UpdateItem;\r
246   UInt32 Index;\r
247   UInt32 ExtensionPos;\r
248   UInt32 NamePos;\r
249   int ExtensionIndex;\r
250   CRefItem(UInt32 index, const CUpdateItem &ui, bool sortByType):\r
251     UpdateItem(&ui),\r
252     Index(index),\r
253     ExtensionPos(0),\r
254     NamePos(0),\r
255     ExtensionIndex(0)\r
256   {\r
257     if (sortByType)\r
258     {\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
264       else\r
265       {\r
266         ExtensionPos = dotPos + 1;\r
267         UString us = ui.Name.Mid(ExtensionPos);\r
268         if (!us.IsEmpty())\r
269         {\r
270           us.MakeLower();\r
271           int i;\r
272           AString s;\r
273           for (i = 0; i < us.Length(); i++)\r
274           {\r
275             wchar_t c = us[i];\r
276             if (c >= 0x80)\r
277               break;\r
278             s += (char)c;\r
279           }\r
280           if (i == us.Length())\r
281             ExtensionIndex = GetExtIndex(s);\r
282           else\r
283             ExtensionIndex = 0;\r
284         }\r
285       }\r
286     }\r
287   }\r
288 };\r
289 \r
290 static int CompareUpdateItems(const CRefItem *p1, const CRefItem *p2, void *param)\r
291 {\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
296   int n;\r
297   if (u1.IsDir != u2.IsDir)\r
298     return (u1.IsDir) ? 1 : -1;\r
299   if (u1.IsDir)\r
300   {\r
301     if (u1.IsAnti != u2.IsAnti)\r
302       return (u1.IsAnti ? 1 : -1);\r
303     n = MyStringCompareNoCase(u1.Name, u2.Name);\r
304     return -n;\r
305   }\r
306   bool sortByType = *(bool *)param;\r
307   if (sortByType)\r
308   {\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
316   }\r
317   return MyStringCompareNoCase(u1.Name, u2.Name);\r
318 }\r
319 \r
320 struct CSolidGroup\r
321 {\r
322   CRecordVector<UInt32> Indices;\r
323 };\r
324 \r
325 static wchar_t *g_ExeExts[] =\r
326 {\r
327   L"dll",\r
328   L"exe",\r
329   L"ocx",\r
330   L"sfx",\r
331   L"sys"\r
332 };\r
333 \r
334 static bool IsExeExt(const UString &ext)\r
335 {\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
338       return true;\r
339   return false;\r
340 }\r
341 \r
342 #ifdef USE_86_FILTER\r
343 \r
344 static inline void GetMethodFull(UInt64 methodID, UInt32 numInStreams, CMethodFull &methodResult)\r
345 {\r
346   methodResult.Id = methodID;\r
347   methodResult.NumInStreams = numInStreams;\r
348   methodResult.NumOutStreams = 1;\r
349 }\r
350 \r
351 static void MakeExeMethod(const CCompressionMethodMode &method,\r
352     bool bcj2Filter, CCompressionMethodMode &exeMethod)\r
353 {\r
354   exeMethod = method;\r
355   if (bcj2Filter)\r
356   {\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
361     {\r
362       CProp prop;\r
363       prop.Id = NCoderPropID::kAlgorithm;\r
364       prop.Value = kAlgorithmForBCJ2_LZMA;\r
365       methodFull.Props.Add(prop);\r
366     }\r
367     {\r
368       CProp prop;\r
369       prop.Id = NCoderPropID::kMatchFinder;\r
370       prop.Value = kMatchFinderForBCJ2_LZMA;\r
371       methodFull.Props.Add(prop);\r
372     }\r
373     {\r
374       CProp prop;\r
375       prop.Id = NCoderPropID::kDictionarySize;\r
376       prop.Value = kDictionaryForBCJ2_LZMA;\r
377       methodFull.Props.Add(prop);\r
378     }\r
379     {\r
380       CProp prop;\r
381       prop.Id = NCoderPropID::kNumFastBytes;\r
382       prop.Value = kNumFastBytesForBCJ2_LZMA;\r
383       methodFull.Props.Add(prop);\r
384     }\r
385     {\r
386       CProp prop;\r
387       prop.Id = NCoderPropID::kNumThreads;\r
388       prop.Value = (UInt32)1;\r
389       methodFull.Props.Add(prop);\r
390     }\r
391 \r
392     exeMethod.Methods.Add(methodFull);\r
393     exeMethod.Methods.Add(methodFull);\r
394     CBind bind;\r
395 \r
396     bind.OutCoder = 0;\r
397     bind.InStream = 0;\r
398 \r
399     bind.InCoder = 1;\r
400     bind.OutStream = 0;\r
401     exeMethod.Binds.Add(bind);\r
402 \r
403     bind.InCoder = 2;\r
404     bind.OutStream = 1;\r
405     exeMethod.Binds.Add(bind);\r
406 \r
407     bind.InCoder = 3;\r
408     bind.OutStream = 2;\r
409     exeMethod.Binds.Add(bind);\r
410   }\r
411   else\r
412   {\r
413     CMethodFull methodFull;\r
414     GetMethodFull(k_BCJ, 1, methodFull);\r
415     exeMethod.Methods.Insert(0, methodFull);\r
416     CBind bind;\r
417     bind.OutCoder = 0;\r
418     bind.InStream = 0;\r
419     bind.InCoder = 1;\r
420     bind.OutStream = 0;\r
421     exeMethod.Binds.Add(bind);\r
422   }\r
423 }\r
424 \r
425 #endif\r
426 \r
427 static void FromUpdateItemToFileItem(const CUpdateItem &ui,\r
428     CFileItem &file, CFileItem2 &file2)\r
429 {\r
430   file.Name = NItemName::MakeLegalName(ui.Name);\r
431   if (ui.AttribDefined)\r
432     file.SetAttrib(ui.Attrib);\r
433   \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
439 \r
440   file.Size = ui.Size;\r
441   file.IsDir = ui.IsDir;\r
442   file.HasStream = ui.HasStream();\r
443 }\r
444 \r
445 class CFolderOutStream2:\r
446   public ISequentialOutStream,\r
447   public CMyUnknownImp\r
448 {\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
455   int _currentIndex;\r
456   bool _fileIsOpen;\r
457   UInt64 _rem;\r
458 \r
459   void OpenFile();\r
460   void CloseFile();\r
461   HRESULT CloseFileAndSetResult();\r
462   HRESULT ProcessEmptyFiles();\r
463 public:\r
464   MY_UNKNOWN_IMP\r
465   \r
466   CFolderOutStream2()\r
467   {\r
468     _crcStreamSpec = new COutStreamWithCRC;\r
469     _crcStream = _crcStreamSpec;\r
470   }\r
471 \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
476 \r
477   STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);\r
478 };\r
479 \r
480 HRESULT CFolderOutStream2::Init(const CArchiveDatabaseEx *db, UInt32 startIndex,\r
481     const CBoolVector *extractStatuses, ISequentialOutStream *outStream)\r
482 {\r
483   _db = db;\r
484   _startIndex = startIndex;\r
485   _extractStatuses = extractStatuses;\r
486   _outStream = outStream;\r
487 \r
488   _currentIndex = 0;\r
489   _fileIsOpen = false;\r
490   return ProcessEmptyFiles();\r
491 }\r
492 \r
493 void CFolderOutStream2::ReleaseOutStream()\r
494 {\r
495   _outStream.Release();\r
496   _crcStreamSpec->ReleaseStream();\r
497 }\r
498 \r
499 void CFolderOutStream2::OpenFile()\r
500 {\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
505 }\r
506 \r
507 void CFolderOutStream2::CloseFile()\r
508 {\r
509   _crcStreamSpec->ReleaseStream();\r
510   _fileIsOpen = false;\r
511   _currentIndex++;\r
512 }\r
513 \r
514 HRESULT CFolderOutStream2::CloseFileAndSetResult()\r
515 {\r
516   const CFileItem &file = _db->Files[_startIndex + _currentIndex];\r
517   CloseFile();\r
518   return (file.IsDir || !file.CrcDefined || file.Crc == _crcStreamSpec->GetCRC()) ? S_OK: S_FALSE;\r
519 }\r
520 \r
521 HRESULT CFolderOutStream2::ProcessEmptyFiles()\r
522 {\r
523   while (_currentIndex < _extractStatuses->Size() && _db->Files[_startIndex + _currentIndex].Size == 0)\r
524   {\r
525     OpenFile();\r
526     RINOK(CloseFileAndSetResult());\r
527   }\r
528   return S_OK;\r
529 }\r
530 \r
531 STDMETHODIMP CFolderOutStream2::Write(const void *data, UInt32 size, UInt32 *processedSize)\r
532 {\r
533   if (processedSize != NULL)\r
534     *processedSize = 0;\r
535   while (size != 0)\r
536   {\r
537     if (_fileIsOpen)\r
538     {\r
539       UInt32 cur = size < _rem ? size : (UInt32)_rem;\r
540       RINOK(_crcStream->Write(data, cur, &cur));\r
541       if (cur == 0)\r
542         break;\r
543       data = (const Byte *)data + cur;\r
544       size -= cur;\r
545       _rem -= cur;\r
546       if (processedSize != NULL)\r
547         *processedSize += cur;\r
548       if (_rem == 0)\r
549       {\r
550         RINOK(CloseFileAndSetResult());\r
551         RINOK(ProcessEmptyFiles());\r
552         continue;\r
553       }\r
554     }\r
555     else\r
556     {\r
557       RINOK(ProcessEmptyFiles());\r
558       if (_currentIndex == _extractStatuses->Size())\r
559       {\r
560         // we don't support partial extracting\r
561         return E_FAIL;\r
562       }\r
563       OpenFile();\r
564     }\r
565   }\r
566   return S_OK;\r
567 }\r
568 \r
569 class CThreadDecoder: public CVirtThread\r
570 {\r
571 public:\r
572   HRESULT Result;\r
573   CMyComPtr<IInStream> InStream;\r
574 \r
575   CFolderOutStream2 *FosSpec;\r
576   CMyComPtr<ISequentialOutStream> Fos;\r
577 \r
578   UInt64 StartPos;\r
579   const UInt64 *PackSizes;\r
580   const CFolder *Folder;\r
581   #ifndef _NO_CRYPTO\r
582   CMyComPtr<ICryptoGetTextPassword> GetTextPassword;\r
583   #endif\r
584 \r
585   DECL_EXTERNAL_CODECS_VARS\r
586   CDecoder Decoder;\r
587 \r
588   #ifndef _7ZIP_ST\r
589   bool MtMode;\r
590   UInt32 NumThreads;\r
591   #endif\r
592 \r
593   CThreadDecoder():\r
594     Decoder(true)\r
595   {\r
596     #ifndef _7ZIP_ST\r
597     MtMode = false;\r
598     NumThreads = 1;\r
599     #endif\r
600     FosSpec = new CFolderOutStream2;\r
601     Fos = FosSpec;\r
602     Result = E_FAIL;\r
603   }\r
604   virtual void Execute();\r
605 };\r
606 \r
607 void CThreadDecoder::Execute()\r
608 {\r
609   try\r
610   {\r
611     #ifndef _NO_CRYPTO\r
612     bool passwordIsDefined;\r
613     #endif\r
614     Result = Decoder.Decode(\r
615       EXTERNAL_CODECS_VARS\r
616       InStream,\r
617       StartPos,\r
618       PackSizes,\r
619       *Folder,\r
620       Fos,\r
621       NULL\r
622       #ifndef _NO_CRYPTO\r
623       , GetTextPassword, passwordIsDefined\r
624       #endif\r
625       #ifndef _7ZIP_ST\r
626       , MtMode, NumThreads\r
627       #endif\r
628       );\r
629   }\r
630   catch(...)\r
631   {\r
632     Result = E_FAIL;\r
633   }\r
634   if (Result == S_OK)\r
635     Result = FosSpec->CheckFinishedState();\r
636   FosSpec->ReleaseOutStream();\r
637 }\r
638 \r
639 bool static Is86FilteredFolder(const CFolder &f)\r
640 {\r
641   for (int i = 0; i < f.Coders.Size(); i++)\r
642   {\r
643     CMethodId m = f.Coders[i].MethodID;\r
644     if (m == k_BCJ || m == k_BCJ2)\r
645       return true;\r
646   }\r
647   return false;\r
648 }\r
649 \r
650 #ifndef _NO_CRYPTO\r
651 \r
652 class CCryptoGetTextPassword:\r
653   public ICryptoGetTextPassword,\r
654   public CMyUnknownImp\r
655 {\r
656 public:\r
657   UString Password;\r
658 \r
659   MY_UNKNOWN_IMP\r
660   STDMETHOD(CryptoGetTextPassword)(BSTR *password);\r
661 };\r
662 \r
663 STDMETHODIMP CCryptoGetTextPassword::CryptoGetTextPassword(BSTR *password)\r
664 {\r
665   return StringToBstr(Password, password);\r
666 }\r
667 \r
668 #endif\r
669 \r
670 static const int kNumGroupsMax = 4;\r
671 \r
672 #ifdef USE_86_FILTER\r
673 static bool Is86Group(int group) { return (group & 1) != 0; }\r
674 #endif\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
678 \r
679 HRESULT Update(\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
689     #ifndef _NO_CRYPTO\r
690     , ICryptoGetTextPassword *getDecoderPassword\r
691     #endif\r
692     )\r
693 {\r
694   UInt64 numSolidFiles = options.NumSolidFiles;\r
695   if (numSolidFiles == 0)\r
696     numSolidFiles = 1;\r
697   /*\r
698   CMyComPtr<IOutStream> outStream;\r
699   RINOK(seqOutStream->QueryInterface(IID_IOutStream, (void **)&outStream));\r
700   if (!outStream)\r
701     return E_NOTIMPL;\r
702   */\r
703 \r
704   UInt64 startBlockSize = db != 0 ? db->ArchiveInfo.StartPosition: 0;\r
705   if (startBlockSize > 0 && !options.RemoveSfxBlock)\r
706   {\r
707     RINOK(WriteRange(inStream, seqOutStream, 0, startBlockSize, NULL));\r
708   }\r
709 \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
715   if (db != 0)\r
716   {\r
717     fileIndexToUpdateIndexMap.Reserve(db->Files.Size());\r
718     int i;\r
719     for (i = 0; i < db->Files.Size(); i++)\r
720       fileIndexToUpdateIndexMap.Add(-1);\r
721 \r
722     for (i = 0; i < updateItems.Size(); i++)\r
723     {\r
724       int index = updateItems[i].IndexInArchive;\r
725       if (index != -1)\r
726         fileIndexToUpdateIndexMap[index] = i;\r
727     }\r
728 \r
729     for (i = 0; i < db->Folders.Size(); i++)\r
730     {\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
736       {\r
737         const CFileItem &file = db->Files[fi];\r
738         if (file.HasStream)\r
739         {\r
740           indexInFolder++;\r
741           int updateIndex = fileIndexToUpdateIndexMap[fi];\r
742           if (updateIndex >= 0 && !updateItems[updateIndex].NewData)\r
743           {\r
744             numCopyItems++;\r
745             repackSize += file.Size;\r
746           }\r
747         }\r
748       }\r
749 \r
750       if (numCopyItems == 0)\r
751         continue;\r
752 \r
753       CFolderRepack rep;\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
762       else\r
763       {\r
764         complexity += repackSize;\r
765         if (repackSize > inSizeForReduce2)\r
766           inSizeForReduce2 = repackSize;\r
767         if (isEncrypted)\r
768           needEncryptedRepack = true;\r
769       }\r
770     }\r
771     folderRefs.Sort(CompareFolderRepacks, (void *)db);\r
772   }\r
773 \r
774   UInt64 inSizeForReduce = 0;\r
775   int i;\r
776   for (i = 0; i < updateItems.Size(); i++)\r
777   {\r
778     const CUpdateItem &ui = updateItems[i];\r
779     if (ui.NewData)\r
780     {\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
786     }\r
787   }\r
788 \r
789   if (inSizeForReduce2 > inSizeForReduce)\r
790     inSizeForReduce = inSizeForReduce2;\r
791 \r
792   const UInt32 kMinReduceSize = (1 << 16);\r
793   if (inSizeForReduce < kMinReduceSize)\r
794     inSizeForReduce = kMinReduceSize;\r
795 \r
796   RINOK(updateCallback->SetTotal(complexity));\r
797 \r
798   CLocalProgress *lps = new CLocalProgress;\r
799   CMyComPtr<ICompressProgressInfo> progress = lps;\r
800   lps->Init(updateCallback, true);\r
801 \r
802   CThreadDecoder threadDecoder;\r
803   if (!folderRefs.IsEmpty())\r
804   {\r
805     #ifdef EXTERNAL_CODECS\r
806     threadDecoder._codecsInfo = codecsInfo;\r
807     threadDecoder._externalCodecs = *externalCodecs;\r
808     #endif\r
809     RINOK(threadDecoder.Create());\r
810   }\r
811 \r
812   CObjectVector<CSolidGroup> groups;\r
813   for (i = 0; i < kNumGroupsMax; i++)\r
814     groups.Add(CSolidGroup());\r
815 \r
816   {\r
817     // ---------- Split files to 2 groups ----------\r
818 \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
824     {\r
825       const CUpdateItem &ui = updateItems[i];\r
826       if (!ui.NewData || !ui.HasStream())\r
827         continue;\r
828       bool filteredGroup = false;\r
829       if (useFilters)\r
830       {\r
831         int dotPos = ui.Name.ReverseFind(L'.');\r
832         if (dotPos >= 0)\r
833           filteredGroup = IsExeExt(ui.Name.Mid(dotPos + 1));\r
834       }\r
835       groups[GetGroupIndex(method.PasswordIsDefined, filteredGroup)].Indices.Add(i);\r
836     }\r
837   }\r
838 \r
839   #ifndef _NO_CRYPTO\r
840 \r
841   CCryptoGetTextPassword *getPasswordSpec = NULL;\r
842   if (needEncryptedRepack)\r
843   {\r
844     getPasswordSpec = new CCryptoGetTextPassword;\r
845     threadDecoder.GetTextPassword = getPasswordSpec;\r
846 \r
847     if (options.Method->PasswordIsDefined)\r
848       getPasswordSpec->Password = options.Method->Password;\r
849     else\r
850     {\r
851       if (!getDecoderPassword)\r
852         return E_NOTIMPL;\r
853       CMyComBSTR password;\r
854       RINOK(getDecoderPassword->CryptoGetTextPassword(&password));\r
855       getPasswordSpec->Password = password;\r
856     }\r
857   }\r
858 \r
859   #endif\r
860 \r
861   // ---------- Compress ----------\r
862 \r
863   RINOK(archive.Create(seqOutStream, false));\r
864   RINOK(archive.SkipPrefixArchiveHeader());\r
865 \r
866   int folderRefIndex = 0;\r
867   lps->ProgressOffset = 0;\r
868 \r
869   for (int groupIndex = 0; groupIndex < kNumGroupsMax; groupIndex++)\r
870   {\r
871     const CSolidGroup &group = groups[groupIndex];\r
872 \r
873     CCompressionMethodMode method;\r
874     #ifdef USE_86_FILTER\r
875     if (Is86Group(groupIndex))\r
876       MakeExeMethod(*options.Method, options.MaxFilter, method);\r
877     else\r
878     #endif\r
879       method = *options.Method;\r
880 \r
881     if (IsEncryptedGroup(groupIndex))\r
882     {\r
883       if (!method.PasswordIsDefined)\r
884       {\r
885         #ifndef _NO_CRYPTO\r
886         if (getPasswordSpec)\r
887           method.Password = getPasswordSpec->Password;\r
888         #endif\r
889         method.PasswordIsDefined = true;\r
890       }\r
891     }\r
892     else\r
893     {\r
894       method.PasswordIsDefined = false;\r
895       method.Password.Empty();\r
896     }\r
897 \r
898     CEncoder encoder(method);\r
899 \r
900     for (; folderRefIndex < folderRefs.Size(); folderRefIndex++)\r
901     {\r
902       const CFolderRepack &rep = folderRefs[folderRefIndex];\r
903       if (rep.Group != groupIndex)\r
904         break;\r
905       int folderIndex = rep.FolderIndex;\r
906       \r
907       if (rep.NumCopyFiles == db->NumUnpackStreamsVector[folderIndex])\r
908       {\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
913         \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
917         {\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
921         }\r
922         newDatabase.Folders.Add(folder);\r
923       }\r
924       else\r
925       {\r
926         CStreamBinder sb;\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
932         \r
933         CNum numUnpackStreams = db->NumUnpackStreamsVector[folderIndex];\r
934         CNum indexInFolder = 0;\r
935         \r
936         for (CNum fi = db->FolderStartFileIndex[folderIndex]; indexInFolder < numUnpackStreams; fi++)\r
937         {\r
938           bool needExtract = false;\r
939           if (db->Files[fi].HasStream)\r
940           {\r
941             indexInFolder++;\r
942             int updateIndex = fileIndexToUpdateIndexMap[fi];\r
943             if (updateIndex >= 0 && !updateItems[updateIndex].NewData)\r
944               needExtract = true;\r
945           }\r
946           extractStatuses.Add(needExtract);\r
947         }\r
948 \r
949         RINOK(threadDecoder.FosSpec->Init(db, db->FolderStartFileIndex[folderIndex], &extractStatuses, sbOutStream));\r
950         sbOutStream.Release();\r
951         \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
956         \r
957         threadDecoder.Start();\r
958         \r
959         int startPackIndex = newDatabase.PackSizes.Size();\r
960         CFolder newFolder;\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
965         \r
966         threadDecoder.WaitFinish();\r
967 \r
968         RINOK(threadDecoder.Result);\r
969 \r
970         for (; startPackIndex < newDatabase.PackSizes.Size(); startPackIndex++)\r
971           lps->OutSize += newDatabase.PackSizes[startPackIndex];\r
972         lps->InSize += newFolder.GetUnpackSize();\r
973         \r
974         newDatabase.Folders.Add(newFolder);\r
975       }\r
976       \r
977       newDatabase.NumUnpackStreamsVector.Add(rep.NumCopyFiles);\r
978       \r
979       CNum numUnpackStreams = db->NumUnpackStreamsVector[folderIndex];\r
980       \r
981       CNum indexInFolder = 0;\r
982       for (CNum fi = db->FolderStartFileIndex[folderIndex]; indexInFolder < numUnpackStreams; fi++)\r
983       {\r
984         CFileItem file;\r
985         CFileItem2 file2;\r
986         db->GetFile(fi, file, file2);\r
987         if (file.HasStream)\r
988         {\r
989           indexInFolder++;\r
990           int updateIndex = fileIndexToUpdateIndexMap[fi];\r
991           if (updateIndex >= 0)\r
992           {\r
993             const CUpdateItem &ui = updateItems[updateIndex];\r
994             if (ui.NewData)\r
995               continue;\r
996             if (ui.NewProps)\r
997             {\r
998               CFileItem uf;\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
1004               file = uf;\r
1005             }\r
1006             newDatabase.AddFile(file, file2);\r
1007           }\r
1008         }\r
1009       }\r
1010     }\r
1011 \r
1012     int numFiles = group.Indices.Size();\r
1013     if (numFiles == 0)\r
1014       continue;\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
1021     \r
1022     CRecordVector<UInt32> indices;\r
1023     indices.Reserve(numFiles);\r
1024 \r
1025     for (i = 0; i < numFiles; i++)\r
1026     {\r
1027       UInt32 index = refItems[i].Index;\r
1028       indices.Add(index);\r
1029       /*\r
1030       const CUpdateItem &ui = updateItems[index];\r
1031       CFileItem file;\r
1032       if (ui.NewProps)\r
1033         FromUpdateItemToFileItem(ui, file);\r
1034       else\r
1035         file = db.Files[ui.IndexInArchive];\r
1036       if (file.IsAnti || file.IsDir)\r
1037         return E_FAIL;\r
1038       newDatabase.Files.Add(file);\r
1039       */\r
1040     }\r
1041     \r
1042     for (i = 0; i < numFiles;)\r
1043     {\r
1044       UInt64 totalSize = 0;\r
1045       int numSubFiles;\r
1046       UString prevExtension;\r
1047       for (numSubFiles = 0; i + numSubFiles < numFiles &&\r
1048           numSubFiles < numSolidFiles; numSubFiles++)\r
1049       {\r
1050         const CUpdateItem &ui = updateItems[indices[i + numSubFiles]];\r
1051         totalSize += ui.Size;\r
1052         if (totalSize > options.NumSolidBytes)\r
1053           break;\r
1054         if (options.SolidExtension)\r
1055         {\r
1056           UString ext = ui.GetExtension();\r
1057           if (numSubFiles == 0)\r
1058             prevExtension = ext;\r
1059           else\r
1060             if (ext.CompareNoCase(prevExtension) != 0)\r
1061               break;\r
1062         }\r
1063       }\r
1064       if (numSubFiles < 1)\r
1065         numSubFiles = 1;\r
1066 \r
1067       CFolderInStream *inStreamSpec = new CFolderInStream;\r
1068       CMyComPtr<ISequentialInStream> solidInStream(inStreamSpec);\r
1069       inStreamSpec->Init(updateCallback, &indices[i], numSubFiles);\r
1070       \r
1071       CFolder folderItem;\r
1072 \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
1078 \r
1079       for (; startPackIndex < newDatabase.PackSizes.Size(); startPackIndex++)\r
1080         lps->OutSize += newDatabase.PackSizes[startPackIndex];\r
1081 \r
1082       lps->InSize += folderItem.GetUnpackSize();\r
1083       // for ()\r
1084       // newDatabase.PackCRCsDefined.Add(false);\r
1085       // newDatabase.PackCRCs.Add(0);\r
1086       \r
1087       newDatabase.Folders.Add(folderItem);\r
1088       \r
1089       CNum numUnpackStreams = 0;\r
1090       for (int subIndex = 0; subIndex < numSubFiles; subIndex++)\r
1091       {\r
1092         const CUpdateItem &ui = updateItems[indices[i + subIndex]];\r
1093         CFileItem file;\r
1094         CFileItem2 file2;\r
1095         if (ui.NewProps)\r
1096           FromUpdateItemToFileItem(ui, file, file2);\r
1097         else\r
1098           db->GetFile(ui.IndexInArchive, file, file2);\r
1099         if (file2.IsAnti || file.IsDir)\r
1100           return E_FAIL;\r
1101         \r
1102         /*\r
1103         CFileItem &file = newDatabase.Files[\r
1104               startFileIndexInDatabase + i + subIndex];\r
1105         */\r
1106         if (!inStreamSpec->Processed[subIndex])\r
1107         {\r
1108           continue;\r
1109           // file.Name += L".locked";\r
1110         }\r
1111 \r
1112         file.Crc = inStreamSpec->CRCs[subIndex];\r
1113         file.Size = inStreamSpec->Sizes[subIndex];\r
1114         if (file.Size != 0)\r
1115         {\r
1116           file.CrcDefined = true;\r
1117           file.HasStream = true;\r
1118           numUnpackStreams++;\r
1119         }\r
1120         else\r
1121         {\r
1122           file.CrcDefined = false;\r
1123           file.HasStream = false;\r
1124         }\r
1125         newDatabase.AddFile(file, file2);\r
1126       }\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
1130       i += numSubFiles;\r
1131     }\r
1132   }\r
1133 \r
1134   if (folderRefIndex != folderRefs.Size())\r
1135     return E_FAIL;\r
1136 \r
1137   /*\r
1138   folderRefs.ClearAndFree();\r
1139   fileIndexToUpdateIndexMap.ClearAndFree();\r
1140   groups.ClearAndFree();\r
1141   */\r
1142 \r
1143   {\r
1144     // ---------- Write Folders & Empty Files ----------\r
1145     \r
1146     CRecordVector<int> emptyRefs;\r
1147     for (i = 0; i < updateItems.Size(); i++)\r
1148     {\r
1149       const CUpdateItem &ui = updateItems[i];\r
1150       if (ui.NewData)\r
1151       {\r
1152         if (ui.HasStream())\r
1153           continue;\r
1154       }\r
1155       else if (ui.IndexInArchive != -1 && db->Files[ui.IndexInArchive].HasStream)\r
1156         continue;\r
1157       emptyRefs.Add(i);\r
1158     }\r
1159     emptyRefs.Sort(CompareEmptyItems, (void *)&updateItems);\r
1160     for (i = 0; i < emptyRefs.Size(); i++)\r
1161     {\r
1162       const CUpdateItem &ui = updateItems[emptyRefs[i]];\r
1163       CFileItem file;\r
1164       CFileItem2 file2;\r
1165       if (ui.NewProps)\r
1166         FromUpdateItemToFileItem(ui, file, file2);\r
1167       else\r
1168         db->GetFile(ui.IndexInArchive, file, file2);\r
1169       newDatabase.AddFile(file, file2);\r
1170     }\r
1171   }\r
1172     \r
1173   newDatabase.ReserveDown();\r
1174   return S_OK;\r
1175 }\r
1176 \r
1177 }}\r