misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zHandlerOut.cpp
changeset 12213 bb5522e88ab2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zHandlerOut.cpp	Mon Apr 10 12:06:43 2017 -0400
@@ -0,0 +1,464 @@
+// 7zHandlerOut.cpp
+
+#include "StdAfx.h"
+
+#include "7zHandler.h"
+#include "7zOut.h"
+#include "7zUpdate.h"
+
+#include "../../../Windows/PropVariant.h"
+
+#include "../../../Common/ComTry.h"
+#include "../../../Common/StringToInt.h"
+#include "../../IPassword.h"
+#include "../../ICoder.h"
+
+#include "../Common/ItemNameUtils.h"
+#include "../Common/ParseProperties.h"
+
+using namespace NWindows;
+
+namespace NArchive {
+namespace N7z {
+
+static const wchar_t *kLZMAMethodName = L"LZMA";
+static const wchar_t *kCopyMethod = L"Copy";
+static const wchar_t *kDefaultMethodName = kLZMAMethodName;
+
+static const UInt32 kLzmaAlgorithmX5 = 1;
+static const wchar_t *kLzmaMatchFinderForHeaders = L"BT2";
+static const UInt32 kDictionaryForHeaders = 1 << 20;
+static const UInt32 kNumFastBytesForHeaders = 273;
+static const UInt32 kAlgorithmForHeaders = kLzmaAlgorithmX5;
+
+static inline bool IsCopyMethod(const UString &methodName)
+  { return (methodName.CompareNoCase(kCopyMethod) == 0); }
+
+STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type)
+{
+  *type = NFileTimeType::kWindows;
+  return S_OK;
+}
+
+HRESULT CHandler::SetPassword(CCompressionMethodMode &methodMode,
+    IArchiveUpdateCallback *updateCallback)
+{
+  CMyComPtr<ICryptoGetTextPassword2> getTextPassword;
+  if (!getTextPassword)
+  {
+    CMyComPtr<IArchiveUpdateCallback> udateCallback2(updateCallback);
+    udateCallback2.QueryInterface(IID_ICryptoGetTextPassword2, &getTextPassword);
+  }
+  
+  if (getTextPassword)
+  {
+    CMyComBSTR password;
+    Int32 passwordIsDefined;
+    RINOK(getTextPassword->CryptoGetTextPassword2(
+        &passwordIsDefined, &password));
+    methodMode.PasswordIsDefined = IntToBool(passwordIsDefined);
+    if (methodMode.PasswordIsDefined)
+      methodMode.Password = password;
+  }
+  else
+    methodMode.PasswordIsDefined = false;
+  return S_OK;
+}
+
+HRESULT CHandler::SetCompressionMethod(
+    CCompressionMethodMode &methodMode,
+    CCompressionMethodMode &headerMethod)
+{
+  HRESULT res = SetCompressionMethod(methodMode, _methods
+  #ifdef COMPRESS_MT
+  , _numThreads
+  #endif
+  );
+  RINOK(res);
+  methodMode.Binds = _binds;
+
+  if (_compressHeaders)
+  {
+    // headerMethod.Methods.Add(methodMode.Methods.Back());
+
+    CObjectVector<COneMethodInfo> headerMethodInfoVector;
+    COneMethodInfo oneMethodInfo;
+    oneMethodInfo.MethodName = kLZMAMethodName;
+    {
+      CProp property;
+      property.Id = NCoderPropID::kMatchFinder;
+      property.Value = kLzmaMatchFinderForHeaders;
+      oneMethodInfo.Properties.Add(property);
+    }
+    {
+      CProp property;
+      property.Id = NCoderPropID::kAlgorithm;
+      property.Value = kAlgorithmForHeaders;
+      oneMethodInfo.Properties.Add(property);
+    }
+    {
+      CProp property;
+      property.Id = NCoderPropID::kNumFastBytes;
+      property.Value = UInt32(kNumFastBytesForHeaders);
+      oneMethodInfo.Properties.Add(property);
+    }
+    {
+      CProp property;
+      property.Id = NCoderPropID::kDictionarySize;
+      property.Value = UInt32(kDictionaryForHeaders);
+      oneMethodInfo.Properties.Add(property);
+    }
+    headerMethodInfoVector.Add(oneMethodInfo);
+    HRESULT res = SetCompressionMethod(headerMethod, headerMethodInfoVector
+      #ifdef COMPRESS_MT
+      ,1
+      #endif
+    );
+    RINOK(res);
+  }
+  return S_OK;
+}
+
+HRESULT CHandler::SetCompressionMethod(
+    CCompressionMethodMode &methodMode,
+    CObjectVector<COneMethodInfo> &methodsInfo
+    #ifdef COMPRESS_MT
+    , UInt32 numThreads
+    #endif
+    )
+{
+  UInt32 level = _level;
+  
+  if (methodsInfo.IsEmpty())
+  {
+    COneMethodInfo oneMethodInfo;
+    oneMethodInfo.MethodName = ((level == 0) ? kCopyMethod : kDefaultMethodName);
+    methodsInfo.Add(oneMethodInfo);
+  }
+
+  bool needSolid = false;
+  for(int i = 0; i < methodsInfo.Size(); i++)
+  {
+    COneMethodInfo &oneMethodInfo = methodsInfo[i];
+    SetCompressionMethod2(oneMethodInfo
+      #ifdef COMPRESS_MT
+      , numThreads
+      #endif
+      );
+
+    if (!IsCopyMethod(oneMethodInfo.MethodName))
+      needSolid = true;
+
+    CMethodFull methodFull;
+
+    if (!FindMethod(
+        EXTERNAL_CODECS_VARS
+        oneMethodInfo.MethodName, methodFull.Id, methodFull.NumInStreams, methodFull.NumOutStreams))
+      return E_INVALIDARG;
+    methodFull.Properties = oneMethodInfo.Properties;
+    methodMode.Methods.Add(methodFull);
+
+    if (!_numSolidBytesDefined)
+    {
+      for (int j = 0; j < methodFull.Properties.Size(); j++)
+      {
+        const CProp &prop = methodFull.Properties[j];
+        if ((prop.Id == NCoderPropID::kDictionarySize || 
+             prop.Id == NCoderPropID::kUsedMemorySize) && prop.Value.vt == VT_UI4)
+        {
+          _numSolidBytes = ((UInt64)prop.Value.ulVal) << 7;
+          const UInt64 kMinSize = (1 << 24);
+          if (_numSolidBytes < kMinSize)
+            _numSolidBytes = kMinSize;
+          _numSolidBytesDefined = true;
+          break;
+        }
+      }
+    }
+  }
+
+  if (!needSolid && !_numSolidBytesDefined)
+  {
+    _numSolidBytesDefined = true;
+    _numSolidBytes  = 0;
+  }
+  return S_OK;
+}
+
+static HRESULT GetTime(IArchiveUpdateCallback *updateCallback, int index, PROPID propID, CArchiveFileTime &filetime, bool &filetimeIsDefined)
+{
+  filetimeIsDefined = false;
+  NCOM::CPropVariant propVariant;
+  RINOK(updateCallback->GetProperty(index, propID, &propVariant));
+  if (propVariant.vt == VT_FILETIME)
+  {
+    filetime = propVariant.filetime;
+    filetimeIsDefined = true;
+  }
+  else if (propVariant.vt != VT_EMPTY)
+    return E_INVALIDARG;
+  return S_OK;
+}
+
+STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
+    IArchiveUpdateCallback *updateCallback)
+{
+  COM_TRY_BEGIN
+
+  const CArchiveDatabaseEx *database = 0;
+  #ifdef _7Z_VOL
+  if(_volumes.Size() > 1)
+    return E_FAIL;
+  const CVolume *volume = 0;
+  if (_volumes.Size() == 1)
+  {
+    volume = &_volumes.Front();
+    database = &volume->Database;
+  }
+  #else
+  if (_inStream != 0)
+    database = &_database;
+  #endif
+
+  // CRecordVector<bool> compressStatuses;
+  CObjectVector<CUpdateItem> updateItems;
+  // CRecordVector<UInt32> copyIndices;
+  
+  // CMyComPtr<IUpdateCallback2> updateCallback2;
+  // updateCallback->QueryInterface(&updateCallback2);
+
+  for(UInt32 i = 0; i < numItems; i++)
+  {
+    Int32 newData;
+    Int32 newProperties;
+    UInt32 indexInArchive;
+    if (!updateCallback)
+      return E_FAIL;
+    RINOK(updateCallback->GetUpdateItemInfo(i,
+        &newData, &newProperties, &indexInArchive));
+    CUpdateItem updateItem;
+    updateItem.NewProperties = IntToBool(newProperties);
+    updateItem.NewData = IntToBool(newData);
+    updateItem.IndexInArchive = indexInArchive;
+    updateItem.IndexInClient = i;
+    updateItem.IsAnti = false;
+    updateItem.Size = 0;
+
+    if (updateItem.IndexInArchive != -1)
+    {
+      const CFileItem &fileItem = database->Files[updateItem.IndexInArchive];
+      updateItem.Name = fileItem.Name;
+      updateItem.IsDirectory = fileItem.IsDirectory;
+      updateItem.Size = fileItem.UnPackSize;
+      updateItem.IsAnti = fileItem.IsAnti;
+      
+      updateItem.CreationTime = fileItem.CreationTime;
+      updateItem.IsCreationTimeDefined = fileItem.IsCreationTimeDefined;
+      updateItem.LastWriteTime = fileItem.LastWriteTime;
+      updateItem.IsLastWriteTimeDefined = fileItem.IsLastWriteTimeDefined;
+      updateItem.LastAccessTime = fileItem.LastAccessTime;
+      updateItem.IsLastAccessTimeDefined = fileItem.IsLastAccessTimeDefined;
+    }
+
+    if (updateItem.NewProperties)
+    {
+      bool nameIsDefined;
+      bool folderStatusIsDefined;
+      {
+        NCOM::CPropVariant propVariant;
+        RINOK(updateCallback->GetProperty(i, kpidAttributes, &propVariant));
+        if (propVariant.vt == VT_EMPTY)
+          updateItem.AttributesAreDefined = false;
+        else if (propVariant.vt != VT_UI4)
+          return E_INVALIDARG;
+        else
+        {
+          updateItem.Attributes = propVariant.ulVal;
+          updateItem.AttributesAreDefined = true;
+        }
+      }
+      
+      RINOK(GetTime(updateCallback, i, kpidCreationTime, updateItem.CreationTime, updateItem.IsCreationTimeDefined));
+      RINOK(GetTime(updateCallback, i, kpidLastWriteTime, updateItem.LastWriteTime , updateItem.IsLastWriteTimeDefined));
+      RINOK(GetTime(updateCallback, i, kpidLastAccessTime, updateItem.LastAccessTime, updateItem.IsLastAccessTimeDefined));
+
+      {
+        NCOM::CPropVariant propVariant;
+        RINOK(updateCallback->GetProperty(i, kpidPath, &propVariant));
+        if (propVariant.vt == VT_EMPTY)
+          nameIsDefined = false;
+        else if (propVariant.vt != VT_BSTR)
+          return E_INVALIDARG;
+        else
+        {
+          updateItem.Name = NItemName::MakeLegalName(propVariant.bstrVal);
+          nameIsDefined = true;
+        }
+      }
+      {
+        NCOM::CPropVariant propVariant;
+        RINOK(updateCallback->GetProperty(i, kpidIsFolder, &propVariant));
+        if (propVariant.vt == VT_EMPTY)
+          folderStatusIsDefined = false;
+        else if (propVariant.vt != VT_BOOL)
+          return E_INVALIDARG;
+        else
+        {
+          updateItem.IsDirectory = (propVariant.boolVal != VARIANT_FALSE);
+          folderStatusIsDefined = true;
+        }
+      }
+
+      {
+        NCOM::CPropVariant propVariant;
+        RINOK(updateCallback->GetProperty(i, kpidIsAnti, &propVariant));
+        if (propVariant.vt == VT_EMPTY)
+          updateItem.IsAnti = false;
+        else if (propVariant.vt != VT_BOOL)
+          return E_INVALIDARG;
+        else
+          updateItem.IsAnti = (propVariant.boolVal != VARIANT_FALSE);
+      }
+
+      if (updateItem.IsAnti)
+      {
+        updateItem.AttributesAreDefined = false;
+
+        updateItem.IsCreationTimeDefined = false;
+        updateItem.IsLastWriteTimeDefined = false;
+        updateItem.IsLastAccessTimeDefined = false;
+        
+        updateItem.Size = 0;
+      }
+
+      if (!folderStatusIsDefined && updateItem.AttributesAreDefined)
+        updateItem.SetDirectoryStatusFromAttributes();
+    }
+
+    if (updateItem.NewData)
+    {
+      NCOM::CPropVariant propVariant;
+      RINOK(updateCallback->GetProperty(i, kpidSize, &propVariant));
+      if (propVariant.vt != VT_UI8)
+        return E_INVALIDARG;
+      updateItem.Size = (UInt64)propVariant.uhVal.QuadPart;
+      if (updateItem.Size != 0 && updateItem.IsAnti)
+        return E_INVALIDARG;
+    }
+    updateItems.Add(updateItem);
+  }
+
+  CCompressionMethodMode methodMode, headerMethod;
+  RINOK(SetCompressionMethod(methodMode, headerMethod));
+  #ifdef COMPRESS_MT
+  methodMode.NumThreads = _numThreads;
+  headerMethod.NumThreads = 1;
+  #endif
+
+  RINOK(SetPassword(methodMode, updateCallback));
+
+  bool compressMainHeader = _compressHeaders;  // check it
+
+  if (methodMode.PasswordIsDefined)
+  {
+    compressMainHeader = true; 
+    if(_encryptHeaders)
+      RINOK(SetPassword(headerMethod, updateCallback));
+  }
+
+  if (numItems < 2)
+    compressMainHeader = false;
+
+  CUpdateOptions options;
+  options.Method = &methodMode;
+  options.HeaderMethod = (_compressHeaders || 
+      (methodMode.PasswordIsDefined && _encryptHeaders)) ? 
+      &headerMethod : 0;
+  options.UseFilters = _level != 0 && _autoFilter;
+  options.MaxFilter = _level >= 8;
+
+  options.HeaderOptions.CompressMainHeader = compressMainHeader;
+  options.HeaderOptions.WriteModified = WriteModified;
+  options.HeaderOptions.WriteCreated = WriteCreated;
+  options.HeaderOptions.WriteAccessed = WriteAccessed;
+  
+  options.NumSolidFiles = _numSolidFiles;
+  options.NumSolidBytes = _numSolidBytes;
+  options.SolidExtension = _solidExtension;
+  options.RemoveSfxBlock = _removeSfxBlock;
+  options.VolumeMode = _volumeMode;
+  return Update(
+      EXTERNAL_CODECS_VARS
+      #ifdef _7Z_VOL
+      volume ? volume->Stream: 0, 
+      volume ? database: 0, 
+      #else
+      _inStream, 
+      database,
+      #endif
+      updateItems, outStream, updateCallback, options);
+  COM_TRY_END
+}
+
+static HRESULT GetBindInfoPart(UString &srcString, UInt32 &coder, UInt32 &stream)
+{
+  stream = 0;
+  int index = ParseStringToUInt32(srcString, coder);
+  if (index == 0)
+    return E_INVALIDARG;
+  srcString.Delete(0, index);
+  if (srcString[0] == 'S')
+  {
+    srcString.Delete(0);
+    int index = ParseStringToUInt32(srcString, stream);
+    if (index == 0)
+      return E_INVALIDARG;
+    srcString.Delete(0, index);
+  }
+  return S_OK;
+}
+
+static HRESULT GetBindInfo(UString &srcString, CBind &bind)
+{
+  RINOK(GetBindInfoPart(srcString, bind.OutCoder, bind.OutStream));
+  if (srcString[0] != ':')
+    return E_INVALIDARG;
+  srcString.Delete(0);
+  RINOK(GetBindInfoPart(srcString, bind.InCoder, bind.InStream));
+  if (!srcString.IsEmpty())
+    return E_INVALIDARG;
+  return S_OK;
+}
+
+STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProperties)
+{
+  COM_TRY_BEGIN
+  _binds.Clear();
+  BeforeSetProperty();
+
+  for (int i = 0; i < numProperties; i++)
+  {
+    UString name = names[i];
+    name.MakeUpper();
+    if (name.IsEmpty())
+      return E_INVALIDARG;
+
+    const PROPVARIANT &value = values[i];
+
+    if (name[0] == 'B')
+    {
+      name.Delete(0);
+      CBind bind;
+      RINOK(GetBindInfo(name, bind));
+      _binds.Add(bind);
+      continue;
+    }
+
+    RINOK(SetProperty(name, value));
+  }
+
+  return S_OK;
+  COM_TRY_END
+}  
+
+}}