// 7zHandler.cpp

#include "StdAfx.h"

#include "../../../../C/CpuArch.h"

#include "../../../Common/ComTry.h"
#include "../../../Common/IntToString.h"

#ifndef Z7_7Z_SET_PROPERTIES
#include "../../../Windows/System.h"
#endif

#include "../Common/ItemNameUtils.h"

#include "7zHandler.h"
#include "7zProperties.h"

#ifdef Z7_7Z_SET_PROPERTIES
#ifdef Z7_EXTRACT_ONLY
#include "../Common/ParseProperties.h"
#endif
#endif

using namespace NWindows;
using namespace NCOM;

namespace NArchive {
namespace N7z {

CHandler::CHandler()
{
  #ifndef Z7_NO_CRYPTO
  _isEncrypted = false;
  _passwordIsDefined = false;
  #endif

  #ifdef Z7_EXTRACT_ONLY
  
  _crcSize = 4;
  
  #ifdef Z7_7Z_SET_PROPERTIES
  _useMultiThreadMixer = true;
  #endif
  
  #endif
}

Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
{
  *numItems = _db.Files.Size();
  return S_OK;
}

#ifdef Z7_SFX

IMP_IInArchive_ArcProps_NO_Table

Z7_COM7F_IMF(CHandler::GetNumberOfProperties(UInt32 *numProps))
{
  *numProps = 0;
  return S_OK;
}

Z7_COM7F_IMF(CHandler::GetPropertyInfo(UInt32 /* index */,
      BSTR * /* name */, PROPID * /* propID */, VARTYPE * /* varType */))
{
  return E_NOTIMPL;
}

#else

static const Byte kArcProps[] =
{
  kpidHeadersSize,
  kpidMethod,
  kpidSolid,
  kpidNumBlocks
  // , kpidIsTree
};

IMP_IInArchive_ArcProps

static inline char GetHex(unsigned value)
{
  return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10)));
}

static unsigned ConvertMethodIdToString_Back(char *s, UInt64 id)
{
  int len = 0;
  do
  {
    s[--len] = GetHex((unsigned)id & 0xF); id >>= 4;
    s[--len] = GetHex((unsigned)id & 0xF); id >>= 4;
  }
  while (id != 0);
  return (unsigned)-len;
}

static void ConvertMethodIdToString(AString &res, UInt64 id)
{
  const unsigned kLen = 32;
  char s[kLen];
  unsigned len = kLen - 1;
  s[len] = 0;
  res += s + len - ConvertMethodIdToString_Back(s + len, id);
}


static char *GetStringForSizeValue(char *s, UInt32 val)
{
  for (unsigned i = 0; i < 32; i++)
    if (((UInt32)1 << i) == val)
    {
      if (i >= 10)
      {
        *s++= (char)('0' + i / 10);
        i %= 10;
      }
      *s++ = (char)('0' + i);
      *s = 0;
      return s;
    }
  
  char c = 'b';
  if      ((val & ((1 << 20) - 1)) == 0) { val >>= 20; c = 'm'; }
  else if ((val & ((1 << 10) - 1)) == 0) { val >>= 10; c = 'k'; }
  s = ConvertUInt32ToString(val, s);
  *s++ = c;
  *s = 0;
  return s;
}


static void GetLzma2String(char *s, unsigned d)
{
  if (d > 40)
  {
    *s = 0;
    return;
    // s = MyStpCpy(s, "unsup");
  }
  else if ((d & 1) == 0)
    d = (d >> 1) + 12;
  else
  {
    // s = GetStringForSizeValue(s, (UInt32)3 << ((d >> 1) + 11));
    d = (d >> 1) + 1;
    char c = 'k';
    if (d >= 10)
    {
      c = 'm';
      d -= 10;
    }
    s = ConvertUInt32ToString((UInt32)3 << d, s);
    *s++ = c;
    *s = 0;
    return;
  }
  ConvertUInt32ToString(d, s);
}


