misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zHandler.cpp
changeset 12213 bb5522e88ab2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zHandler.cpp	Mon Apr 10 12:06:43 2017 -0400
@@ -0,0 +1,793 @@
+// 7zHandler.cpp
+
+#include "StdAfx.h"
+
+#include "7zHandler.h"
+#include "7zProperties.h"
+
+#include "../../../Common/IntToString.h"
+#include "../../../Common/ComTry.h"
+#include "../../../Windows/Defs.h"
+
+#include "../Common/ItemNameUtils.h"
+#ifdef _7Z_VOL
+#include "../Common/MultiStream.h"
+#endif
+
+#ifdef __7Z_SET_PROPERTIES
+#ifdef EXTRACT_ONLY
+#include "../Common/ParseProperties.h"
+#endif
+#endif
+
+#ifdef COMPRESS_MT
+#include "../../../Windows/System.h"
+#endif
+
+using namespace NWindows;
+
+extern UString ConvertMethodIdToString(UInt64 id);
+
+namespace NArchive {
+namespace N7z {
+
+CHandler::CHandler()
+{
+  _crcSize = 4;
+
+  #ifdef EXTRACT_ONLY
+  #ifdef COMPRESS_MT
+  _numThreads = NWindows::NSystem::GetNumberOfProcessors();
+  #endif
+  #else
+  Init();
+  #endif
+}
+
+STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
+{
+  *numItems = 
+  #ifdef _7Z_VOL
+  _refs.Size();
+  #else
+  *numItems = _database.Files.Size();
+  #endif
+  return S_OK;
+}
+
+#ifdef _SFX
+
+IMP_IInArchive_ArcProps_NO
+
+STDMETHODIMP CHandler::GetNumberOfProperties(UInt32 * /* numProperties */)
+{
+  return E_NOTIMPL;
+}
+
+STDMETHODIMP CHandler::GetPropertyInfo(UInt32 /* index */,     
+      BSTR * /* name */, PROPID * /* propID */, VARTYPE * /* varType */)
+{
+  return E_NOTIMPL;
+}
+
+
+#else
+
+STATPROPSTG kArcProps[] = 
+{
+  { NULL, kpidMethod, VT_BSTR},
+  { NULL, kpidSolid, VT_BOOL},
+  { NULL, kpidNumBlocks, VT_UI4}
+};
+
+STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
+{
+  COM_TRY_BEGIN
+  NWindows::NCOM::CPropVariant prop;
+  switch(propID)
+  {
+    case kpidMethod:
+    {
+      UString resString;
+      CRecordVector<UInt64> ids;
+      int i;
+      for (i = 0; i < _database.Folders.Size(); i++)
+      {
+        const CFolder &f = _database.Folders[i];
+        for (int j = f.Coders.Size() - 1; j >= 0; j--)
+          ids.AddToUniqueSorted(f.Coders[j].MethodID);
+      }
+
+      for (i = 0; i < ids.Size(); i++)
+      {
+        UInt64 id = ids[i];
+        UString methodName;
+        /* bool methodIsKnown = */ FindMethod(EXTERNAL_CODECS_VARS id, methodName);
+        if (methodName.IsEmpty())
+          methodName = ConvertMethodIdToString(id);
+        if (!resString.IsEmpty())
+          resString += L' ';
+        resString += methodName;
+      }
+      prop = resString; 
+      break;
+    }
+    case kpidSolid: prop = _database.IsSolid(); break;
+    case kpidNumBlocks: prop = (UInt32)_database.Folders.Size(); break;
+  }
+  prop.Detach(value);
+  return S_OK;
+  COM_TRY_END
+}
+
+IMP_IInArchive_ArcProps
+
+#endif
+
+static void MySetFileTime(bool timeDefined, FILETIME unixTime, NWindows::NCOM::CPropVariant &prop)
+{
+  if (timeDefined)
+    prop = unixTime;
+}
+
+#ifndef _SFX
+
+static UString ConvertUInt32ToString(UInt32 value)
+{
+  wchar_t buffer[32];
+  ConvertUInt64ToString(value, buffer);
+  return buffer;
+}
+
+static UString GetStringForSizeValue(UInt32 value)
+{
+  for (int i = 31; i >= 0; i--)
+    if ((UInt32(1) << i) == value)
+      return ConvertUInt32ToString(i);
+  UString result;
+  if (value % (1 << 20) == 0)
+  {
+    result += ConvertUInt32ToString(value >> 20);
+    result += L"m";
+  }
+  else if (value % (1 << 10) == 0)
+  {
+    result += ConvertUInt32ToString(value >> 10);
+    result += L"k";
+  }
+  else
+  {
+    result += ConvertUInt32ToString(value);
+    result += L"b";
+  }
+  return result;
+}
+
+static const UInt64 k_Copy = 0x0;
+static const UInt64 k_LZMA  = 0x030101;
+static const UInt64 k_PPMD  = 0x030401;
+
+static wchar_t GetHex(Byte value)
+{
+  return (wchar_t)((value < 10) ? (L'0' + value) : (L'A' + (value - 10)));
+}
+static inline UString GetHex2(Byte value)
+{
+  UString result;
+  result += GetHex((Byte)(value >> 4));
+  result += GetHex((Byte)(value & 0xF));
+  return result;
+}
+
+#endif
+
+static const UInt64 k_AES  = 0x06F10701;
+
+#ifndef _SFX
+static inline UInt32 GetUInt32FromMemLE(const Byte *p)
+{
+  return p[0] | (((UInt32)p[1]) << 8) | (((UInt32)p[2]) << 16) | (((UInt32)p[3]) << 24);
+}
+#endif
+
+bool CHandler::IsEncrypted(UInt32 index2) const
+{
+  CNum folderIndex = _database.FileIndexToFolderIndexMap[index2];
+  if (folderIndex != kNumNoIndex)
+  {
+    const CFolder &folderInfo = _database.Folders[folderIndex];
+    for (int i = folderInfo.Coders.Size() - 1; i >= 0; i--)
+      if (folderInfo.Coders[i].MethodID == k_AES)
+        return true;
+  }
+  return false;
+}
+
+STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID,  PROPVARIANT *value)
+{
+  COM_TRY_BEGIN
+  NWindows::NCOM::CPropVariant prop;
+  
+  /*
+  const CRef2 &ref2 = _refs[index];
+  if (ref2.Refs.IsEmpty())
+    return E_FAIL;
+  const CRef &ref = ref2.Refs.Front();
+  */
+  
+  #ifdef _7Z_VOL
+  const CRef &ref = _refs[index];
+  const CVolume &volume = _volumes[ref.VolumeIndex];
+  const CArchiveDatabaseEx &_database = volume.Database;
+  UInt32 index2 = ref.ItemIndex;
+  const CFileItem &item = _database.Files[index2];
+  #else
+  const CFileItem &item = _database.Files[index];
+  UInt32 index2 = index;
+  #endif
+
+  switch(propID)
+  {
+    case kpidPath:
+    {
+      if (!item.Name.IsEmpty())
+        prop = NItemName::GetOSName(item.Name);
+      break;
+    }
+    case kpidIsFolder:
+      prop = item.IsDirectory;
+      break;
+    case kpidSize:
+    {
+      prop = item.UnPackSize;
+      // prop = ref2.UnPackSize;
+      break;
+    }
+    case kpidPosition:
+    {
+      /*
+      if (ref2.Refs.Size() > 1)
+        prop = ref2.StartPos;
+      else
+      */
+        if (item.IsStartPosDefined)
+          prop = item.StartPos;
+      break;
+    }
+    case kpidPackedSize:
+    {
+      // prop = ref2.PackSize;
+      {
+        CNum folderIndex = _database.FileIndexToFolderIndexMap[index2];
+        if (folderIndex != kNumNoIndex)
+        {
+          if (_database.FolderStartFileIndex[folderIndex] == (CNum)index2)
+            prop = _database.GetFolderFullPackSize(folderIndex);
+          /*
+          else
+            prop = (UInt64)0;
+          */
+        }
+        else
+          prop = (UInt64)0;
+      }
+      break;
+    }
+    case kpidLastAccessTime:
+      MySetFileTime(item.IsLastAccessTimeDefined, item.LastAccessTime, prop);
+      break;
+    case kpidCreationTime:
+      MySetFileTime(item.IsCreationTimeDefined, item.CreationTime, prop);
+      break;
+    case kpidLastWriteTime:
+      MySetFileTime(item.IsLastWriteTimeDefined, item.LastWriteTime, prop);
+      break;
+    case kpidAttributes:
+      if (item.AreAttributesDefined)
+        prop = item.Attributes;
+      break;
+    case kpidCRC:
+      if (item.IsFileCRCDefined)
+        prop = item.FileCRC;
+      break;
+    case kpidEncrypted:
+    {
+      prop = IsEncrypted(index2);
+      break;
+    }
+    #ifndef _SFX
+    case kpidMethod:
+      {
+        CNum folderIndex = _database.FileIndexToFolderIndexMap[index2];
+        if (folderIndex != kNumNoIndex)
+        {
+          const CFolder &folderInfo = _database.Folders[folderIndex];
+          UString methodsString;
+          for (int i = folderInfo.Coders.Size() - 1; i >= 0; i--)
+          {
+            const CCoderInfo &coderInfo = folderInfo.Coders[i];
+            if (!methodsString.IsEmpty())
+              methodsString += L' ';
+
+            {
+              UString methodName;
+              bool methodIsKnown = FindMethod(
+                  EXTERNAL_CODECS_VARS 
+                  coderInfo.MethodID, methodName);
+
+              if (methodIsKnown)
+              {
+                methodsString += methodName;
+                if (coderInfo.MethodID == k_LZMA)
+                {
+                  if (coderInfo.Properties.GetCapacity() >= 5)
+                  {
+                    methodsString += L":";
+                    UInt32 dicSize = GetUInt32FromMemLE(
+                      ((const Byte *)coderInfo.Properties + 1));
+                    methodsString += GetStringForSizeValue(dicSize);
+                  }
+                }
+                else if (coderInfo.MethodID == k_PPMD)
+                {
+                  if (coderInfo.Properties.GetCapacity() >= 5)
+                  {
+                    Byte order = *(const Byte *)coderInfo.Properties;
+                    methodsString += L":o";
+                    methodsString += ConvertUInt32ToString(order);
+                    methodsString += L":mem";
+                    UInt32 dicSize = GetUInt32FromMemLE(
+                      ((const Byte *)coderInfo.Properties + 1));
+                    methodsString += GetStringForSizeValue(dicSize);
+                  }
+                }
+                else if (coderInfo.MethodID == k_AES)
+                {
+                  if (coderInfo.Properties.GetCapacity() >= 1)
+                  {
+                    methodsString += L":";
+                    const Byte *data = (const Byte *)coderInfo.Properties;
+                    Byte firstByte = *data++;
+                    UInt32 numCyclesPower = firstByte & 0x3F;
+                    methodsString += ConvertUInt32ToString(numCyclesPower);
+                    /*
+                    if ((firstByte & 0xC0) != 0)
+                    {
+                      methodsString += L":";
+                      return S_OK;
+                      UInt32 saltSize = (firstByte >> 7) & 1;
+                      UInt32 ivSize = (firstByte >> 6) & 1;
+                      if (coderInfo.Properties.GetCapacity() >= 2)
+                      {
+                        Byte secondByte = *data++;
+                        saltSize += (secondByte >> 4);
+                        ivSize += (secondByte & 0x0F);
+                      }
+                    }
+                    */
+                  }
+                }
+                else
+                {
+                  if (coderInfo.Properties.GetCapacity() > 0)
+                  {
+                    methodsString += L":[";
+                    for (size_t bi = 0; bi < coderInfo.Properties.GetCapacity(); bi++)
+                    {
+                      if (bi > 5 && bi + 1 < coderInfo.Properties.GetCapacity())
+                      {
+                        methodsString += L"..";
+                        break;
+                      }
+                      else
+                        methodsString += GetHex2(coderInfo.Properties[bi]);
+                    }
+                    methodsString += L"]";
+                  }
+                }
+              }
+              else
+              {
+                methodsString += ConvertMethodIdToString(coderInfo.MethodID);
+              }
+            }
+          }
+          prop = methodsString;
+        }
+      }
+      break;
+    case kpidBlock:
+      {
+        CNum folderIndex = _database.FileIndexToFolderIndexMap[index2];
+        if (folderIndex != kNumNoIndex)
+          prop = (UInt32)folderIndex;
+      }
+      break;
+    case kpidPackedSize0:
+    case kpidPackedSize1:
+    case kpidPackedSize2:
+    case kpidPackedSize3:
+    case kpidPackedSize4:
+      {
+        CNum folderIndex = _database.FileIndexToFolderIndexMap[index2];
+        if (folderIndex != kNumNoIndex)
+        {
+          const CFolder &folderInfo = _database.Folders[folderIndex];
+          if (_database.FolderStartFileIndex[folderIndex] == (CNum)index2 &&
+              folderInfo.PackStreams.Size() > (int)(propID - kpidPackedSize0))
+          {
+            prop = _database.GetFolderPackStreamSize(folderIndex, propID - kpidPackedSize0);
+          }
+          else
+            prop = (UInt64)0;
+        }
+        else
+          prop = (UInt64)0;
+      }
+      break;
+    #endif
+    case kpidIsAnti:
+      prop = item.IsAnti;
+      break;
+  }
+  prop.Detach(value);
+  return S_OK;
+  COM_TRY_END
+}
+
+#ifdef _7Z_VOL
+
+static const wchar_t *kExt = L"7z";
+static const wchar_t *kAfterPart = L".7z";
+
+class CVolumeName
+{
+  bool _first;
+  UString _unchangedPart;
+  UString _changedPart;    
+  UString _afterPart;    
+public:
+  bool InitName(const UString &name)
+  {
+    _first = true;
+    int dotPos = name.ReverseFind('.');
+    UString basePart = name;
+    if (dotPos >= 0)
+    {
+      UString ext = name.Mid(dotPos + 1);
+      if (ext.CompareNoCase(kExt)==0 || 
+        ext.CompareNoCase(L"EXE") == 0)
+      {
+        _afterPart = kAfterPart;
+        basePart = name.Left(dotPos);
+      }
+    }
+
+    int numLetters = 1;
+    bool splitStyle = false;
+    if (basePart.Right(numLetters) == L"1")
+    {
+      while (numLetters < basePart.Length())
+      {
+        if (basePart[basePart.Length() - numLetters - 1] != '0')
+          break;
+        numLetters++;
+      }
+    }
+    else 
+      return false;
+    _unchangedPart = basePart.Left(basePart.Length() - numLetters);
+    _changedPart = basePart.Right(numLetters);
+    return true;
+  }
+
+  UString GetNextName()
+  {
+    UString newName; 
+    // if (_newStyle || !_first)
+    {
+      int i;
+      int numLetters = _changedPart.Length();
+      for (i = numLetters - 1; i >= 0; i--)
+      {
+        wchar_t c = _changedPart[i];
+        if (c == L'9')
+        {
+          c = L'0';
+          newName = c + newName;
+          if (i == 0)
+            newName = UString(L'1') + newName;
+          continue;
+        }
+        c++;
+        newName = UString(c) + newName;
+        i--;
+        for (; i >= 0; i--)
+          newName = _changedPart[i] + newName;
+        break;
+      }
+      _changedPart = newName;
+    }
+    _first = false;
+    return _unchangedPart + _changedPart + _afterPart;
+  }
+};
+
+#endif
+
+STDMETHODIMP CHandler::Open(IInStream *stream,
+    const UInt64 *maxCheckStartPosition, 
+    IArchiveOpenCallback *openArchiveCallback)
+{
+  COM_TRY_BEGIN
+  Close();
+  #ifndef _SFX
+  _fileInfoPopIDs.Clear();
+  #endif
+  try
+  {
+    CMyComPtr<IArchiveOpenCallback> openArchiveCallbackTemp = openArchiveCallback;
+    #ifdef _7Z_VOL
+    CVolumeName seqName;
+
+    CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback;
+    #endif
+
+    #ifndef _NO_CRYPTO
+    CMyComPtr<ICryptoGetTextPassword> getTextPassword;
+    if (openArchiveCallback)
+    {
+      openArchiveCallbackTemp.QueryInterface(
+          IID_ICryptoGetTextPassword, &getTextPassword);
+    }
+    #endif
+    #ifdef _7Z_VOL
+    if (openArchiveCallback)
+    {
+      openArchiveCallbackTemp.QueryInterface(IID_IArchiveOpenVolumeCallback, &openVolumeCallback);
+    }
+    for (;;)
+    {
+      CMyComPtr<IInStream> inStream;
+      if (!_volumes.IsEmpty())
+      {
+        if (!openVolumeCallback)
+          break;
+        if(_volumes.Size() == 1)
+        {
+          UString baseName;
+          {
+            NCOM::CPropVariant prop;
+            RINOK(openVolumeCallback->GetProperty(kpidName, &prop));
+            if (prop.vt != VT_BSTR)
+              break;
+            baseName = prop.bstrVal;
+          }
+          seqName.InitName(baseName);
+        }
+
+        UString fullName = seqName.GetNextName();
+        HRESULT result = openVolumeCallback->GetStream(fullName, &inStream);
+        if (result == S_FALSE)
+          break;
+        if (result != S_OK)
+          return result;
+        if (!stream)
+          break;
+      }
+      else
+        inStream = stream;
+
+      CInArchive archive;
+      RINOK(archive.Open(inStream, maxCheckStartPosition));
+
+      _volumes.Add(CVolume());
+      CVolume &volume = _volumes.Back();
+      CArchiveDatabaseEx &database = volume.Database;
+      volume.Stream = inStream;
+      volume.StartRef2Index = _refs.Size();
+
+      HRESULT result = archive.ReadDatabase(database
+          #ifndef _NO_CRYPTO
+          , getTextPassword
+          #endif
+          );
+      if (result != S_OK)
+      {
+        _volumes.Clear();
+        return result;
+      }
+      database.Fill();
+      for(int i = 0; i < database.Files.Size(); i++)
+      {
+        CRef refNew;
+        refNew.VolumeIndex = _volumes.Size() - 1;
+        refNew.ItemIndex = i;
+        _refs.Add(refNew);
+        /*
+        const CFileItem &file = database.Files[i];
+        int j;
+        */
+        /*
+        for (j = _refs.Size() - 1; j >= 0; j--)
+        {
+          CRef2 &ref2 = _refs[j];
+          const CRef &ref = ref2.Refs.Back();
+          const CVolume &volume2 = _volumes[ref.VolumeIndex];
+          const CArchiveDatabaseEx &database2 = volume2.Database;
+          const CFileItem &file2 = database2.Files[ref.ItemIndex];
+          if (file2.Name.CompareNoCase(file.Name) == 0)
+          {
+            if (!file.IsStartPosDefined)
+              continue;
+            if (file.StartPos != ref2.StartPos + ref2.UnPackSize)
+              continue;
+            ref2.Refs.Add(refNew);
+            break;
+          }
+        }
+        */
+        /*
+        j = -1;
+        if (j < 0)
+        {
+          CRef2 ref2New;
+          ref2New.Refs.Add(refNew);
+          j = _refs.Add(ref2New);
+        }
+        CRef2 &ref2 = _refs[j];
+        ref2.UnPackSize += file.UnPackSize;
+        ref2.PackSize += database.GetFilePackSize(i);
+        if (ref2.Refs.Size() == 1 && file.IsStartPosDefined)
+          ref2.StartPos = file.StartPos;
+        */
+      }
+      if (database.Files.Size() != 1)
+        break;
+      const CFileItem &file = database.Files.Front();
+      if (!file.IsStartPosDefined)
+        break;
+    }
+    #else
+    CInArchive archive;
+    RINOK(archive.Open(stream, maxCheckStartPosition));
+    HRESULT result = archive.ReadDatabase(
+      EXTERNAL_CODECS_VARS
+      _database
+      #ifndef _NO_CRYPTO
+      , getTextPassword
+      #endif
+      );
+    RINOK(result);
+    _database.Fill();
+    _inStream = stream;
+    #endif
+  }
+  catch(...)
+  {
+    Close();
+    return S_FALSE;
+  }
+  // _inStream = stream;
+  #ifndef _SFX
+  FillPopIDs();
+  #endif
+  return S_OK;
+  COM_TRY_END
+}
+
+STDMETHODIMP CHandler::Close()
+{
+  COM_TRY_BEGIN
+  #ifdef _7Z_VOL
+  _volumes.Clear();
+  _refs.Clear();
+  #else
+  _inStream.Release();
+  _database.Clear();
+  #endif
+  return S_OK;
+  COM_TRY_END
+}
+
+#ifdef _7Z_VOL
+STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
+{
+  if (index != 0)
+    return E_INVALIDARG;
+  *stream = 0;
+  CMultiStream *streamSpec = new CMultiStream;
+  CMyComPtr<ISequentialInStream> streamTemp = streamSpec;
+  
+  UInt64 pos = 0;
+  const UString *fileName;
+  for (int i = 0; i < _refs.Size(); i++)
+  {
+    const CRef &ref = _refs[i];
+    const CVolume &volume = _volumes[ref.VolumeIndex];
+    const CArchiveDatabaseEx &database = volume.Database;
+    const CFileItem &file = database.Files[ref.ItemIndex];
+    if (i == 0)
+      fileName = &file.Name;
+    else
+      if (fileName->Compare(file.Name) != 0)
+        return S_FALSE;
+    if (!file.IsStartPosDefined)
+      return S_FALSE;
+    if (file.StartPos != pos)
+      return S_FALSE;
+    CNum folderIndex = database.FileIndexToFolderIndexMap[ref.ItemIndex];
+    if (folderIndex == kNumNoIndex)
+    {
+      if (file.UnPackSize != 0)
+        return E_FAIL;
+      continue;
+    }
+    if (database.NumUnPackStreamsVector[folderIndex] != 1)
+      return S_FALSE;
+    const CFolder &folder = database.Folders[folderIndex];
+    if (folder.Coders.Size() != 1)
+      return S_FALSE;
+    const CCoderInfo &coder = folder.Coders.Front();
+    if (coder.NumInStreams != 1 || coder.NumOutStreams != 1)
+      return S_FALSE;
+    if (coder.MethodID != k_Copy)
+      return S_FALSE;
+
+    pos += file.UnPackSize;
+    CMultiStream::CSubStreamInfo subStreamInfo;
+    subStreamInfo.Stream = volume.Stream;
+    subStreamInfo.Pos = database.GetFolderStreamPos(folderIndex, 0);
+    subStreamInfo.Size = file.UnPackSize;
+    streamSpec->Streams.Add(subStreamInfo);
+  }
+  streamSpec->Init();
+  *stream = streamTemp.Detach();
+  return S_OK;
+}
+#endif
+
+
+#ifdef __7Z_SET_PROPERTIES
+#ifdef EXTRACT_ONLY
+
+STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProperties)
+{
+  COM_TRY_BEGIN
+  #ifdef COMPRESS_MT
+  const UInt32 numProcessors = NSystem::GetNumberOfProcessors();
+  _numThreads = numProcessors;
+  #endif
+
+  for (int i = 0; i < numProperties; i++)
+  {
+    UString name = names[i];
+    name.MakeUpper();
+    if (name.IsEmpty())
+      return E_INVALIDARG;
+    const PROPVARIANT &value = values[i];
+    UInt32 number;
+    int index = ParseStringToUInt32(name, number);
+    if (index == 0)
+    {
+      if(name.Left(2).CompareNoCase(L"MT") == 0)
+      {
+        #ifdef COMPRESS_MT
+        RINOK(ParseMtProp(name.Mid(2), value, numProcessors, _numThreads));
+        #endif
+        continue;
+      }
+      else
+        return E_INVALIDARG;
+    }
+  }
+  return S_OK;
+  COM_TRY_END
+}  
+
+#endif
+#endif
+
+IMPL_ISetCompressCodecsInfo
+
+}}