Home | History | Annotate | Download | only in 7z
      1 // 7zUpdate.cpp
      2 
      3 #include "StdAfx.h"
      4 
      5 #include "../../../../C/CpuArch.h"
      6 
      7 #include "../../Common/LimitedStreams.h"
      8 #include "../../Common/ProgressUtils.h"
      9 
     10 #include "../../Common/CreateCoder.h"
     11 
     12 #include "../../Compress/CopyCoder.h"
     13 
     14 #include "../Common/ItemNameUtils.h"
     15 #include "../Common/OutStreamWithCRC.h"
     16 
     17 #include "7zDecode.h"
     18 #include "7zEncode.h"
     19 #include "7zFolderInStream.h"
     20 #include "7zHandler.h"
     21 #include "7zOut.h"
     22 #include "7zUpdate.h"
     23 
     24 namespace NArchive {
     25 namespace N7z {
     26 
     27 static const UInt64 k_LZMA = 0x030101;
     28 static const UInt64 k_BCJ  = 0x03030103;
     29 static const UInt64 k_BCJ2 = 0x0303011B;
     30 
     31 static const wchar_t *kMatchFinderForBCJ2_LZMA = L"BT2";
     32 static const UInt32 kDictionaryForBCJ2_LZMA = 1 << 20;
     33 static const UInt32 kAlgorithmForBCJ2_LZMA = 1;
     34 static const UInt32 kNumFastBytesForBCJ2_LZMA = 64;
     35 
     36 #ifdef MY_CPU_X86_OR_AMD64
     37 #define USE_86_FILTER
     38 #endif
     39 
     40 static HRESULT WriteRange(IInStream *inStream, ISequentialOutStream *outStream,
     41     UInt64 position, UInt64 size, ICompressProgressInfo *progress)
     42 {
     43   RINOK(inStream->Seek(position, STREAM_SEEK_SET, 0));
     44   CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
     45   CMyComPtr<CLimitedSequentialInStream> inStreamLimited(streamSpec);
     46   streamSpec->SetStream(inStream);
     47   streamSpec->Init(size);
     48 
     49   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder;
     50   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
     51   RINOK(copyCoder->Code(inStreamLimited, outStream, NULL, NULL, progress));
     52   return (copyCoderSpec->TotalSize == size ? S_OK : E_FAIL);
     53 }
     54 
     55 static int GetReverseSlashPos(const UString &name)
     56 {
     57   int slashPos = name.ReverseFind(L'/');
     58   #ifdef _WIN32
     59   int slash1Pos = name.ReverseFind(L'\\');
     60   slashPos = MyMax(slashPos, slash1Pos);
     61   #endif
     62   return slashPos;
     63 }
     64 
     65 int CUpdateItem::GetExtensionPos() const
     66 {
     67   int slashPos = GetReverseSlashPos(Name);
     68   int dotPos = Name.ReverseFind(L'.');
     69   if (dotPos < 0 || (dotPos < slashPos && slashPos >= 0))
     70     return Name.Length();
     71   return dotPos + 1;
     72 }
     73 
     74 UString CUpdateItem::GetExtension() const
     75 {
     76   return Name.Mid(GetExtensionPos());
     77 }
     78 
     79 #define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; }
     80 
     81 #define RINOZ_COMP(a, b) RINOZ(MyCompare(a, b))
     82 
     83 static int CompareBuffers(const CByteBuffer &a1, const CByteBuffer &a2)
     84 {
     85   size_t c1 = a1.GetCapacity();
     86   size_t c2 = a2.GetCapacity();
     87   RINOZ_COMP(c1, c2);
     88   for (size_t i = 0; i < c1; i++)
     89     RINOZ_COMP(a1[i], a2[i]);
     90   return 0;
     91 }
     92 
     93 static int CompareCoders(const CCoderInfo &c1, const CCoderInfo &c2)
     94 {
     95   RINOZ_COMP(c1.NumInStreams, c2.NumInStreams);
     96   RINOZ_COMP(c1.NumOutStreams, c2.NumOutStreams);
     97   RINOZ_COMP(c1.MethodID, c2.MethodID);
     98   return CompareBuffers(c1.Props, c2.Props);
     99 }
    100 
    101 static int CompareBindPairs(const CBindPair &b1, const CBindPair &b2)
    102 {
    103   RINOZ_COMP(b1.InIndex, b2.InIndex);
    104   return MyCompare(b1.OutIndex, b2.OutIndex);
    105 }
    106 
    107 static int CompareFolders(const CFolder &f1, const CFolder &f2)
    108 {
    109   int s1 = f1.Coders.Size();
    110   int s2 = f2.Coders.Size();
    111   RINOZ_COMP(s1, s2);
    112   int i;
    113   for (i = 0; i < s1; i++)
    114     RINOZ(CompareCoders(f1.Coders[i], f2.Coders[i]));
    115   s1 = f1.BindPairs.Size();
    116   s2 = f2.BindPairs.Size();
    117   RINOZ_COMP(s1, s2);
    118   for (i = 0; i < s1; i++)
    119     RINOZ(CompareBindPairs(f1.BindPairs[i], f2.BindPairs[i]));
    120   return 0;
    121 }
    122 
    123 /*
    124 static int CompareFiles(const CFileItem &f1, const CFileItem &f2)
    125 {
    126   return MyStringCompareNoCase(f1.Name, f2.Name);
    127 }
    128 */
    129 
    130 struct CFolderRepack
    131 {
    132   int FolderIndex;
    133   int Group;
    134   CNum NumCopyFiles;
    135 };
    136 
    137 static int CompareFolderRepacks(const CFolderRepack *p1, const CFolderRepack *p2, void *param)
    138 {
    139   RINOZ_COMP(p1->Group, p2->Group);
    140   int i1 = p1->FolderIndex;
    141   int i2 = p2->FolderIndex;
    142   const CArchiveDatabaseEx &db = *(const CArchiveDatabaseEx *)param;
    143   RINOZ(CompareFolders(
    144       db.Folders[i1],
    145       db.Folders[i2]));
    146   return MyCompare(i1, i2);
    147   /*
    148   RINOZ_COMP(
    149       db.NumUnpackStreamsVector[i1],
    150       db.NumUnpackStreamsVector[i2]);
    151   if (db.NumUnpackStreamsVector[i1] == 0)
    152     return 0;
    153   return CompareFiles(
    154       db.Files[db.FolderStartFileIndex[i1]],
    155       db.Files[db.FolderStartFileIndex[i2]]);
    156   */
    157 }
    158 
    159 ////////////////////////////////////////////////////////////
    160 
    161 static int CompareEmptyItems(const int *p1, const int *p2, void *param)
    162 {
    163   const CObjectVector<CUpdateItem> &updateItems = *(const CObjectVector<CUpdateItem> *)param;
    164   const CUpdateItem &u1 = updateItems[*p1];
    165   const CUpdateItem &u2 = updateItems[*p2];
    166   if (u1.IsDir != u2.IsDir)
    167     return (u1.IsDir) ? 1 : -1;
    168   if (u1.IsDir)
    169   {
    170     if (u1.IsAnti != u2.IsAnti)
    171       return (u1.IsAnti ? 1 : -1);
    172     int n = MyStringCompareNoCase(u1.Name, u2.Name);
    173     return -n;
    174   }
    175   if (u1.IsAnti != u2.IsAnti)
    176     return (u1.IsAnti ? 1 : -1);
    177   return MyStringCompareNoCase(u1.Name, u2.Name);
    178 }
    179 
    180 static const char *g_Exts =
    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"
    182   " zip jar ear war msi"
    183   " 3gp avi mov mpeg mpg mpe wmv"
    184   " aac ape fla flac la mp3 m4a mp4 ofr ogg pac ra rm rka shn swa tta wv wma wav"
    185   " swf "
    186   " chm hxi hxs"
    187   " gif jpeg jpg jp2 png tiff  bmp ico psd psp"
    188   " awg ps eps cgm dxf svg vrml wmf emf ai md"
    189   " cad dwg pps key sxi"
    190   " max 3ds"
    191   " iso bin nrg mdf img pdi tar cpio xpi"
    192   " vfd vhd vud vmc vsv"
    193   " vmdk dsk nvram vmem vmsd vmsn vmss vmtm"
    194   " inl inc idl acf asa h hpp hxx c cpp cxx rc java cs pas bas vb cls ctl frm dlg def"
    195   " f77 f f90 f95"
    196   " asm sql manifest dep "
    197   " mak clw csproj vcproj sln dsp dsw "
    198   " class "
    199   " bat cmd"
    200   " xml xsd xsl xslt hxk hxc htm html xhtml xht mht mhtml htw asp aspx css cgi jsp shtml"
    201   " awk sed hta js php php3 php4 php5 phptml pl pm py pyo rb sh tcl vbs"
    202   " text txt tex ans asc srt reg ini doc docx mcw dot rtf hlp xls xlr xlt xlw ppt pdf"
    203   " sxc sxd sxi sxg sxw stc sti stw stm odt ott odg otg odp otp ods ots odf"
    204   " abw afp cwk lwp wpd wps wpt wrf wri"
    205   " abf afm bdf fon mgf otf pcf pfa snf ttf"
    206   " dbf mdb nsf ntf wdb db fdb gdb"
    207   " exe dll ocx vbx sfx sys tlb awx com obj lib out o so "
    208   " pdb pch idb ncb opt";
    209 
    210 int GetExtIndex(const char *ext)
    211 {
    212   int extIndex = 1;
    213   const char *p = g_Exts;
    214   for (;;)
    215   {
    216     char c = *p++;
    217     if (c == 0)
    218       return extIndex;
    219     if (c == ' ')
    220       continue;
    221     int pos = 0;
    222     for (;;)
    223     {
    224       char c2 = ext[pos++];
    225       if (c2 == 0 && (c == 0 || c == ' '))
    226         return extIndex;
    227       if (c != c2)
    228         break;
    229       c = *p++;
    230     }
    231     extIndex++;
    232     for (;;)
    233     {
    234       if (c == 0)
    235         return extIndex;
    236       if (c == ' ')
    237         break;
    238       c = *p++;
    239     }
    240   }
    241 }
    242 
    243 struct CRefItem
    244 {
    245   const CUpdateItem *UpdateItem;
    246   UInt32 Index;
    247   UInt32 ExtensionPos;
    248   UInt32 NamePos;
    249   int ExtensionIndex;
    250   CRefItem(UInt32 index, const CUpdateItem &ui, bool sortByType):
    251     UpdateItem(&ui),
    252     Index(index),
    253     ExtensionPos(0),
    254     NamePos(0),
    255     ExtensionIndex(0)
    256   {
    257     if (sortByType)
    258     {
    259       int slashPos = GetReverseSlashPos(ui.Name);
    260       NamePos = ((slashPos >= 0) ? (slashPos + 1) : 0);
    261       int dotPos = ui.Name.ReverseFind(L'.');
    262       if (dotPos < 0 || (dotPos < slashPos && slashPos >= 0))
    263         ExtensionPos = ui.Name.Length();
    264       else
    265       {
    266         ExtensionPos = dotPos + 1;
    267         UString us = ui.Name.Mid(ExtensionPos);
    268         if (!us.IsEmpty())
    269         {
    270           us.MakeLower();
    271           int i;
    272           AString s;
    273           for (i = 0; i < us.Length(); i++)
    274           {
    275             wchar_t c = us[i];
    276             if (c >= 0x80)
    277               break;
    278             s += (char)c;
    279           }
    280           if (i == us.Length())
    281             ExtensionIndex = GetExtIndex(s);
    282           else
    283             ExtensionIndex = 0;
    284         }
    285       }
    286     }
    287   }
    288 };
    289 
    290 static int CompareUpdateItems(const CRefItem *p1, const CRefItem *p2, void *param)
    291 {
    292   const CRefItem &a1 = *p1;
    293   const CRefItem &a2 = *p2;
    294   const CUpdateItem &u1 = *a1.UpdateItem;
    295   const CUpdateItem &u2 = *a2.UpdateItem;
    296   int n;
    297   if (u1.IsDir != u2.IsDir)
    298     return (u1.IsDir) ? 1 : -1;
    299   if (u1.IsDir)
    300   {
    301     if (u1.IsAnti != u2.IsAnti)
    302       return (u1.IsAnti ? 1 : -1);
    303     n = MyStringCompareNoCase(u1.Name, u2.Name);
    304     return -n;
    305   }
    306   bool sortByType = *(bool *)param;
    307   if (sortByType)
    308   {
    309     RINOZ_COMP(a1.ExtensionIndex, a2.ExtensionIndex);
    310     RINOZ(MyStringCompareNoCase(u1.Name + a1.ExtensionPos, u2.Name + a2.ExtensionPos));
    311     RINOZ(MyStringCompareNoCase(u1.Name + a1.NamePos, u2.Name + a2.NamePos));
    312     if (!u1.MTimeDefined && u2.MTimeDefined) return 1;
    313     if (u1.MTimeDefined && !u2.MTimeDefined) return -1;
    314     if (u1.MTimeDefined && u2.MTimeDefined) RINOZ_COMP(u1.MTime, u2.MTime);
    315     RINOZ_COMP(u1.Size, u2.Size);
    316   }
    317   return MyStringCompareNoCase(u1.Name, u2.Name);
    318 }
    319 
    320 struct CSolidGroup
    321 {
    322   CRecordVector<UInt32> Indices;
    323 };
    324 
    325 static wchar_t *g_ExeExts[] =
    326 {
    327   L"dll",
    328   L"exe",
    329   L"ocx",
    330   L"sfx",
    331   L"sys"
    332 };
    333 
    334 static bool IsExeExt(const UString &ext)
    335 {
    336   for (int i = 0; i < sizeof(g_ExeExts) / sizeof(g_ExeExts[0]); i++)
    337     if (ext.CompareNoCase(g_ExeExts[i]) == 0)
    338       return true;
    339   return false;
    340 }
    341 
    342 #ifdef USE_86_FILTER
    343 
    344 static inline void GetMethodFull(UInt64 methodID, UInt32 numInStreams, CMethodFull &methodResult)
    345 {
    346   methodResult.Id = methodID;
    347   methodResult.NumInStreams = numInStreams;
    348   methodResult.NumOutStreams = 1;
    349 }
    350 
    351 static void MakeExeMethod(const CCompressionMethodMode &method,
    352     bool bcj2Filter, CCompressionMethodMode &exeMethod)
    353 {
    354   exeMethod = method;
    355   if (bcj2Filter)
    356   {
    357     CMethodFull methodFull;
    358     GetMethodFull(k_BCJ2, 4, methodFull);
    359     exeMethod.Methods.Insert(0, methodFull);
    360     GetMethodFull(k_LZMA, 1, methodFull);
    361     {
    362       CProp prop;
    363       prop.Id = NCoderPropID::kAlgorithm;
    364       prop.Value = kAlgorithmForBCJ2_LZMA;
    365       methodFull.Props.Add(prop);
    366     }
    367     {
    368       CProp prop;
    369       prop.Id = NCoderPropID::kMatchFinder;
    370       prop.Value = kMatchFinderForBCJ2_LZMA;
    371       methodFull.Props.Add(prop);
    372     }
    373     {
    374       CProp prop;
    375       prop.Id = NCoderPropID::kDictionarySize;
    376       prop.Value = kDictionaryForBCJ2_LZMA;
    377       methodFull.Props.Add(prop);
    378     }
    379     {
    380       CProp prop;
    381       prop.Id = NCoderPropID::kNumFastBytes;
    382       prop.Value = kNumFastBytesForBCJ2_LZMA;
    383       methodFull.Props.Add(prop);
    384     }
    385     {
    386       CProp prop;
    387       prop.Id = NCoderPropID::kNumThreads;
    388       prop.Value = (UInt32)1;
    389       methodFull.Props.Add(prop);
    390     }
    391 
    392     exeMethod.Methods.Add(methodFull);
    393     exeMethod.Methods.Add(methodFull);
    394     CBind bind;
    395 
    396     bind.OutCoder = 0;
    397     bind.InStream = 0;
    398 
    399     bind.InCoder = 1;
    400     bind.OutStream = 0;
    401     exeMethod.Binds.Add(bind);
    402 
    403     bind.InCoder = 2;
    404     bind.OutStream = 1;
    405     exeMethod.Binds.Add(bind);
    406 
    407     bind.InCoder = 3;
    408     bind.OutStream = 2;
    409     exeMethod.Binds.Add(bind);
    410   }
    411   else
    412   {
    413     CMethodFull methodFull;
    414     GetMethodFull(k_BCJ, 1, methodFull);
    415     exeMethod.Methods.Insert(0, methodFull);
    416     CBind bind;
    417     bind.OutCoder = 0;
    418     bind.InStream = 0;
    419     bind.InCoder = 1;
    420     bind.OutStream = 0;
    421     exeMethod.Binds.Add(bind);
    422   }
    423 }
    424 
    425 #endif
    426 
    427 static void FromUpdateItemToFileItem(const CUpdateItem &ui,
    428     CFileItem &file, CFileItem2 &file2)
    429 {
    430   file.Name = NItemName::MakeLegalName(ui.Name);
    431   if (ui.AttribDefined)
    432     file.SetAttrib(ui.Attrib);
    433 
    434   file2.CTime = ui.CTime;  file2.CTimeDefined = ui.CTimeDefined;
    435   file2.ATime = ui.ATime;  file2.ATimeDefined = ui.ATimeDefined;
    436   file2.MTime = ui.MTime;  file2.MTimeDefined = ui.MTimeDefined;
    437   file2.IsAnti = ui.IsAnti;
    438   file2.StartPosDefined = false;
    439 
    440   file.Size = ui.Size;
    441   file.IsDir = ui.IsDir;
    442   file.HasStream = ui.HasStream();
    443 }
    444 
    445 class CFolderOutStream2:
    446   public ISequentialOutStream,
    447   public CMyUnknownImp
    448 {
    449   COutStreamWithCRC *_crcStreamSpec;
    450   CMyComPtr<ISequentialOutStream> _crcStream;
    451   const CArchiveDatabaseEx *_db;
    452   const CBoolVector *_extractStatuses;
    453   CMyComPtr<ISequentialOutStream> _outStream;
    454   UInt32 _startIndex;
    455   int _currentIndex;
    456   bool _fileIsOpen;
    457   UInt64 _rem;
    458 
    459   void OpenFile();
    460   void CloseFile();
    461   HRESULT CloseFileAndSetResult();
    462   HRESULT ProcessEmptyFiles();
    463 public:
    464   MY_UNKNOWN_IMP
    465 
    466   CFolderOutStream2()
    467   {
    468     _crcStreamSpec = new COutStreamWithCRC;
    469     _crcStream = _crcStreamSpec;
    470   }
    471 
    472   HRESULT Init(const CArchiveDatabaseEx *db, UInt32 startIndex,
    473       const CBoolVector *extractStatuses, ISequentialOutStream *outStream);
    474   void ReleaseOutStream();
    475   HRESULT CheckFinishedState() const { return (_currentIndex == _extractStatuses->Size()) ? S_OK: E_FAIL; }
    476 
    477   STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
    478 };
    479 
    480 HRESULT CFolderOutStream2::Init(const CArchiveDatabaseEx *db, UInt32 startIndex,
    481     const CBoolVector *extractStatuses, ISequentialOutStream *outStream)
    482 {
    483   _db = db;
    484   _startIndex = startIndex;
    485   _extractStatuses = extractStatuses;
    486   _outStream = outStream;
    487 
    488   _currentIndex = 0;
    489   _fileIsOpen = false;
    490   return ProcessEmptyFiles();
    491 }
    492 
    493 void CFolderOutStream2::ReleaseOutStream()
    494 {
    495   _outStream.Release();
    496   _crcStreamSpec->ReleaseStream();
    497 }
    498 
    499 void CFolderOutStream2::OpenFile()
    500 {
    501   _crcStreamSpec->SetStream((*_extractStatuses)[_currentIndex] ? _outStream : NULL);
    502   _crcStreamSpec->Init(true);
    503   _fileIsOpen = true;
    504   _rem = _db->Files[_startIndex + _currentIndex].Size;
    505 }
    506 
    507 void CFolderOutStream2::CloseFile()
    508 {
    509   _crcStreamSpec->ReleaseStream();
    510   _fileIsOpen = false;
    511   _currentIndex++;
    512 }
    513 
    514 HRESULT CFolderOutStream2::CloseFileAndSetResult()
    515 {
    516   const CFileItem &file = _db->Files[_startIndex + _currentIndex];
    517   CloseFile();
    518   return (file.IsDir || !file.CrcDefined || file.Crc == _crcStreamSpec->GetCRC()) ? S_OK: S_FALSE;
    519 }
    520 
    521 HRESULT CFolderOutStream2::ProcessEmptyFiles()
    522 {
    523   while (_currentIndex < _extractStatuses->Size() && _db->Files[_startIndex + _currentIndex].Size == 0)
    524   {
    525     OpenFile();
    526     RINOK(CloseFileAndSetResult());
    527   }
    528   return S_OK;
    529 }
    530 
    531 STDMETHODIMP CFolderOutStream2::Write(const void *data, UInt32 size, UInt32 *processedSize)
    532 {
    533   if (processedSize != NULL)
    534     *processedSize = 0;
    535   while (size != 0)
    536   {
    537     if (_fileIsOpen)
    538     {
    539       UInt32 cur = size < _rem ? size : (UInt32)_rem;
    540       RINOK(_crcStream->Write(data, cur, &cur));
    541       if (cur == 0)
    542         break;
    543       data = (const Byte *)data + cur;
    544       size -= cur;
    545       _rem -= cur;
    546       if (processedSize != NULL)
    547         *processedSize += cur;
    548       if (_rem == 0)
    549       {
    550         RINOK(CloseFileAndSetResult());
    551         RINOK(ProcessEmptyFiles());
    552         continue;
    553       }
    554     }
    555     else
    556     {
    557       RINOK(ProcessEmptyFiles());
    558       if (_currentIndex == _extractStatuses->Size())
    559       {
    560         // we don't support partial extracting
    561         return E_FAIL;
    562       }
    563       OpenFile();
    564     }
    565   }
    566   return S_OK;
    567 }
    568 
    569 class CThreadDecoder: public CVirtThread
    570 {
    571 public:
    572   HRESULT Result;
    573   CMyComPtr<IInStream> InStream;
    574 
    575   CFolderOutStream2 *FosSpec;
    576   CMyComPtr<ISequentialOutStream> Fos;
    577 
    578   UInt64 StartPos;
    579   const UInt64 *PackSizes;
    580   const CFolder *Folder;
    581   #ifndef _NO_CRYPTO
    582   CMyComPtr<ICryptoGetTextPassword> GetTextPassword;
    583   #endif
    584 
    585   DECL_EXTERNAL_CODECS_VARS
    586   CDecoder Decoder;
    587 
    588   #ifndef _7ZIP_ST
    589   bool MtMode;
    590   UInt32 NumThreads;
    591   #endif
    592 
    593   CThreadDecoder():
    594     Decoder(true)
    595   {
    596     #ifndef _7ZIP_ST
    597     MtMode = false;
    598     NumThreads = 1;
    599     #endif
    600     FosSpec = new CFolderOutStream2;
    601     Fos = FosSpec;
    602     Result = E_FAIL;
    603   }
    604   virtual void Execute();
    605 };
    606 
    607 void CThreadDecoder::Execute()
    608 {
    609   try
    610   {
    611     #ifndef _NO_CRYPTO
    612     bool passwordIsDefined;
    613     #endif
    614     Result = Decoder.Decode(
    615       EXTERNAL_CODECS_VARS
    616       InStream,
    617       StartPos,
    618       PackSizes,
    619       *Folder,
    620       Fos,
    621       NULL
    622       #ifndef _NO_CRYPTO
    623       , GetTextPassword, passwordIsDefined
    624       #endif
    625       #ifndef _7ZIP_ST
    626       , MtMode, NumThreads
    627       #endif
    628       );
    629   }
    630   catch(...)
    631   {
    632     Result = E_FAIL;
    633   }
    634   if (Result == S_OK)
    635     Result = FosSpec->CheckFinishedState();
    636   FosSpec->ReleaseOutStream();
    637 }
    638 
    639 bool static Is86FilteredFolder(const CFolder &f)
    640 {
    641   for (int i = 0; i < f.Coders.Size(); i++)
    642   {
    643     CMethodId m = f.Coders[i].MethodID;
    644     if (m == k_BCJ || m == k_BCJ2)
    645       return true;
    646   }
    647   return false;
    648 }
    649 
    650 #ifndef _NO_CRYPTO
    651 
    652 class CCryptoGetTextPassword:
    653   public ICryptoGetTextPassword,
    654   public CMyUnknownImp
    655 {
    656 public:
    657   UString Password;
    658 
    659   MY_UNKNOWN_IMP
    660   STDMETHOD(CryptoGetTextPassword)(BSTR *password);
    661 };
    662 
    663 STDMETHODIMP CCryptoGetTextPassword::CryptoGetTextPassword(BSTR *password)
    664 {
    665   return StringToBstr(Password, password);
    666 }
    667 
    668 #endif
    669 
    670 static const int kNumGroupsMax = 4;
    671 
    672 #ifdef USE_86_FILTER
    673 static bool Is86Group(int group) { return (group & 1) != 0; }
    674 #endif
    675 static bool IsEncryptedGroup(int group) { return (group & 2) != 0; }
    676 static int GetGroupIndex(bool encrypted, int bcjFiltered)
    677   { return (encrypted ? 2 : 0) + (bcjFiltered ? 1 : 0); }
    678 
    679 HRESULT Update(
    680     DECL_EXTERNAL_CODECS_LOC_VARS
    681     IInStream *inStream,
    682     const CArchiveDatabaseEx *db,
    683     const CObjectVector<CUpdateItem> &updateItems,
    684     COutArchive &archive,
    685     CArchiveDatabase &newDatabase,
    686     ISequentialOutStream *seqOutStream,
    687     IArchiveUpdateCallback *updateCallback,
    688     const CUpdateOptions &options
    689     #ifndef _NO_CRYPTO
    690     , ICryptoGetTextPassword *getDecoderPassword
    691     #endif
    692     )
    693 {
    694   UInt64 numSolidFiles = options.NumSolidFiles;
    695   if (numSolidFiles == 0)
    696     numSolidFiles = 1;
    697   /*
    698   CMyComPtr<IOutStream> outStream;
    699   RINOK(seqOutStream->QueryInterface(IID_IOutStream, (void **)&outStream));
    700   if (!outStream)
    701     return E_NOTIMPL;
    702   */
    703 
    704   UInt64 startBlockSize = db != 0 ? db->ArchiveInfo.StartPosition: 0;
    705   if (startBlockSize > 0 && !options.RemoveSfxBlock)
    706   {
    707     RINOK(WriteRange(inStream, seqOutStream, 0, startBlockSize, NULL));
    708   }
    709 
    710   CRecordVector<int> fileIndexToUpdateIndexMap;
    711   CRecordVector<CFolderRepack> folderRefs;
    712   UInt64 complexity = 0;
    713   UInt64 inSizeForReduce2 = 0;
    714   bool needEncryptedRepack = false;
    715   if (db != 0)
    716   {
    717     fileIndexToUpdateIndexMap.Reserve(db->Files.Size());
    718     int i;
    719     for (i = 0; i < db->Files.Size(); i++)
    720       fileIndexToUpdateIndexMap.Add(-1);
    721 
    722     for (i = 0; i < updateItems.Size(); i++)
    723     {
    724       int index = updateItems[i].IndexInArchive;
    725       if (index != -1)
    726         fileIndexToUpdateIndexMap[index] = i;
    727     }
    728 
    729     for (i = 0; i < db->Folders.Size(); i++)
    730     {
    731       CNum indexInFolder = 0;
    732       CNum numCopyItems = 0;
    733       CNum numUnpackStreams = db->NumUnpackStreamsVector[i];
    734       UInt64 repackSize = 0;
    735       for (CNum fi = db->FolderStartFileIndex[i]; indexInFolder < numUnpackStreams; fi++)
    736       {
    737         const CFileItem &file = db->Files[fi];
    738         if (file.HasStream)
    739         {
    740           indexInFolder++;
    741           int updateIndex = fileIndexToUpdateIndexMap[fi];
    742           if (updateIndex >= 0 && !updateItems[updateIndex].NewData)
    743           {
    744             numCopyItems++;
    745             repackSize += file.Size;
    746           }
    747         }
    748       }
    749 
    750       if (numCopyItems == 0)
    751         continue;
    752 
    753       CFolderRepack rep;
    754       rep.FolderIndex = i;
    755       rep.NumCopyFiles = numCopyItems;
    756       const CFolder &f = db->Folders[i];
    757       bool isEncrypted = f.IsEncrypted();
    758       rep.Group = GetGroupIndex(isEncrypted, Is86FilteredFolder(f));
    759       folderRefs.Add(rep);
    760       if (numCopyItems == numUnpackStreams)
    761         complexity += db->GetFolderFullPackSize(i);
    762       else
    763       {
    764         complexity += repackSize;
    765         if (repackSize > inSizeForReduce2)
    766           inSizeForReduce2 = repackSize;
    767         if (isEncrypted)
    768           needEncryptedRepack = true;
    769       }
    770     }
    771     folderRefs.Sort(CompareFolderRepacks, (void *)db);
    772   }
    773 
    774   UInt64 inSizeForReduce = 0;
    775   int i;
    776   for (i = 0; i < updateItems.Size(); i++)
    777   {
    778     const CUpdateItem &ui = updateItems[i];
    779     if (ui.NewData)
    780     {
    781       complexity += ui.Size;
    782       if (numSolidFiles != 1)
    783         inSizeForReduce += ui.Size;
    784       else if (ui.Size > inSizeForReduce)
    785         inSizeForReduce = ui.Size;
    786     }
    787   }
    788 
    789   if (inSizeForReduce2 > inSizeForReduce)
    790     inSizeForReduce = inSizeForReduce2;
    791 
    792   const UInt32 kMinReduceSize = (1 << 16);
    793   if (inSizeForReduce < kMinReduceSize)
    794     inSizeForReduce = kMinReduceSize;
    795 
    796   RINOK(updateCallback->SetTotal(complexity));
    797 
    798   CLocalProgress *lps = new CLocalProgress;
    799   CMyComPtr<ICompressProgressInfo> progress = lps;
    800   lps->Init(updateCallback, true);
    801 
    802   CThreadDecoder threadDecoder;
    803   if (!folderRefs.IsEmpty())
    804   {
    805     #ifdef EXTERNAL_CODECS
    806     threadDecoder._codecsInfo = codecsInfo;
    807     threadDecoder._externalCodecs = *externalCodecs;
    808     #endif
    809     RINOK(threadDecoder.Create());
    810   }
    811 
    812   CObjectVector<CSolidGroup> groups;
    813   for (i = 0; i < kNumGroupsMax; i++)
    814     groups.Add(CSolidGroup());
    815 
    816   {
    817     // ---------- Split files to 2 groups ----------
    818 
    819     bool useFilters = options.UseFilters;
    820     const CCompressionMethodMode &method = *options.Method;
    821     if (method.Methods.Size() != 1 || method.Binds.Size() != 0)
    822       useFilters = false;
    823     for (i = 0; i < updateItems.Size(); i++)
    824     {
    825       const CUpdateItem &ui = updateItems[i];
    826       if (!ui.NewData || !ui.HasStream())
    827         continue;
    828       bool filteredGroup = false;
    829       if (useFilters)
    830       {
    831         int dotPos = ui.Name.ReverseFind(L'.');
    832         if (dotPos >= 0)
    833           filteredGroup = IsExeExt(ui.Name.Mid(dotPos + 1));
    834       }
    835       groups[GetGroupIndex(method.PasswordIsDefined, filteredGroup)].Indices.Add(i);
    836     }
    837   }
    838 
    839   #ifndef _NO_CRYPTO
    840 
    841   CCryptoGetTextPassword *getPasswordSpec = NULL;
    842   if (needEncryptedRepack)
    843   {
    844     getPasswordSpec = new CCryptoGetTextPassword;
    845     threadDecoder.GetTextPassword = getPasswordSpec;
    846 
    847     if (options.Method->PasswordIsDefined)
    848       getPasswordSpec->Password = options.Method->Password;
    849     else
    850     {
    851       if (!getDecoderPassword)
    852         return E_NOTIMPL;
    853       CMyComBSTR password;
    854       RINOK(getDecoderPassword->CryptoGetTextPassword(&password));
    855       getPasswordSpec->Password = password;
    856     }
    857   }
    858 
    859   #endif
    860 
    861   // ---------- Compress ----------
    862 
    863   RINOK(archive.Create(seqOutStream, false));
    864   RINOK(archive.SkipPrefixArchiveHeader());
    865 
    866   int folderRefIndex = 0;
    867   lps->ProgressOffset = 0;
    868 
    869   for (int groupIndex = 0; groupIndex < kNumGroupsMax; groupIndex++)
    870   {
    871     const CSolidGroup &group = groups[groupIndex];
    872 
    873     CCompressionMethodMode method;
    874     #ifdef USE_86_FILTER
    875     if (Is86Group(groupIndex))
    876       MakeExeMethod(*options.Method, options.MaxFilter, method);
    877     else
    878     #endif
    879       method = *options.Method;
    880 
    881     if (IsEncryptedGroup(groupIndex))
    882     {
    883       if (!method.PasswordIsDefined)
    884       {
    885         #ifndef _NO_CRYPTO
    886         if (getPasswordSpec)
    887           method.Password = getPasswordSpec->Password;
    888         #endif
    889         method.PasswordIsDefined = true;
    890       }
    891     }
    892     else
    893     {
    894       method.PasswordIsDefined = false;
    895       method.Password.Empty();
    896     }
    897 
    898     CEncoder encoder(method);
    899 
    900     for (; folderRefIndex < folderRefs.Size(); folderRefIndex++)
    901     {
    902       const CFolderRepack &rep = folderRefs[folderRefIndex];
    903       if (rep.Group != groupIndex)
    904         break;
    905       int folderIndex = rep.FolderIndex;
    906 
    907       if (rep.NumCopyFiles == db->NumUnpackStreamsVector[folderIndex])
    908       {
    909         UInt64 packSize = db->GetFolderFullPackSize(folderIndex);
    910         RINOK(WriteRange(inStream, archive.SeqStream,
    911           db->GetFolderStreamPos(folderIndex, 0), packSize, progress));
    912         lps->ProgressOffset += packSize;
    913 
    914         const CFolder &folder = db->Folders[folderIndex];
    915         CNum startIndex = db->FolderStartPackStreamIndex[folderIndex];
    916         for (int j = 0; j < folder.PackStreams.Size(); j++)
    917         {
    918           newDatabase.PackSizes.Add(db->PackSizes[startIndex + j]);
    919           // newDatabase.PackCRCsDefined.Add(db.PackCRCsDefined[startIndex + j]);
    920           // newDatabase.PackCRCs.Add(db.PackCRCs[startIndex + j]);
    921         }
    922         newDatabase.Folders.Add(folder);
    923       }
    924       else
    925       {
    926         CStreamBinder sb;
    927         RINOK(sb.CreateEvents());
    928         CMyComPtr<ISequentialOutStream> sbOutStream;
    929         CMyComPtr<ISequentialInStream> sbInStream;
    930         sb.CreateStreams(&sbInStream, &sbOutStream);
    931         CBoolVector extractStatuses;
    932 
    933         CNum numUnpackStreams = db->NumUnpackStreamsVector[folderIndex];
    934         CNum indexInFolder = 0;
    935 
    936         for (CNum fi = db->FolderStartFileIndex[folderIndex]; indexInFolder < numUnpackStreams; fi++)
    937         {
    938           bool needExtract = false;
    939           if (db->Files[fi].HasStream)
    940           {
    941             indexInFolder++;
    942             int updateIndex = fileIndexToUpdateIndexMap[fi];
    943             if (updateIndex >= 0 && !updateItems[updateIndex].NewData)
    944               needExtract = true;
    945           }
    946           extractStatuses.Add(needExtract);
    947         }
    948 
    949         RINOK(threadDecoder.FosSpec->Init(db, db->FolderStartFileIndex[folderIndex], &extractStatuses, sbOutStream));
    950         sbOutStream.Release();
    951 
    952         threadDecoder.InStream = inStream;
    953         threadDecoder.Folder = &db->Folders[folderIndex];
    954         threadDecoder.StartPos = db->GetFolderStreamPos(folderIndex, 0);
    955         threadDecoder.PackSizes = &db->PackSizes[db->FolderStartPackStreamIndex[folderIndex]];
    956 
    957         threadDecoder.Start();
    958 
    959         int startPackIndex = newDatabase.PackSizes.Size();
    960         CFolder newFolder;
    961         RINOK(encoder.Encode(
    962           EXTERNAL_CODECS_LOC_VARS
    963           sbInStream, NULL, &inSizeForReduce, newFolder,
    964           archive.SeqStream, newDatabase.PackSizes, progress));
    965 
    966         threadDecoder.WaitFinish();
    967 
    968         RINOK(threadDecoder.Result);
    969 
    970         for (; startPackIndex < newDatabase.PackSizes.Size(); startPackIndex++)
    971           lps->OutSize += newDatabase.PackSizes[startPackIndex];
    972         lps->InSize += newFolder.GetUnpackSize();
    973 
    974         newDatabase.Folders.Add(newFolder);
    975       }
    976 
    977       newDatabase.NumUnpackStreamsVector.Add(rep.NumCopyFiles);
    978 
    979       CNum numUnpackStreams = db->NumUnpackStreamsVector[folderIndex];
    980 
    981       CNum indexInFolder = 0;
    982       for (CNum fi = db->FolderStartFileIndex[folderIndex]; indexInFolder < numUnpackStreams; fi++)
    983       {
    984         CFileItem file;
    985         CFileItem2 file2;
    986         db->GetFile(fi, file, file2);
    987         if (file.HasStream)
    988         {
    989           indexInFolder++;
    990           int updateIndex = fileIndexToUpdateIndexMap[fi];
    991           if (updateIndex >= 0)
    992           {
    993             const CUpdateItem &ui = updateItems[updateIndex];
    994             if (ui.NewData)
    995               continue;
    996             if (ui.NewProps)
    997             {
    998               CFileItem uf;
    999               FromUpdateItemToFileItem(ui, uf, file2);
   1000               uf.Size = file.Size;
   1001               uf.Crc = file.Crc;
   1002               uf.CrcDefined = file.CrcDefined;
   1003               uf.HasStream = file.HasStream;
   1004               file = uf;
   1005             }
   1006             newDatabase.AddFile(file, file2);
   1007           }
   1008         }
   1009       }
   1010     }
   1011 
   1012     int numFiles = group.Indices.Size();
   1013     if (numFiles == 0)
   1014       continue;
   1015     CRecordVector<CRefItem> refItems;
   1016     refItems.Reserve(numFiles);
   1017     bool sortByType = (numSolidFiles > 1);
   1018     for (i = 0; i < numFiles; i++)
   1019       refItems.Add(CRefItem(group.Indices[i], updateItems[group.Indices[i]], sortByType));
   1020     refItems.Sort(CompareUpdateItems, (void *)&sortByType);
   1021 
   1022     CRecordVector<UInt32> indices;
   1023     indices.Reserve(numFiles);
   1024 
   1025     for (i = 0; i < numFiles; i++)
   1026     {
   1027       UInt32 index = refItems[i].Index;
   1028       indices.Add(index);
   1029       /*
   1030       const CUpdateItem &ui = updateItems[index];
   1031       CFileItem file;
   1032       if (ui.NewProps)
   1033         FromUpdateItemToFileItem(ui, file);
   1034       else
   1035         file = db.Files[ui.IndexInArchive];
   1036       if (file.IsAnti || file.IsDir)
   1037         return E_FAIL;
   1038       newDatabase.Files.Add(file);
   1039       */
   1040     }
   1041 
   1042     for (i = 0; i < numFiles;)
   1043     {
   1044       UInt64 totalSize = 0;
   1045       int numSubFiles;
   1046       UString prevExtension;
   1047       for (numSubFiles = 0; i + numSubFiles < numFiles &&
   1048           numSubFiles < numSolidFiles; numSubFiles++)
   1049       {
   1050         const CUpdateItem &ui = updateItems[indices[i + numSubFiles]];
   1051         totalSize += ui.Size;
   1052         if (totalSize > options.NumSolidBytes)
   1053           break;
   1054         if (options.SolidExtension)
   1055         {
   1056           UString ext = ui.GetExtension();
   1057           if (numSubFiles == 0)
   1058             prevExtension = ext;
   1059           else
   1060             if (ext.CompareNoCase(prevExtension) != 0)
   1061               break;
   1062         }
   1063       }
   1064       if (numSubFiles < 1)
   1065         numSubFiles = 1;
   1066 
   1067       CFolderInStream *inStreamSpec = new CFolderInStream;
   1068       CMyComPtr<ISequentialInStream> solidInStream(inStreamSpec);
   1069       inStreamSpec->Init(updateCallback, &indices[i], numSubFiles);
   1070 
   1071       CFolder folderItem;
   1072 
   1073       int startPackIndex = newDatabase.PackSizes.Size();
   1074       RINOK(encoder.Encode(
   1075           EXTERNAL_CODECS_LOC_VARS
   1076           solidInStream, NULL, &inSizeForReduce, folderItem,
   1077           archive.SeqStream, newDatabase.PackSizes, progress));
   1078 
   1079       for (; startPackIndex < newDatabase.PackSizes.Size(); startPackIndex++)
   1080         lps->OutSize += newDatabase.PackSizes[startPackIndex];
   1081 
   1082       lps->InSize += folderItem.GetUnpackSize();
   1083       // for ()
   1084       // newDatabase.PackCRCsDefined.Add(false);
   1085       // newDatabase.PackCRCs.Add(0);
   1086 
   1087       newDatabase.Folders.Add(folderItem);
   1088 
   1089       CNum numUnpackStreams = 0;
   1090       for (int subIndex = 0; subIndex < numSubFiles; subIndex++)
   1091       {
   1092         const CUpdateItem &ui = updateItems[indices[i + subIndex]];
   1093         CFileItem file;
   1094         CFileItem2 file2;
   1095         if (ui.NewProps)
   1096           FromUpdateItemToFileItem(ui, file, file2);
   1097         else
   1098           db->GetFile(ui.IndexInArchive, file, file2);
   1099         if (file2.IsAnti || file.IsDir)
   1100           return E_FAIL;
   1101 
   1102         /*
   1103         CFileItem &file = newDatabase.Files[
   1104               startFileIndexInDatabase + i + subIndex];
   1105         */
   1106         if (!inStreamSpec->Processed[subIndex])
   1107         {
   1108           continue;
   1109           // file.Name += L".locked";
   1110         }
   1111 
   1112         file.Crc = inStreamSpec->CRCs[subIndex];
   1113         file.Size = inStreamSpec->Sizes[subIndex];
   1114         if (file.Size != 0)
   1115         {
   1116           file.CrcDefined = true;
   1117           file.HasStream = true;
   1118           numUnpackStreams++;
   1119         }
   1120         else
   1121         {
   1122           file.CrcDefined = false;
   1123           file.HasStream = false;
   1124         }
   1125         newDatabase.AddFile(file, file2);
   1126       }
   1127       // numUnpackStreams = 0 is very bad case for locked files
   1128       // v3.13 doesn't understand it.
   1129       newDatabase.NumUnpackStreamsVector.Add(numUnpackStreams);
   1130       i += numSubFiles;
   1131     }
   1132   }
   1133 
   1134   if (folderRefIndex != folderRefs.Size())
   1135     return E_FAIL;
   1136 
   1137   /*
   1138   folderRefs.ClearAndFree();
   1139   fileIndexToUpdateIndexMap.ClearAndFree();
   1140   groups.ClearAndFree();
   1141   */
   1142 
   1143   {
   1144     // ---------- Write Folders & Empty Files ----------
   1145 
   1146     CRecordVector<int> emptyRefs;
   1147     for (i = 0; i < updateItems.Size(); i++)
   1148     {
   1149       const CUpdateItem &ui = updateItems[i];
   1150       if (ui.NewData)
   1151       {
   1152         if (ui.HasStream())
   1153           continue;
   1154       }
   1155       else if (ui.IndexInArchive != -1 && db->Files[ui.IndexInArchive].HasStream)
   1156         continue;
   1157       emptyRefs.Add(i);
   1158     }
   1159     emptyRefs.Sort(CompareEmptyItems, (void *)&updateItems);
   1160     for (i = 0; i < emptyRefs.Size(); i++)
   1161     {
   1162       const CUpdateItem &ui = updateItems[emptyRefs[i]];
   1163       CFileItem file;
   1164       CFileItem2 file2;
   1165       if (ui.NewProps)
   1166         FromUpdateItemToFileItem(ui, file, file2);
   1167       else
   1168         db->GetFile(ui.IndexInArchive, file, file2);
   1169       newDatabase.AddFile(file, file2);
   1170     }
   1171   }
   1172 
   1173   newDatabase.ReserveDown();
   1174   return S_OK;
   1175 }
   1176 
   1177 }}
   1178