/*
static inline void AddHexToString(UString &res, Byte value)
{
  res += GetHex((Byte)(value >> 4));
  res += GetHex((Byte)(value & 0xF));
}
*/

static char *AddProp32(char *s, const char *name, UInt32 v)
{
  *s++ = ':';
  s = MyStpCpy(s, name);
  return ConvertUInt32ToString(v, s);
}
 
void CHandler::AddMethodName(AString &s, UInt64 id)
{
  AString name;
  FindMethod(EXTERNAL_CODECS_VARS id, name);
  if (name.IsEmpty())
    ConvertMethodIdToString(s, id);
  else
    s += name;
}

#endif

Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
{
  #ifndef Z7_SFX
  COM_TRY_BEGIN
  #endif
  NCOM::CPropVariant prop;
  switch (propID)
  {
    #ifndef Z7_SFX
    case kpidMethod:
    {
      AString s;
      const CParsedMethods &pm = _db.ParsedMethods;
      FOR_VECTOR (i, pm.IDs)
      {
        UInt64 id = pm.IDs[i];
        s.Add_Space_if_NotEmpty();
        char temp[16];
        if (id == k_LZMA2)
        {
          s += "LZMA2:";
          GetLzma2String(temp, pm.Lzma2Prop);
          s += temp;
        }
        else if (id == k_LZMA)
        {
          s += "LZMA:";
          GetStringForSizeValue(temp, pm.LzmaDic);
          s += temp;
        }
        /*
        else if (id == k_ZSTD)
        {
          s += "ZSTD";
        }
        */
        else
          AddMethodName(s, id);
      }
      prop = s;
      break;
    }
    case kpidSolid: prop = _db.IsSolid(); break;
    case kpidNumBlocks: prop = (UInt32)_db.NumFolders; break;
    case kpidHeadersSize:  prop = _db.HeadersSize; break;
    case kpidPhySize:  prop = _db.PhySize; break;
    case kpidOffset: if (_db.ArcInfo.StartPosition != 0) prop = _db.ArcInfo.StartPosition; break;
    /*
    case kpidIsTree: if (_db.IsTree) prop = true; break;
    case kpidIsAltStream: if (_db.ThereAreAltStreams) prop = true; break;
    case kpidIsAux: if (_db.IsTree) prop = true; break;
    */
    // case kpidError: if (_db.ThereIsHeaderError) prop = "Header error"; break;
    #endif
    
    case kpidWarningFlags:
    {
      UInt32 v = 0;
      if (_db.StartHeaderWasRecovered) v |= kpv_ErrorFlags_HeadersError;
      if (_db.UnsupportedFeatureWarning) v |= kpv_ErrorFlags_UnsupportedFeature;
      if (v != 0)
        prop = v;
      break;
    }
    
    case kpidErrorFlags:
    {
      UInt32 v = 0;
      if (!_db.IsArc) v |= kpv_ErrorFlags_IsNotArc;
      if (_db.ThereIsHeaderError) v |= kpv_ErrorFlags_HeadersError;
      if (_db.UnexpectedEnd) v |= kpv_ErrorFlags_UnexpectedEnd;
      // if (_db.UnsupportedVersion) v |= kpv_ErrorFlags_Unsupported;
      if (_db.UnsupportedFeatureError) v |= kpv_ErrorFlags_UnsupportedFeature;
      prop = v;
      break;
    }

    case kpidReadOnly:
    {
      if (!_db.CanUpdate())
        prop = true;
      break;
    }
    default: break;
  }
  return prop.Detach(value);
  #ifndef Z7_SFX
  COM_TRY_END
  #endif
}

static void SetFileTimeProp_From_UInt64Def(PROPVARIANT *prop, const CUInt64DefVector &v, unsigned index)
{
  UInt64 value;
  if (v.GetItem(index, value))
    PropVarEm_Set_FileTime64_Prec(prop, value, k_PropVar_TimePrec_100ns);
}

