misc/libphysfs/lzma/CPP/7zip/Archive/Common/HandlerOut.cpp
author nemo
Mon, 10 Apr 2017 12:06:43 -0400
changeset 12213 bb5522e88ab2
permissions -rw-r--r--
bulk copy of latest physfs to our misc/libphysfs since this seems to fix an off-by-1 error reliably hit in readln read of 1 byte probably introduced in the addition of the buffered read. Whether this is excessive or whether libphysfs should even be maintained by us is another matter. But at least we shouldn't crash

// HandlerOutCommon.cpp

#include "StdAfx.h"

#include "HandlerOut.h"
#include "../../../Windows/PropVariant.h"
#include "../../../Common/StringToInt.h"
#include "../../ICoder.h"
#include "../Common/ParseProperties.h"

#ifdef COMPRESS_MT
#include "../../../Windows/System.h"
#endif

using namespace NWindows;

namespace NArchive {

static const wchar_t *kCopyMethod = L"Copy";
static const wchar_t *kLZMAMethodName = L"LZMA";
static const wchar_t *kLZMA2MethodName = L"LZMA2";
static const wchar_t *kBZip2MethodName = L"BZip2";
static const wchar_t *kPpmdMethodName = L"PPMd";
static const wchar_t *kDeflateMethodName = L"Deflate";
static const wchar_t *kDeflate64MethodName = L"Deflate64";

static const wchar_t *kLzmaMatchFinderX1 = L"HC4";
static const wchar_t *kLzmaMatchFinderX5 = L"BT4";

static const UInt32 kLzmaAlgoX1 = 0;
static const UInt32 kLzmaAlgoX5 = 1;

static const UInt32 kLzmaDicSizeX1 = 1 << 16;
static const UInt32 kLzmaDicSizeX3 = 1 << 20;
static const UInt32 kLzmaDicSizeX5 = 1 << 24;
static const UInt32 kLzmaDicSizeX7 = 1 << 25;
static const UInt32 kLzmaDicSizeX9 = 1 << 26;

static const UInt32 kLzmaFastBytesX1 = 32;
static const UInt32 kLzmaFastBytesX7 = 64;

static const UInt32 kPpmdMemSizeX1 = (1 << 22);
static const UInt32 kPpmdMemSizeX5 = (1 << 24);
static const UInt32 kPpmdMemSizeX7 = (1 << 26);
static const UInt32 kPpmdMemSizeX9 = (192 << 20);

static const UInt32 kPpmdOrderX1 = 4;
static const UInt32 kPpmdOrderX5 = 6;
static const UInt32 kPpmdOrderX7 = 16;
static const UInt32 kPpmdOrderX9 = 32;

static const UInt32 kDeflateAlgoX1 = 0;
static const UInt32 kDeflateAlgoX5 = 1;

static const UInt32 kDeflateFastBytesX1 = 32;
static const UInt32 kDeflateFastBytesX7 = 64;
static const UInt32 kDeflateFastBytesX9 = 128;

static const UInt32 kDeflatePassesX1 = 1;
static const UInt32 kDeflatePassesX7 = 3;
static const UInt32 kDeflatePassesX9 = 10;

static const UInt32 kBZip2NumPassesX1 = 1;
static const UInt32 kBZip2NumPassesX7 = 2;
static const UInt32 kBZip2NumPassesX9 = 7;

static const UInt32 kBZip2DicSizeX1 = 100000;
static const UInt32 kBZip2DicSizeX3 = 500000;
static const UInt32 kBZip2DicSizeX5 = 900000;

static const wchar_t *kDefaultMethodName = kLZMAMethodName;

static const wchar_t *kLzmaMatchFinderForHeaders = L"BT2";
static const UInt32 kDictionaryForHeaders = 1 << 20;
static const UInt32 kNumFastBytesForHeaders = 273;
static const UInt32 kAlgorithmForHeaders = kLzmaAlgoX5;

static bool AreEqual(const UString &methodName, const wchar_t *s)
  { return (methodName.CompareNoCase(s) == 0); }

static inline bool IsLZMAMethod(const UString &methodName)
{ 
  return 
    AreEqual(methodName, kLZMAMethodName) || 
    AreEqual(methodName, kLZMA2MethodName); 
}

static inline bool IsBZip2Method(const UString &methodName)
  { return AreEqual(methodName, kBZip2MethodName); }

static inline bool IsPpmdMethod(const UString &methodName)
  { return AreEqual(methodName, kPpmdMethodName); }

static inline bool IsDeflateMethod(const UString &methodName)
{ 
  return 
    AreEqual(methodName, kDeflateMethodName) || 
    AreEqual(methodName, kDeflate64MethodName); 
}

struct CNameToPropID
{
  PROPID PropID;
  VARTYPE VarType;
  const wchar_t *Name;
};

CNameToPropID g_NameToPropID[] = 
{
  { NCoderPropID::kOrder, VT_UI4, L"O" },
  { NCoderPropID::kPosStateBits, VT_UI4, L"PB" },
  { NCoderPropID::kLitContextBits, VT_UI4, L"LC" },
  { NCoderPropID::kLitPosBits, VT_UI4, L"LP" },
  { NCoderPropID::kEndMarker, VT_BOOL, L"eos" },

  { NCoderPropID::kNumPasses, VT_UI4, L"Pass" },
  { NCoderPropID::kNumFastBytes, VT_UI4, L"fb" },
  { NCoderPropID::kMatchFinderCycles, VT_UI4, L"mc" },
  { NCoderPropID::kAlgorithm, VT_UI4, L"a" },
  { NCoderPropID::kMatchFinder, VT_BSTR, L"mf" },
  { NCoderPropID::kNumThreads, VT_UI4, L"mt" }
};

static bool ConvertProperty(PROPVARIANT srcProp, VARTYPE varType, NCOM::CPropVariant &destProp)
{
  if (varType == srcProp.vt)
  {
    destProp = srcProp;
    return true;
  }
  if (varType == VT_UI1)
  {
    if (srcProp.vt == VT_UI4)
    {
      UInt32 value = srcProp.ulVal;
      if (value > 0xFF)
        return false;
      destProp = (Byte)value;
      return true;
    }
  }
  else if (varType == VT_BOOL)
  {
    bool res;
    if (SetBoolProperty(res, srcProp) != S_OK)
      return false;
    destProp = res;
    return true;
  }
  return false;
}
    
static int FindPropIdFromStringName(const UString &name)
{
  for (int i = 0; i < sizeof(g_NameToPropID) / sizeof(g_NameToPropID[0]); i++)
    if (name.CompareNoCase(g_NameToPropID[i].Name) == 0)
      return i;
  return -1;
}

static void SetOneMethodProp(COneMethodInfo &oneMethodInfo, PROPID propID, 
    const NWindows::NCOM::CPropVariant &value)
{
  for (int j = 0; j < oneMethodInfo.Properties.Size(); j++)
    if (oneMethodInfo.Properties[j].Id == propID)
      return;
  CProp property;
  property.Id = propID;
  property.Value = value;
  oneMethodInfo.Properties.Add(property);
}

void COutHandler::SetCompressionMethod2(COneMethodInfo &oneMethodInfo
    #ifdef COMPRESS_MT
    , UInt32 numThreads
    #endif
    )
{
  UInt32 level = _level;
  if (oneMethodInfo.MethodName.IsEmpty())
    oneMethodInfo.MethodName = kDefaultMethodName;
  
  if (IsLZMAMethod(oneMethodInfo.MethodName))
  {
    UInt32 dicSize = 
      (level >= 9 ? kLzmaDicSizeX9 : 
      (level >= 7 ? kLzmaDicSizeX7 : 
      (level >= 5 ? kLzmaDicSizeX5 : 
      (level >= 3 ? kLzmaDicSizeX3 : 
                    kLzmaDicSizeX1)))); 
    
    UInt32 algo = 
      (level >= 5 ? kLzmaAlgoX5 : 
                    kLzmaAlgoX1); 
    
    UInt32 fastBytes = 
      (level >= 7 ? kLzmaFastBytesX7 : 
                    kLzmaFastBytesX1); 
    
    const wchar_t *matchFinder = 
      (level >= 5 ? kLzmaMatchFinderX5 : 
                    kLzmaMatchFinderX1); 
    
    SetOneMethodProp(oneMethodInfo, NCoderPropID::kDictionarySize, dicSize);
    SetOneMethodProp(oneMethodInfo, NCoderPropID::kAlgorithm, algo);
    SetOneMethodProp(oneMethodInfo, NCoderPropID::kNumFastBytes, fastBytes);
    SetOneMethodProp(oneMethodInfo, NCoderPropID::kMatchFinder, matchFinder);
    #ifdef COMPRESS_MT
    SetOneMethodProp(oneMethodInfo, NCoderPropID::kNumThreads, numThreads);
    #endif
  }
  else if (IsDeflateMethod(oneMethodInfo.MethodName))
  {
    UInt32 fastBytes = 
      (level >= 9 ? kDeflateFastBytesX9 : 
      (level >= 7 ? kDeflateFastBytesX7 : 
                    kDeflateFastBytesX1));
    
    UInt32 numPasses = 
      (level >= 9 ? kDeflatePassesX9 :  
      (level >= 7 ? kDeflatePassesX7 : 
                    kDeflatePassesX1));
    
    UInt32 algo = 
      (level >= 5 ? kDeflateAlgoX5 : 
                    kDeflateAlgoX1); 
    
    SetOneMethodProp(oneMethodInfo, NCoderPropID::kAlgorithm, algo);
    SetOneMethodProp(oneMethodInfo, NCoderPropID::kNumFastBytes, fastBytes);
    SetOneMethodProp(oneMethodInfo, NCoderPropID::kNumPasses, numPasses);
  }
  else if (IsBZip2Method(oneMethodInfo.MethodName))
  {
    UInt32 numPasses = 
      (level >= 9 ? kBZip2NumPassesX9 : 
      (level >= 7 ? kBZip2NumPassesX7 :  
                    kBZip2NumPassesX1));
    
    UInt32 dicSize = 
      (level >= 5 ? kBZip2DicSizeX5 : 
      (level >= 3 ? kBZip2DicSizeX3 : 
                    kBZip2DicSizeX1));
    
    SetOneMethodProp(oneMethodInfo, NCoderPropID::kNumPasses, numPasses);
    SetOneMethodProp(oneMethodInfo, NCoderPropID::kDictionarySize, dicSize);
    #ifdef COMPRESS_MT
    SetOneMethodProp(oneMethodInfo, NCoderPropID::kNumThreads, numThreads);
    #endif
  }
  else if (IsPpmdMethod(oneMethodInfo.MethodName))
  {
    UInt32 useMemSize = 
      (level >= 9 ? kPpmdMemSizeX9 : 
      (level >= 7 ? kPpmdMemSizeX7 : 
      (level >= 5 ? kPpmdMemSizeX5 : 
                    kPpmdMemSizeX1)));
    
    UInt32 order = 
      (level >= 9 ? kPpmdOrderX9 : 
      (level >= 7 ? kPpmdOrderX7 : 
      (level >= 5 ? kPpmdOrderX5 : 
                    kPpmdOrderX1)));
    
    SetOneMethodProp(oneMethodInfo, NCoderPropID::kUsedMemorySize, useMemSize);
    SetOneMethodProp(oneMethodInfo, NCoderPropID::kOrder, order);
  }
}

static void SplitParams(const UString &srcString, UStringVector &subStrings)
{
  subStrings.Clear();
  UString name;
  int len = srcString.Length();
  if (len == 0)
    return;
  for (int i = 0; i < len; i++)
  {
    wchar_t c = srcString[i];
    if (c == L':')
    {
      subStrings.Add(name);
      name.Empty();
    }
    else
      name += c;
  }
  subStrings.Add(name);
}

static void SplitParam(const UString &param, UString &name, UString &value)
{
  int eqPos = param.Find(L'=');
  if (eqPos >= 0)
  {
    name = param.Left(eqPos);
    value = param.Mid(eqPos + 1);
    return;
  }
  for(int i = 0; i < param.Length(); i++)
  {
    wchar_t c = param[i];
    if (c >= L'0' && c <= L'9')
    {
      name = param.Left(i);
      value = param.Mid(i);
      return;
    }
  }
  name = param;
}

HRESULT COutHandler::SetParam(COneMethodInfo &oneMethodInfo, const UString &name, const UString &value)
{
  CProp property;
  if (
    name.CompareNoCase(L"D") == 0 || 
    name.CompareNoCase(L"MEM") == 0)
  {
    UInt32 dicSize;
    RINOK(ParsePropDictionaryValue(value, dicSize));
    if (name.CompareNoCase(L"D") == 0)
      property.Id = NCoderPropID::kDictionarySize;
    else
      property.Id = NCoderPropID::kUsedMemorySize;
    property.Value = dicSize;
    oneMethodInfo.Properties.Add(property);
  }
  else
  {
    int index = FindPropIdFromStringName(name);
    if (index < 0)
      return E_INVALIDARG;
    
    const CNameToPropID &nameToPropID = g_NameToPropID[index];
    property.Id = nameToPropID.PropID;
    
    NCOM::CPropVariant propValue;
    
    if (nameToPropID.VarType == VT_BSTR)
      propValue = value;
    else if (nameToPropID.VarType == VT_BOOL)
    {
      bool res;
      if (!StringToBool(value, res))
        return E_INVALIDARG;
      propValue = res;
    }
    else
    {
      UInt32 number;
      if (ParseStringToUInt32(value, number) == value.Length())
        propValue = number;
      else
        propValue = value;
    }
    
    if (!ConvertProperty(propValue, nameToPropID.VarType, property.Value))
      return E_INVALIDARG;
    
    oneMethodInfo.Properties.Add(property);
  }
  return S_OK;
}

HRESULT COutHandler::SetParams(COneMethodInfo &oneMethodInfo, const UString &srcString)
{
  UStringVector params;
  SplitParams(srcString, params);
  if (params.Size() > 0)
    oneMethodInfo.MethodName = params[0];
  for (int i = 1; i < params.Size(); i++)
  {
    const UString &param = params[i];
    UString name, value;
    SplitParam(param, name, value);
    RINOK(SetParam(oneMethodInfo, name, value));
  }
  return S_OK;
}

HRESULT COutHandler::SetSolidSettings(const UString &s)
{
  bool res;
  if (StringToBool(s, res))
  {
    if (res)
      InitSolid();
    else
      _numSolidFiles = 1;
    return S_OK;
  }
  UString s2 = s;
  s2.MakeUpper();
  for (int i = 0; i < s2.Length();)
  {
    const wchar_t *start = ((const wchar_t *)s2) + i;
    const wchar_t *end;
    UInt64 v = ConvertStringToUInt64(start, &end);
    if (start == end)
    {
      if (s2[i++] != 'E')
        return E_INVALIDARG;
      _solidExtension = true;
      continue;
    }
    i += (int)(end - start);
    if (i == s2.Length())
      return E_INVALIDARG;
    wchar_t c = s2[i++];
    switch(c)
    {
      case 'F':
        if (v < 1)
          v = 1;
        _numSolidFiles = v;
        break;
      case 'B':
        _numSolidBytes = v;
        _numSolidBytesDefined = true;
        break;
      case 'K':
        _numSolidBytes = (v << 10);
        _numSolidBytesDefined = true;
        break;
      case 'M':
        _numSolidBytes = (v << 20);
        _numSolidBytesDefined = true;
        break;
      case 'G':
        _numSolidBytes = (v << 30);
        _numSolidBytesDefined = true;
        break;
      default:
        return E_INVALIDARG;
    }
  }
  return S_OK;
}

HRESULT COutHandler::SetSolidSettings(const PROPVARIANT &value)
{
  switch(value.vt)
  {
    case VT_EMPTY:
      InitSolid();
      return S_OK;
    case VT_BSTR:
      return SetSolidSettings(value.bstrVal);
    default:
      return E_INVALIDARG;
  }
}

void COutHandler::Init()
{
  _removeSfxBlock = false;
  _compressHeaders = true;
  _encryptHeaders = false;
  
  WriteModified = true;
  WriteCreated = false;
  WriteAccessed = false;
  
  #ifdef COMPRESS_MT
  _numThreads = NWindows::NSystem::GetNumberOfProcessors();
  #endif
  
  _level = 5;
  _autoFilter = true;
  _volumeMode = false;
  _crcSize = 4;
  InitSolid();
}

void COutHandler::BeforeSetProperty()
{
  Init();
  #ifdef COMPRESS_MT
  numProcessors = NSystem::GetNumberOfProcessors();
  #endif

  mainDicSize = 0xFFFFFFFF;
  mainDicMethodIndex = 0xFFFFFFFF;
  minNumber = 0;
  _crcSize = 4;
}

HRESULT COutHandler::SetProperty(const wchar_t *nameSpec, const PROPVARIANT &value)
{
  UString name = nameSpec;
  name.MakeUpper();
  if (name.IsEmpty())
    return E_INVALIDARG;
  
  if (name[0] == 'X')
  {
    name.Delete(0);
    _level = 9;
    return ParsePropValue(name, value, _level);
  }
  
  if (name[0] == L'S')
  {
    name.Delete(0);
    if (name.IsEmpty())
      return SetSolidSettings(value);
    if (value.vt != VT_EMPTY)
      return E_INVALIDARG;
    return SetSolidSettings(name);
  }
  
  if (name == L"CRC")
  {
    _crcSize = 4;
    name.Delete(0, 3);
    return ParsePropValue(name, value, _crcSize);
  }
  
  UInt32 number;
  int index = ParseStringToUInt32(name, number);
  UString realName = name.Mid(index);
  if (index == 0)
  {
    if(name.Left(2).CompareNoCase(L"MT") == 0)
    {
      #ifdef COMPRESS_MT
      RINOK(ParseMtProp(name.Mid(2), value, numProcessors, _numThreads));
      #endif
      return S_OK;
    }
    if (name.CompareNoCase(L"RSFX") == 0)
      return SetBoolProperty(_removeSfxBlock, value);
    if (name.CompareNoCase(L"F") == 0)
      return SetBoolProperty(_autoFilter, value);
    if (name.CompareNoCase(L"HC") == 0)
      return SetBoolProperty(_compressHeaders, value);
    if (name.CompareNoCase(L"HCF") == 0)
    {
      bool compressHeadersFull = true;
      RINOK(SetBoolProperty(compressHeadersFull, value));
      if (!compressHeadersFull)
        return E_INVALIDARG;
      return S_OK;
    }
    if (name.CompareNoCase(L"HE") == 0)
      return SetBoolProperty(_encryptHeaders, value);
    if (name.CompareNoCase(L"TM") == 0)
      return SetBoolProperty(WriteModified, value);
    if (name.CompareNoCase(L"TC") == 0)
      return SetBoolProperty(WriteCreated, value);
    if (name.CompareNoCase(L"TA") == 0)
      return SetBoolProperty(WriteAccessed, value);
    if (name.CompareNoCase(L"V") == 0)
      return SetBoolProperty(_volumeMode, value);
    number = 0;
  }
  if (number > 10000)
    return E_FAIL;
  if (number < minNumber)
    return E_INVALIDARG;
  number -= minNumber;
  for(int j = _methods.Size(); j <= (int)number; j++)
  {
    COneMethodInfo oneMethodInfo;
    _methods.Add(oneMethodInfo);
  }
  
  COneMethodInfo &oneMethodInfo = _methods[number];
  
  if (realName.Length() == 0)
  {
    if (value.vt != VT_BSTR)
      return E_INVALIDARG;
    
    RINOK(SetParams(oneMethodInfo, value.bstrVal));
  }
  else
  {
    CProp property;
    if (realName.Left(1).CompareNoCase(L"D") == 0)
    {
      UInt32 dicSize;
      RINOK(ParsePropDictionaryValue(realName.Mid(1), value, dicSize));
      property.Id = NCoderPropID::kDictionarySize;
      property.Value = dicSize;
      oneMethodInfo.Properties.Add(property);
      if (number <= mainDicMethodIndex)
        mainDicSize = dicSize;
    }
    else if (realName.Left(3).CompareNoCase(L"MEM") == 0)
    {
      UInt32 dicSize;
      RINOK(ParsePropDictionaryValue(realName.Mid(3), value, dicSize));
      property.Id = NCoderPropID::kUsedMemorySize;
      property.Value = dicSize;
      oneMethodInfo.Properties.Add(property);
      if (number <= mainDicMethodIndex)
        mainDicSize = dicSize;
    }
    else
    {
      int index = FindPropIdFromStringName(realName);
      if (index < 0)
        return E_INVALIDARG;
      
      const CNameToPropID &nameToPropID = g_NameToPropID[index];
      property.Id = nameToPropID.PropID;
      
      if (!ConvertProperty(value, nameToPropID.VarType, property.Value))
        return E_INVALIDARG;
      
      oneMethodInfo.Properties.Add(property);
    }
  }
  return S_OK;
}  

}