bool CHandler::IsFolderEncrypted(CNum folderIndex) const
{
  if (folderIndex == kNumNoIndex)
    return false;
  const size_t startPos = _db.FoCodersDataOffset[folderIndex];
  const Byte *p = _db.CodersData.ConstData() + startPos;
  const size_t size = _db.FoCodersDataOffset[folderIndex + 1] - startPos;
  CInByte2 inByte;
  inByte.Init(p, size);
  
  CNum numCoders = inByte.ReadNum();
  for (; numCoders != 0; numCoders--)
  {
    const Byte mainByte = inByte.ReadByte();
    const unsigned idSize = (mainByte & 0xF);
    const Byte *longID = inByte.GetPtr();
    UInt64 id64 = 0;
    for (unsigned j = 0; j < idSize; j++)
      id64 = ((id64 << 8) | longID[j]);
    inByte.SkipDataNoCheck(idSize);
    if (id64 == k_AES)
      return true;
    if ((mainByte & 0x20) != 0)
      inByte.SkipDataNoCheck(inByte.ReadNum());
  }
  return false;
}

Z7_COM7F_IMF(CHandler::GetNumRawProps(UInt32 *numProps))
{
  *numProps = 0;
  return S_OK;
}

Z7_COM7F_IMF(CHandler::GetRawPropInfo(UInt32 /* index */, BSTR *name, PROPID *propID))
{
  *name = NULL;
  *propID = kpidNtSecure;
  return S_OK;
}

Z7_COM7F_IMF(CHandler::GetParent(UInt32 /* index */, UInt32 *parent, UInt32 *parentType))
{
  /*
  const CFileItem &file = _db.Files[index];
  *parentType = (file.IsAltStream ? NParentType::kAltStream : NParentType::kDir);
  *parent = (UInt32)(Int32)file.Parent;
  */
  *parentType = NParentType::kDir;
  *parent = (UInt32)(Int32)-1;
  return S_OK;
}

Z7_COM7F_IMF(CHandler::GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType))
{
  *data = NULL;
  *dataSize = 0;
  *propType = 0;

  if (/* _db.IsTree && propID == kpidName ||
      !_db.IsTree && */ propID == kpidPath)
  {
    if (_db.NameOffsets && _db.NamesBuf)
    {
      const size_t offset = _db.NameOffsets[index];
      const size_t size = (_db.NameOffsets[index + 1] - offset) * 2;
      if (size < ((UInt32)1 << 31))
      {
        *data = (const void *)(_db.NamesBuf.ConstData() + offset * 2);
        *dataSize = (UInt32)size;
        *propType = NPropDataType::kUtf16z;
      }
    }
    return S_OK;
  }
  /*
  if (propID == kpidNtSecure)
  {
    if (index < (UInt32)_db.SecureIDs.Size())
    {
      int id = _db.SecureIDs[index];
      size_t offs = _db.SecureOffsets[id];
      size_t size = _db.SecureOffsets[id + 1] - offs;
      if (size >= 0)
      {
        *data = _db.SecureBuf + offs;
        *dataSize = (UInt32)size;
        *propType = NPropDataType::kRaw;
      }
    }
  }
  */
  return S_OK;
}

#ifndef Z7_SFX

/*
   Used to obtain the method with level from first archived item block where it would be able to retrieve them
   (and will set this method (and if found also the level) in the caller (only if no methods are specified
   in command-line arguments or supplied from UI);
   The filtered methods (BCJ, etc) and encryption (7zAES) will be ignored, so first not filtered method of 
   first block will win.
   Current restrictions (todo's):
   - complex compressed archives, e. g. using multiple codecs per item (first method will win) or different 
     methods across items groups (method of first item will win);
   - not all methods allow to obtain levels (e. g. LZMA, LZMA2, PPMD)
   - ZSTD fast levels will be not set to codec at the moment (and resulting to level 1); also other parameters
     like windowLog are not supported currently by recompression.
 */
bool CHandler::ObtainMethodFromBlocks(CHandler::MethodInfo *info) const
{
  // no blocks - no methods can be found:
  if (!_db.NumFolders) return false;
  // for every block:
  CNum folderIndex;
  for (folderIndex = 0; folderIndex < _db.NumFolders; folderIndex++) {
    if (ObtainBlockMethods(folderIndex, NULL, info) == S_OK &&
        !info->methName.IsEmpty()
    ) {
      return true;
    }
  }
  return false;
}

HRESULT CHandler::ObtainBlockMethods(CNum folderIndex, PROPVARIANT *prop, CHandler::MethodInfo *info) const
{
  if (prop) PropVariant_Clear(prop);
  if (folderIndex == kNumNoIndex)
    return S_OK;
  const unsigned kTempSize = 256;
  char temp[kTempSize];
  unsigned pos = kTempSize;
  temp[--pos] = 0;
 
  const size_t startPos = _db.FoCodersDataOffset[folderIndex];
  const Byte *p = _db.CodersData.ConstData() + startPos;
  const size_t size = _db.FoCodersDataOffset[folderIndex + 1] - startPos;
  CInByte2 inByte;
  inByte.Init(p, size);
  
  // numCoders == 0 ???
  CNum numCoders = inByte.ReadNum();
  bool needSpace = false;
  
  for (; numCoders != 0; numCoders--)
  {
    if (pos < 32) // max size of property
      break;
    const Byte mainByte = inByte.ReadByte();
    UInt64 id64 = 0;
    const unsigned idSize = (mainByte & 0xF);
    const Byte *longID = inByte.GetPtr();
    for (unsigned j = 0; j < idSize; j++)
      id64 = ((id64 << 8) | longID[j]);
    inByte.SkipDataNoCheck(idSize);

    if ((mainByte & 0x10) != 0)
    {
      inByte.ReadNum(); // NumInStreams
      inByte.ReadNum(); // NumOutStreams
    }
  
    CNum propsSize = 0;
    const Byte *props = NULL;
    if ((mainByte & 0x20) != 0)
    {
      propsSize = inByte.ReadNum();
      props = inByte.GetPtr();
      inByte.SkipDataNoCheck(propsSize);
    }
    
    const char *name = NULL;
    int lev = (int)-1;
    char s[32];
    s[0] = 0;
    
    if (id64 <= (UInt32)0xFFFFFFFF)
    {
      const UInt32 id = (UInt32)id64;
      switch (id) {
      case k_LZMA:
        name = "LZMA";
        if (info) {goto obtainInfo;}
        if (propsSize == 5)
        {
          const UInt32 dicSize = GetUi32((const Byte *)props + 1);
          char *dest = GetStringForSizeValue(s, dicSize);
          UInt32 d = props[0];
          if (d != 0x5D)
          {
            const UInt32 lc = d % 9;
            d /= 9;
            const UInt32 pb = d / 5;
            const UInt32 lp = d % 5;
            if (lc != 3) dest = AddProp32(dest, "lc", lc);
            if (lp != 0) dest = AddProp32(dest, "lp", lp);
            if (pb != 2) dest = AddProp32(dest, "pb", pb);
          }
        }
        break;
      case k_LZMA2:
        name = "LZMA2";
        if (info) {goto obtainInfo;}
        if (propsSize == 1)
          GetLzma2String(s, props[0]);
        break;
      case k_PPMD:
        name = "PPMD";
        if (info) {goto obtainInfo;}
        if (propsSize == 5)
        {
          char *dest = s;
          *dest++ = 'o';
          dest = ConvertUInt32ToString(*props, dest);
          dest = MyStpCpy(dest, ":mem");
          GetStringForSizeValue(dest, GetUi32(props + 1));
        }
        break;
      case k_LZHAM:
        name = "LZHAM";
        if (propsSize == 5)
        {
          if (info) {lev = props[2]; goto obtainInfo;}
          char *dest = s;
          *dest++ = 'v';
          ConvertUInt32ToString(props[0], dest);
          dest += MyStringLen(dest);
          *dest++ = ',';
          *dest++ = 'd';
          ConvertUInt32ToString(props[1], dest);
          dest += MyStringLen(dest);
          *dest++ = ',';
          *dest++ = 'l';
          ConvertUInt32ToString(props[2], dest);
          dest += MyStringLen(dest);
        }
        break;
      case k_BROTLI:
        name = "Brotli";
        if (propsSize == 3)
        {
          if (info) {lev = props[2]; goto obtainInfo;}
          char *dest = s;
          *dest++ = 'v';
          ConvertUInt32ToString(props[0], dest);
          dest += MyStringLen(dest);
          *dest++ = '.';
          ConvertUInt32ToString(props[1], dest);
          dest += MyStringLen(dest);
          *dest++ = ',';
          *dest++ = 'l';
          ConvertUInt32ToString(props[2], dest);
          dest += MyStringLen(dest);
        }
        break;
      case k_LIZARD:
        name = "Lizard";
        if (propsSize == 3)
        {
          if (info) {lev = props[2]; goto obtainInfo;}
          char *dest = s;
          *dest++ = 'v';
          ConvertUInt32ToString(props[0], dest);
          dest += MyStringLen(dest);
          *dest++ = '.';
          ConvertUInt32ToString(props[1], dest);
          dest += MyStringLen(dest);
          *dest++ = ',';
          *dest++ = 'l';
          ConvertUInt32ToString(props[2], dest);
          dest += MyStringLen(dest);
        }
        break;
      case k_LZ4:
        name = "LZ4";
        if (propsSize == 3 || propsSize == 5)
        {
          if (info) {lev = props[2]; goto obtainInfo;}
          char *dest = s;
          *dest++ = 'v';
          ConvertUInt32ToString(props[0], dest);
          dest += MyStringLen(dest);
          *dest++ = '.';
          ConvertUInt32ToString(props[1], dest);
          dest += MyStringLen(dest);
          *dest++ = ',';
          *dest++ = 'l';
          ConvertUInt32ToString(props[2], dest);
          dest += MyStringLen(dest);
        }
        break;
      case k_LZ5:
        name = "LZ5";
        if (propsSize == 3 || propsSize == 5)
        {
          if (info) {lev = props[2]; goto obtainInfo;}
          char *dest = s;
          *dest++ = 'v';
          ConvertUInt32ToString(props[0], dest);
          dest += MyStringLen(dest);
          *dest++ = '.';
          ConvertUInt32ToString(props[1], dest);
          dest += MyStringLen(dest);
          *dest++ = ',';
          *dest++ = 'l';
          ConvertUInt32ToString(props[2], dest);
          dest += MyStringLen(dest);
        }
        break;
      case k_ZSTD:
        name = "ZSTD";
        if (propsSize == 3 || propsSize == 5)
        {
          UInt32 l = props[2];
          if (info) {
            if (l <= 22 || l == Z7_ZSTD_ULTIMATE_LEV) {
              lev = l;
            } else {
              // todo: need parameter to set fast mode (to NCoderPropID::kFast)
              // fast = 1;
              // l -= 32;
              lev = 1; // use fastest positive level at the moment;
            }
            goto obtainInfo;
          }
          char *dest = s;
          *dest++ = 'v';
          ConvertUInt32ToString(props[0], dest);
          dest += MyStringLen(dest);
          *dest++ = '.';
          ConvertUInt32ToString(props[1], dest);
          dest += MyStringLen(dest);
          *dest++ = ',';
          if (l <= 22) {
            *dest++ = 'l';
            ConvertUInt32ToString(l, dest);
          } else if (l == Z7_ZSTD_ULTIMATE_LEV) { // special level for zstd --max (advanced ultra) 
            *dest++ = 'm';
            *dest++ = 'a';
            *dest++ = 'x';
            *dest = '\0';
          } else {
            *dest++ = 'f';
            *dest++ = 'l';
            ConvertUInt32ToString(l - Z7_ZSTD_FAST_LEV_INC, dest);
          }
          dest += MyStringLen(dest);
        }
        break;
      // filtered methods from here, if we need method info, ignore them - continue search
      case k_Delta:
        if (info) continue;
        name = "Delta";
        if (propsSize == 1)
          ConvertUInt32ToString((UInt32)props[0] + 1, s);
        break;
      case k_ARM64:
        if (info) continue;
        name = "ARM64";
        break;
      case k_RISCV:
        if (info) continue;
        if (id == k_RISCV) name = "RISCV";
        if (propsSize == 4)
          ConvertUInt32ToString(GetUi32(props), s);
        /*
        else if (propsSize != 0)
          MyStringCopy(s, "unsupported");
        */
        break;
      case k_BCJ2:
        if (info) continue;
        name = "BCJ2";
        break;
      case k_BCJ:
        if (info) continue;
        name = "BCJ";
        break;
      case k_SWAP2:
        if (info) continue;
        name = "SWAP2";
        break;
      case k_SWAP4:
        if (info) continue;
        name = "SWAP4";
        break;
      case k_PPC:
        if (info) continue;
        name = "PPC";
        break;
      case k_IA64:
        if (info) continue;
        name = "IA64";
        break;
      case k_ARM:
        if (info) continue;
        name = "ARM";
        break;
      case k_ARMT:
        if (info) continue;
        name = "ARMT";
        break;
      case k_SPARC:
        if (info) continue;
        name = "SPARC";
        break;
      // and encryption (also ignore by obtaining info):
      case k_AES:
        if (info) continue;
        name = "7zAES";
        if (propsSize >= 1)
        {
          const Byte firstByte = props[0];
          const UInt32 numCyclesPower = firstByte & 0x3F;
          ConvertUInt32ToString(numCyclesPower, s);
        }
        break;
      }
    }
  
  // if need to retrieve single method info only:
  obtainInfo:
    if (info) {
      if (name)
      {
        info->methName = name;
      }
      else
      {
        FindMethod(EXTERNAL_CODECS_VARS id64, info->methName);
      }
      //printf("******* %d/%d %s - %d\n", folderIndex, numCoders, info->methName.Ptr(), lev);
      // found 1st useable for compression method, use it for now:
      if (!info->methName.IsEmpty()) {
        info->level = lev;
        return S_OK;
      }
      // search for another method:
      continue;
    }
    
    if (name)
    {
      const unsigned nameLen = MyStringLen(name);
      const unsigned propsLen = MyStringLen(s);
      unsigned totalLen = nameLen + propsLen;
      if (propsLen != 0)
        totalLen++;
      if (needSpace)
        totalLen++;
      if (totalLen + 5 >= pos)
        break;
      pos -= totalLen;
      MyStringCopy(temp + pos, name);
      if (propsLen != 0)
      {
        char *dest = temp + pos + nameLen;
        *dest++ = ':';
        MyStringCopy(dest, s);
      }
      if (needSpace)
        temp[pos + totalLen - 1] = ' ';
    }
    else
    {
      AString methodName;
      FindMethod(EXTERNAL_CODECS_VARS id64, methodName);
      if (needSpace)
        temp[--pos] = ' ';
      if (methodName.IsEmpty())
        pos -= ConvertMethodIdToString_Back(temp + pos, id64);
      else
      {
        const unsigned len = methodName.Len();
        if (len + 5 > pos)
          break;
        pos -= len;
        for (unsigned i = 0; i < len; i++)
          temp[pos + i] = methodName[i];
      }
    }
    needSpace = true;
  }
  
  if (numCoders != 0 && pos >= 4)
  {
    temp[--pos] = ' ';
    temp[--pos] = '.';
    temp[--pos] = '.';
    temp[--pos] = '.';
  }
  
  if (prop)
    return PropVarEm_Set_Str(prop, temp + pos);
  return S_OK;
  // }
}

HRESULT CHandler::SetMethodToProp(CNum folderIndex, PROPVARIANT *prop) const
{
  return ObtainBlockMethods(folderIndex, prop, NULL);
}

#endif

Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
{
  RINOK(PropVariant_Clear(value))
  // COM_TRY_BEGIN
  // NCOM::CPropVariant prop;
  
  /*
  const CRef2 &ref2 = _refs[index];
  if (ref2.Refs.IsEmpty())
    return E_FAIL;
  const CRef &ref = ref2.Refs.Front();
  */
  
  const CFileItem &item = _db.Files[index];
  const UInt32 index2 = index;

  switch (propID)
  {
    case kpidIsDir: PropVarEm_Set_Bool(value, item.IsDir); break;
    case kpidSize:
    {
      PropVarEm_Set_UInt64(value, item.Size);
      // prop = ref2.Size;
      break;
    }
    case kpidPackSize:
    {
      // prop = ref2.PackSize;
      {
        const CNum folderIndex = _db.FileIndexToFolderIndexMap[index2];
        if (folderIndex != kNumNoIndex)
        {
          if (_db.FolderStartFileIndex[folderIndex] == (CNum)index2)
            PropVarEm_Set_UInt64(value, _db.GetFolderFullPackSize(folderIndex));
          /*
          else
            PropVarEm_Set_UInt64(value, 0);
          */
        }
        else
          PropVarEm_Set_UInt64(value, 0);
      }
      break;
    }
    // case kpidIsAux: prop = _db.IsItemAux(index2); break;
    case kpidPosition:  { UInt64 v; if (_db.StartPos.GetItem(index2, v)) PropVarEm_Set_UInt64(value, v); break; }
    case kpidCTime:  SetFileTimeProp_From_UInt64Def(value, _db.CTime, index2); break;
    case kpidATime:  SetFileTimeProp_From_UInt64Def(value, _db.ATime, index2); break;
    case kpidMTime:  SetFileTimeProp_From_UInt64Def(value, _db.MTime, index2); break;
    case kpidAttrib:  if (_db.Attrib.ValidAndDefined(index2)) PropVarEm_Set_UInt32(value, _db.Attrib.Vals[index2]); break;
    case kpidCRC:  if (item.CrcDefined) PropVarEm_Set_UInt32(value, item.Crc); break;
    case kpidEncrypted:  PropVarEm_Set_Bool(value, IsFolderEncrypted(_db.FileIndexToFolderIndexMap[index2])); break;
    case kpidIsAnti:  PropVarEm_Set_Bool(value, _db.IsItemAnti(index2)); break;
    /*
    case kpidIsAltStream:  prop = item.IsAltStream; break;
    case kpidNtSecure:
      {
        int id = _db.SecureIDs[index];
        size_t offs = _db.SecureOffsets[id];
        size_t size = _db.SecureOffsets[id + 1] - offs;
        if (size >= 0)
        {
          prop.SetBlob(_db.SecureBuf + offs, (ULONG)size);
        }
        break;
      }
    */

    case kpidPath: return _db.GetPath_Prop(index, value);
    
    #ifndef Z7_SFX
    
    case kpidMethod: return SetMethodToProp(_db.FileIndexToFolderIndexMap[index2], value);
    case kpidBlock:
      {
        const CNum folderIndex = _db.FileIndexToFolderIndexMap[index2];
        if (folderIndex != kNumNoIndex)
          PropVarEm_Set_UInt32(value, (UInt32)folderIndex);
      }
      break;
   #ifdef Z7_7Z_SHOW_PACK_STREAMS_SIZES
    case kpidPackedSize0:
    case kpidPackedSize1:
    case kpidPackedSize2:
    case kpidPackedSize3:
    case kpidPackedSize4:
      {
        const CNum folderIndex = _db.FileIndexToFolderIndexMap[index2];
        if (folderIndex != kNumNoIndex)
        {
          if (_db.FolderStartFileIndex[folderIndex] == (CNum)index2 &&
              _db.FoStartPackStreamIndex[folderIndex + 1] -
              _db.FoStartPackStreamIndex[folderIndex] > (propID - kpidPackedSize0))
          {
            PropVarEm_Set_UInt64(value, _db.GetFolderPackStreamSize(folderIndex, propID - kpidPackedSize0));
          }
        }
        else
          PropVarEm_Set_UInt64(value, 0);
      }
      break;
   #endif
    
    #endif
    default: break;
  }
  // return prop.Detach(value);
  return S_OK;
  // COM_TRY_END
}

Z7_COM7F_IMF(CHandler::Open(IInStream *stream,
    const UInt64 *maxCheckStartPosition,
    IArchiveOpenCallback *openArchiveCallback))
{
  COM_TRY_BEGIN
  Close();
  #ifndef Z7_SFX
  _fileInfoPopIDs.Clear();
  #endif
  
  try
  {
    CMyComPtr<IArchiveOpenCallback> openArchiveCallbackTemp = openArchiveCallback;

    #ifndef Z7_NO_CRYPTO
    CMyComPtr<ICryptoGetTextPassword> getTextPassword;
    if (openArchiveCallback)
      openArchiveCallbackTemp.QueryInterface(IID_ICryptoGetTextPassword, &getTextPassword);
    #endif

    CInArchive archive(
          #ifdef Z7_7Z_SET_PROPERTIES
          _useMultiThreadMixer
          #else
          true
          #endif
          );
    _db.IsArc = false;
    RINOK(archive.Open(stream, maxCheckStartPosition))
    _db.IsArc = true;
    
    HRESULT result = archive.ReadDatabase(
        EXTERNAL_CODECS_VARS
        _db
        #ifndef Z7_NO_CRYPTO
          , getTextPassword, _isEncrypted, _passwordIsDefined, _password
        #endif
        );
    RINOK(result)
    
    _inStream = stream;
  }
  catch(...)
  {
    Close();
    // return E_INVALIDARG;
    // return S_FALSE;
    // we must return out_of_memory here
    return E_OUTOFMEMORY;
  }
  // _inStream = stream;
  #ifndef Z7_SFX
  FillPopIDs();
  #endif
  return S_OK;
  COM_TRY_END
}

Z7_COM7F_IMF(CHandler::Close())
{
  COM_TRY_BEGIN
  _inStream.Release();
  _db.Clear();
  #ifndef Z7_NO_CRYPTO
  _isEncrypted = false;
  _passwordIsDefined = false;
  _password.Wipe_and_Empty();
  #endif
  return S_OK;
  COM_TRY_END
}

#ifdef Z7_7Z_SET_PROPERTIES
#ifdef Z7_EXTRACT_ONLY

Z7_COM7F_IMF(CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps))
{
  COM_TRY_BEGIN
  
  InitCommon();
  _useMultiThreadMixer = true;

  for (UInt32 i = 0; i < numProps; i++)
  {
    UString name = names[i];
    name.MakeLower_Ascii();
    if (name.IsEmpty())
      return E_INVALIDARG;
    const PROPVARIANT &value = values[i];
    UInt32 number;
    const unsigned index = ParseStringToUInt32(name, number);
    if (index == 0)
    {
      if (name.IsEqualTo("mtf"))
      {
        RINOK(PROPVARIANT_to_bool(value, _useMultiThreadMixer))
        continue;
      }
      {
        HRESULT hres;
        if (SetCommonProperty(name, value, hres))
        {
          RINOK(hres)
          continue;
        }
      }
      return E_INVALIDARG;
    }
  }
  return S_OK;
  COM_TRY_END
}

#endif
#endif

IMPL_ISetCompressCodecsInfo

}}
