// LoadCodecs.cpp

#include "StdAfx.h"

#include "../../../../C/7zVersion.h"

#include "../../../Common/MyCom.h"
#include "../../../Common/StringToInt.h"
#include "../../../Common/StringConvert.h"

#include "../../../Windows/PropVariant.h"

#include "LoadCodecs.h"

using namespace NWindows;

#ifdef NEW_FOLDER_INTERFACE
#include "../../../Common/StringToInt.h"
#endif

#include "../../ICoder.h"
#include "../../Common/RegisterArc.h"

#ifdef EXTERNAL_CODECS

#include "../../../Windows/FileFind.h"
#include "../../../Windows/DLL.h"
#ifdef NEW_FOLDER_INTERFACE
#include "../../../Windows/ResourceString.h"
static const UINT kIconTypesResId = 100;
#endif

#ifdef _WIN32
#include "../../../Windows/FileName.h"
#include "../../../Windows/Registry.h"
#endif

using namespace NFile;

#ifdef _WIN32
extern HINSTANCE g_hInstance;
#endif

#define kCodecsFolderName FTEXT("Codecs")
#define kFormatsFolderName FTEXT("Formats")
static CFSTR kMainDll = FTEXT("7z.dll");

#ifdef _WIN32

static LPCTSTR kRegistryPath = TEXT("Software") TEXT(STRING_PATH_SEPARATOR) TEXT("7-zip");
static LPCWSTR kProgramPathValue = L"Path";
static LPCWSTR kProgramPath2Value = L"Path"
  #ifdef _WIN64
  L"64";
  #else
  L"32";
  #endif

static bool ReadPathFromRegistry(HKEY baseKey, LPCWSTR value, FString &path)
{
  NRegistry::CKey key;
  if (key.Open(baseKey, kRegistryPath, KEY_READ) == ERROR_SUCCESS)
  {
    UString pathU;
    if (key.QueryValue(value, pathU) == ERROR_SUCCESS)
    {
      path = us2fs(pathU);
      NName::NormalizeDirPathPrefix(path);
      return NFind::DoesFileExist(path + kMainDll);
    }
  }
  return false;
}

#endif // _WIN32

#endif // EXTERNAL_CODECS


static const unsigned kNumArcsMax = 48;
static unsigned g_NumArcs = 0;
static const CArcInfo *g_Arcs[kNumArcsMax];

void RegisterArc(const CArcInfo *arcInfo) throw()
{
  if (g_NumArcs < kNumArcsMax)
  {
    g_Arcs[g_NumArcs] = arcInfo;
    g_NumArcs++;
  }
}

static void SplitString(const UString &srcString, UStringVector &destStrings)
{
  destStrings.Clear();
  UString s;
  unsigned len = srcString.Len();
  if (len == 0)
    return;
  for (unsigned i = 0; i < len; i++)
  {
    wchar_t c = srcString[i];
    if (c == L' ')
    {
      if (!s.IsEmpty())
      {
        destStrings.Add(s);
        s.Empty();
      }
    }
    else
      s += c;
  }
  if (!s.IsEmpty())
    destStrings.Add(s);
}

int CArcInfoEx::FindExtension(const UString &ext) const
{
  FOR_VECTOR (i, Exts)
    if (ext.IsEqualToNoCase(Exts[i].Ext))
      return i;
  return -1;
}

void CArcInfoEx::AddExts(const UString &ext, const UString &addExt)
{
  UStringVector exts, addExts;
  SplitString(ext, exts);
  SplitString(addExt, addExts);
  FOR_VECTOR (i, exts)
  {
    CArcExtInfo extInfo;
    extInfo.Ext = exts[i];
    if (i < addExts.Size())
    {
      extInfo.AddExt = addExts[i];
      if (extInfo.AddExt == L"*")
        extInfo.AddExt.Empty();
    }
    Exts.Add(extInfo);
  }
}

#ifndef _SFX

static bool ParseSignatures(const Byte *data, unsigned size, CObjectVector<CByteBuffer> &signatures)
{
  signatures.Clear();
  while (size > 0)
  {
    unsigned len = *data++;
    size--;
    if (len > size)
      return false;
    signatures.AddNew().CopyFrom(data, len);
    data += len;
    size -= len;
  }
  return true;
}

#endif // _SFX

#ifdef EXTERNAL_CODECS

static FString GetBaseFolderPrefixFromRegistry()
{
  FString moduleFolderPrefix = NDLL::GetModuleDirPrefix();
  #ifdef _WIN32
  if (!NFind::DoesFileExist(moduleFolderPrefix + kMainDll) &&
      !NFind::DoesDirExist(moduleFolderPrefix + kCodecsFolderName) &&
      !NFind::DoesDirExist(moduleFolderPrefix + kFormatsFolderName))
  {
    FString path;
    if (ReadPathFromRegistry(HKEY_CURRENT_USER,  kProgramPath2Value, path)) return path;
    if (ReadPathFromRegistry(HKEY_LOCAL_MACHINE, kProgramPath2Value, path)) return path;
    if (ReadPathFromRegistry(HKEY_CURRENT_USER,  kProgramPathValue,  path)) return path;
    if (ReadPathFromRegistry(HKEY_LOCAL_MACHINE, kProgramPathValue,  path)) return path;
  }
  #endif
  return moduleFolderPrefix;
}

static HRESULT GetCoderClass(Func_GetMethodProperty getMethodProperty, UInt32 index,
    PROPID propId, CLSID &clsId, bool &isAssigned)
{
  NCOM::CPropVariant prop;
  isAssigned = false;
  RINOK(getMethodProperty(index, propId, &prop));
  if (prop.vt == VT_BSTR)
  {
    if (::SysStringByteLen(prop.bstrVal) != sizeof(GUID))
      return E_FAIL;
    isAssigned = true;
    clsId = *(const GUID *)prop.bstrVal;
  }
  else if (prop.vt != VT_EMPTY)
    return E_FAIL;
  return S_OK;
}

HRESULT CCodecs::LoadCodecs()
{
  CCodecLib &lib = Libs.Back();
  lib.GetMethodProperty = (Func_GetMethodProperty)lib.Lib.GetProc("GetMethodProperty");
  if (lib.GetMethodProperty)
  {
    UInt32 numMethods = 1;
    Func_GetNumberOfMethods getNumberOfMethodsFunc = (Func_GetNumberOfMethods)lib.Lib.GetProc("GetNumberOfMethods");
    if (getNumberOfMethodsFunc)
    {
      RINOK(getNumberOfMethodsFunc(&numMethods));
    }
    for (UInt32 i = 0; i < numMethods; i++)
    {
      CDllCodecInfo info;
      info.LibIndex = Libs.Size() - 1;
      info.CodecIndex = i;
      RINOK(GetCoderClass(lib.GetMethodProperty, i, NMethodPropID::kEncoder, info.Encoder, info.EncoderIsAssigned));
      RINOK(GetCoderClass(lib.GetMethodProperty, i, NMethodPropID::kDecoder, info.Decoder, info.DecoderIsAssigned));
      Codecs.Add(info);
    }
  }

  Func_GetHashers getHashers = (Func_GetHashers)lib.Lib.GetProc("GetHashers");
  if (getHashers)
  {
    RINOK(getHashers(&lib.Hashers));
    if (lib.Hashers)
    {
      UInt32 numMethods = lib.Hashers->GetNumHashers();
      for (UInt32 i = 0; i < numMethods; i++)
      {
        CDllHasherInfo info;
        info.LibIndex = Libs.Size() - 1;
        info.HasherIndex = i;
        Hashers.Add(info);
      }
    }
  }
  return S_OK;
}

static HRESULT GetProp(
    Func_GetHandlerProperty getProp,
    Func_GetHandlerProperty2 getProp2,
    UInt32 index, PROPID propID, NCOM::CPropVariant &prop)
{
  if (getProp2)
    return getProp2(index, propID, &prop);;
  return getProp(propID, &prop);
}

static HRESULT GetProp_Bool(
    Func_GetHandlerProperty getProp,
    Func_GetHandlerProperty2 getProp2,
    UInt32 index, PROPID propID, bool &res)
{
  res = false;
  NCOM::CPropVariant prop;
  RINOK(GetProp(getProp, getProp2, index, propID, prop));
  if (prop.vt == VT_BOOL)
    res = VARIANT_BOOLToBool(prop.boolVal);
  else if (prop.vt != VT_EMPTY)
    return E_FAIL;
  return S_OK;
}

static HRESULT GetProp_UInt32(
    Func_GetHandlerProperty getProp,
    Func_GetHandlerProperty2 getProp2,
    UInt32 index, PROPID propID, UInt32 &res, bool &defined)
{
  res = 0;
  defined = false;
  NCOM::CPropVariant prop;
  RINOK(GetProp(getProp, getProp2, index, propID, prop));
  if (prop.vt == VT_UI4)
  {
    res = prop.ulVal;
    defined = true;
  }
  else if (prop.vt != VT_EMPTY)
    return E_FAIL;
  return S_OK;
}

static HRESULT GetProp_String(
    Func_GetHandlerProperty getProp,
    Func_GetHandlerProperty2 getProp2,
    UInt32 index, PROPID propID, UString &res)
{
  res.Empty();
  NCOM::CPropVariant prop;
  RINOK(GetProp(getProp, getProp2, index, propID, prop));
  if (prop.vt == VT_BSTR)
    res = prop.bstrVal;
  else if (prop.vt != VT_EMPTY)
    return E_FAIL;
  return S_OK;
}

static HRESULT GetProp_RawData(
    Func_GetHandlerProperty getProp,
    Func_GetHandlerProperty2 getProp2,
    UInt32 index, PROPID propID, CByteBuffer &bb)
{
  bb.Free();
  NCOM::CPropVariant prop;
  RINOK(GetProp(getProp, getProp2, index, propID, prop));
  if (prop.vt == VT_BSTR)
  {
    UINT len = ::SysStringByteLen(prop.bstrVal);
    bb.CopyFrom((const Byte *)prop.bstrVal, len);
  }
  else if (prop.vt != VT_EMPTY)
    return E_FAIL;
  return S_OK;
}

static const UInt32 kArcFlagsPars[] =
{
  NArchive::NHandlerPropID::kKeepName, NArcInfoFlags::kKeepName,
  NArchive::NHandlerPropID::kAltStreams, NArcInfoFlags::kAltStreams,
  NArchive::NHandlerPropID::kNtSecure, NArcInfoFlags::kNtSecure
};

HRESULT CCodecs::LoadFormats()
{
  const NDLL::CLibrary &lib = Libs.Back().Lib;
  
  Func_GetHandlerProperty getProp = NULL;
  Func_GetHandlerProperty2 getProp2 = (Func_GetHandlerProperty2)lib.GetProc("GetHandlerProperty2");
  Func_GetIsArc getIsArc = (Func_GetIsArc)lib.GetProc("GetIsArc");
  
  UInt32 numFormats = 1;

  if (getProp2)
  {
    Func_GetNumberOfFormats getNumberOfFormats = (Func_GetNumberOfFormats)lib.GetProc("GetNumberOfFormats");
    if (getNumberOfFormats)
    {
      RINOK(getNumberOfFormats(&numFormats));
    }
  }
  else
  {
    getProp = (Func_GetHandlerProperty)lib.GetProc("GetHandlerProperty");
    if (!getProp)
      return S_OK;
  }
  
  for (UInt32 i = 0; i < numFormats; i++)
  {
    CArcInfoEx item;
    item.LibIndex = Libs.Size() - 1;
    item.FormatIndex = i;

    RINOK(GetProp_String(getProp, getProp2, i, NArchive::NHandlerPropID::kName, item.Name));

    {
      NCOM::CPropVariant prop;
      if (GetProp(getProp, getProp2, i, NArchive::NHandlerPropID::kClassID, prop) != S_OK)
        continue;
      if (prop.vt != VT_BSTR)
        continue;
      if (::SysStringByteLen(prop.bstrVal) != sizeof(GUID))
        return E_FAIL;
      item.ClassID = *(const GUID *)prop.bstrVal;
      prop.Clear();
    }

    UString ext, addExt;
    RINOK(GetProp_String(getProp, getProp2, i, NArchive::NHandlerPropID::kExtension, ext));
    RINOK(GetProp_String(getProp, getProp2, i, NArchive::NHandlerPropID::kAddExtension, addExt));
    item.AddExts(ext, addExt);

    GetProp_Bool(getProp, getProp2, i, NArchive::NHandlerPropID::kUpdate, item.UpdateEnabled);
    bool flags_Defined = false;
    RINOK(GetProp_UInt32(getProp, getProp2, i, NArchive::NHandlerPropID::kFlags, item.Flags, flags_Defined));
    item.NewInterface = flags_Defined;
    if (!flags_Defined) // && item.UpdateEnabled
    {
      // support for DLL version before 9.31:
      for (unsigned j = 0; j < ARRAY_SIZE(kArcFlagsPars); j += 2)
      {
        bool val = false;
        GetProp_Bool(getProp, getProp2, i, kArcFlagsPars[j], val);
        if (val)
          item.Flags |= kArcFlagsPars[j + 1];
      }
    }
    
    CByteBuffer sig;
    RINOK(GetProp_RawData(getProp, getProp2, i, NArchive::NHandlerPropID::kSignature, sig));
    if (sig.Size() != 0)
      item.Signatures.Add(sig);
    else
    {
      RINOK(GetProp_RawData(getProp, getProp2, i, NArchive::NHandlerPropID::kMultiSignature, sig));
      ParseSignatures(sig, (unsigned)sig.Size(), item.Signatures);
    }

    bool signatureOffset_Defined;
    RINOK(GetProp_UInt32(getProp, getProp2, i, NArchive::NHandlerPropID::kSignatureOffset, item.SignatureOffset, signatureOffset_Defined));
    
    // bool version_Defined;
    // RINOK(GetProp_UInt32(getProp, getProp2, i, NArchive::NHandlerPropID::kVersion, item.Version, version_Defined));

    if (getIsArc)
      getIsArc(i, &item.IsArcFunc);

    Formats.Add(item);
  }
  return S_OK;
}

#ifdef NEW_FOLDER_INTERFACE
void CCodecIcons::LoadIcons(HMODULE m)
{
  UString iconTypes;
  MyLoadString(m, kIconTypesResId, iconTypes);
  UStringVector pairs;
  SplitString(iconTypes, pairs);
  FOR_VECTOR (i, pairs)
  {
    const UString &s = pairs[i];
    int pos = s.Find(L':');
    CIconPair iconPair;
    iconPair.IconIndex = -1;
    if (pos < 0)
      pos = s.Len();
    else
    {
      UString num = s.Ptr(pos + 1);
      if (!num.IsEmpty())
      {
        const wchar_t *end;
        iconPair.IconIndex = ConvertStringToUInt32(num, &end);
        if (*end != 0)
          continue;
      }
    }
    iconPair.Ext = s.Left(pos);
    IconPairs.Add(iconPair);
  }
}

bool CCodecIcons::FindIconIndex(const UString &ext, int &iconIndex) const
{
  iconIndex = -1;
  FOR_VECTOR (i, IconPairs)
  {
    const CIconPair &pair = IconPairs[i];
    if (ext.IsEqualToNoCase(pair.Ext))
    {
      iconIndex = pair.IconIndex;
      return true;
    }
  }
  return false;
}

#endif // EXTERNAL_CODECS

#ifdef _7ZIP_LARGE_PAGES
extern "C"
{
  extern SIZE_T g_LargePageSize;
}
#endif

HRESULT CCodecs::LoadDll(const FString &dllPath, bool needCheckDll)
{
  if (needCheckDll)
  {
    NDLL::CLibrary library;
    if (!library.LoadEx(dllPath, LOAD_LIBRARY_AS_DATAFILE))
      return S_OK;
  }
  Libs.Add(CCodecLib());
  CCodecLib &lib = Libs.Back();
  lib.Path = dllPath;
  bool used = false;
  HRESULT res = S_OK;
  if (lib.Lib.Load(dllPath))
  {
    #ifdef NEW_FOLDER_INTERFACE
    lib.LoadIcons();
    #endif

    #ifdef _7ZIP_LARGE_PAGES
    if (g_LargePageSize != 0)
    {
      Func_SetLargePageMode setLargePageMode = (Func_SetLargePageMode)lib.Lib.GetProc("SetLargePageMode");
      if (setLargePageMode)
        setLargePageMode();
    }
    #endif

    if (CaseSensitiveChange)
    {
      Func_SetCaseSensitive setCaseSensitive = (Func_SetCaseSensitive)lib.Lib.GetProc("SetCaseSensitive");
      if (setCaseSensitive)
        setCaseSensitive(CaseSensitive ? 1 : 0);
    }

    lib.CreateObject = (Func_CreateObject)lib.Lib.GetProc("CreateObject");
    if (lib.CreateObject)
    {
      unsigned startSize = Codecs.Size() + Hashers.Size();
      res = LoadCodecs();
      used = (startSize != Codecs.Size() + Hashers.Size());
      if (res == S_OK)
      {
        startSize = Formats.Size();
        res = LoadFormats();
        if (startSize != Formats.Size())
          used = true;
      }
    }
  }
  if (!used)
    Libs.DeleteBack();
  return res;
}

HRESULT CCodecs::LoadDllsFromFolder(const FString &folderPrefix)
{
  NFile::NFind::CEnumerator enumerator(folderPrefix + FCHAR_ANY_MASK);
  NFile::NFind::CFileInfo fi;
  while (enumerator.Next(fi))
  {
    if (fi.IsDir())
      continue;
    RINOK(LoadDll(folderPrefix + fi.Name, true));
  }
  return S_OK;
}

#endif

HRESULT CCodecs::Load()
{
  #ifdef NEW_FOLDER_INTERFACE
    InternalIcons.LoadIcons(g_hInstance);
  #endif

  Formats.Clear();
  
  #ifdef EXTERNAL_CODECS
    Codecs.Clear();
    Hashers.Clear();
  #endif
  
  for (UInt32 i = 0; i < g_NumArcs; i++)
  {
    const CArcInfo &arc = *g_Arcs[i];
    CArcInfoEx item;
    
    item.Name.SetFromAscii(arc.Name);
    item.CreateInArchive = arc.CreateInArchive;
    item.IsArcFunc = arc.IsArc;
    item.Flags = arc.Flags;
  
    {
      UString e, ae;
      if (arc.Ext)
        e.SetFromAscii(arc.Ext);
      if (arc.AddExt)
        ae.SetFromAscii(arc.AddExt);
      item.AddExts(e, ae);
    }

    #ifndef _SFX

    item.CreateOutArchive = arc.CreateOutArchive;
    item.UpdateEnabled = (arc.CreateOutArchive != NULL);
    item.SignatureOffset = arc.SignatureOffset;
    // item.Version = MY_VER_MIX;
    item.NewInterface = true;
    
    if (arc.IsMultiSignature())
      ParseSignatures(arc.Signature, arc.SignatureSize, item.Signatures);
    else
      item.Signatures.AddNew().CopyFrom(arc.Signature, arc.SignatureSize);
    
    #endif

    Formats.Add(item);
  }
  
  #ifdef EXTERNAL_CODECS
    const FString baseFolder = GetBaseFolderPrefixFromRegistry();
    RINOK(LoadDll(baseFolder + kMainDll, false));
    RINOK(LoadDllsFromFolder(baseFolder + kCodecsFolderName FSTRING_PATH_SEPARATOR));
    RINOK(LoadDllsFromFolder(baseFolder + kFormatsFolderName FSTRING_PATH_SEPARATOR));
  #endif
  
  return S_OK;
}

#ifndef _SFX

int CCodecs::FindFormatForArchiveName(const UString &arcPath) const
{
  int slashPos = arcPath.ReverseFind(WCHAR_PATH_SEPARATOR);
  int dotPos = arcPath.ReverseFind(L'.');
  if (dotPos < 0 || dotPos < slashPos)
    return -1;
  const UString ext = arcPath.Ptr(dotPos + 1);
  if (ext.IsEmpty())
    return -1;
  if (ext.IsEqualToNoCase(L"exe"))
    return -1;
  FOR_VECTOR (i, Formats)
  {
    const CArcInfoEx &arc = Formats[i];
    /*
    if (!arc.UpdateEnabled)
      continue;
    */
    if (arc.FindExtension(ext) >= 0)
      return i;
  }
  return -1;
}

int CCodecs::FindFormatForExtension(const UString &ext) const
{
  if (ext.IsEmpty())
    return -1;
  FOR_VECTOR (i, Formats)
    if (Formats[i].FindExtension(ext) >= 0)
      return i;
  return -1;
}

int CCodecs::FindFormatForArchiveType(const UString &arcType) const
{
  FOR_VECTOR (i, Formats)
    if (Formats[i].Name.IsEqualToNoCase(arcType))
      return i;
  return -1;
}

bool CCodecs::FindFormatForArchiveType(const UString &arcType, CIntVector &formatIndices) const
{
  formatIndices.Clear();
  for (unsigned pos = 0; pos < arcType.Len();)
  {
    int pos2 = arcType.Find('.', pos);
    if (pos2 < 0)
      pos2 = arcType.Len();
    const UString name = arcType.Mid(pos, pos2 - pos);
    if (name.IsEmpty())
      return false;
    int index = FindFormatForArchiveType(name);
    if (index < 0 && name != L"*")
    {
      formatIndices.Clear();
      return false;
    }
    formatIndices.Add(index);
    pos = pos2 + 1;
  }
  return true;
}

#endif // _SFX


#ifdef EXTERNAL_CODECS

// #define EXPORT_CODECS

#ifdef EXPORT_CODECS

extern unsigned g_NumCodecs;
STDAPI CreateCoder2(bool encode, UInt32 index, const GUID *iid, void **outObject);
STDAPI GetMethodProperty(UInt32 codecIndex, PROPID propID, PROPVARIANT *value);
#define NUM_EXPORT_CODECS g_NumCodecs

extern unsigned g_NumHashers;
STDAPI CreateHasher(UInt32 index, IHasher **hasher);
STDAPI GetHasherProp(UInt32 codecIndex, PROPID propID, PROPVARIANT *value);
#define NUM_EXPORT_HASHERS g_NumHashers

#else // EXPORT_CODECS

#define NUM_EXPORT_CODECS 0
#define NUM_EXPORT_HASHERS 0

#endif // EXPORT_CODECS

STDMETHODIMP CCodecs::GetNumberOfMethods(UInt32 *numMethods)
{
  *numMethods = NUM_EXPORT_CODECS
    #ifdef EXTERNAL_CODECS
    + Codecs.Size()
    #endif
    ;
  return S_OK;
}

STDMETHODIMP CCodecs::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
{
  #ifdef EXPORT_CODECS
  if (index < g_NumCodecs)
    return GetMethodProperty(index, propID, value);
  #endif

  #ifdef EXTERNAL_CODECS
  const CDllCodecInfo &ci = Codecs[index - NUM_EXPORT_CODECS];

  if (propID == NMethodPropID::kDecoderIsAssigned ||
      propID == NMethodPropID::kEncoderIsAssigned)
  {
    NCOM::CPropVariant prop;
    prop = (propID == NMethodPropID::kDecoderIsAssigned) ?
        ci.DecoderIsAssigned :
        ci.EncoderIsAssigned;
    prop.Detach(value);
    return S_OK;
  }
  return Libs[ci.LibIndex].GetMethodProperty(ci.CodecIndex, propID, value);
  #else
  return E_FAIL;
  #endif
}

STDMETHODIMP CCodecs::CreateDecoder(UInt32 index, const GUID *iid, void **coder)
{
  #ifdef EXPORT_CODECS
  if (index < g_NumCodecs)
    return CreateCoder2(false, index, iid, coder);
  #endif
  #ifdef EXTERNAL_CODECS
  const CDllCodecInfo &ci = Codecs[index - NUM_EXPORT_CODECS];
  if (ci.DecoderIsAssigned)
    return Libs[ci.LibIndex].CreateObject(&ci.Decoder, iid, (void **)coder);
  return S_OK;
  #else
  return E_FAIL;
  #endif
}

STDMETHODIMP CCodecs::CreateEncoder(UInt32 index, const GUID *iid, void **coder)
{
  #ifdef EXPORT_CODECS
  if (index < g_NumCodecs)
    return CreateCoder2(true, index, iid, coder);
  #endif
  #ifdef EXTERNAL_CODECS
  const CDllCodecInfo &ci = Codecs[index - NUM_EXPORT_CODECS];
  if (ci.EncoderIsAssigned)
    return Libs[ci.LibIndex].CreateObject(&ci.Encoder, iid, (void **)coder);
  return S_OK;
  #else
  return E_FAIL;
  #endif
}


STDMETHODIMP_(UInt32) CCodecs::GetNumHashers()
{
  return NUM_EXPORT_HASHERS
    #ifdef EXTERNAL_CODECS
    + Hashers.Size()
    #endif
    ;
}

STDMETHODIMP CCodecs::GetHasherProp(UInt32 index, PROPID propID, PROPVARIANT *value)
{
  #ifdef EXPORT_CODECS
  if (index < g_NumHashers)
    return ::GetHasherProp(index, propID, value);
  #endif

  #ifdef EXTERNAL_CODECS
  const CDllHasherInfo &ci = Hashers[index - NUM_EXPORT_HASHERS];
  return Libs[ci.LibIndex].Hashers->GetHasherProp(ci.HasherIndex, propID, value);
  #else
  return E_FAIL;
  #endif
}

STDMETHODIMP CCodecs::CreateHasher(UInt32 index, IHasher **hasher)
{
  #ifdef EXPORT_CODECS
  if (index < g_NumHashers)
    return CreateHasher(index, hasher);
  #endif
  #ifdef EXTERNAL_CODECS
  const CDllHasherInfo &ci = Hashers[index - NUM_EXPORT_HASHERS];
  return Libs[ci.LibIndex].Hashers->CreateHasher(ci.HasherIndex, hasher);
  #else
  return E_FAIL;
  #endif
}

int CCodecs::GetCodecLibIndex(UInt32 index)
{
  #ifdef EXPORT_CODECS
  if (index < g_NumCodecs)
    return -1;
  #endif
  #ifdef EXTERNAL_CODECS
  const CDllCodecInfo &ci = Codecs[index - NUM_EXPORT_CODECS];
  return ci.LibIndex;
  #else
  return -1;
  #endif
}

int CCodecs::GetHasherLibIndex(UInt32 index)
{
  #ifdef EXPORT_CODECS
  if (index < g_NumHashers)
    return -1;
  #endif
  #ifdef EXTERNAL_CODECS
  const CDllHasherInfo &ci = Hashers[index - NUM_EXPORT_HASHERS];
  return ci.LibIndex;
  #else
  return -1;
  #endif
}

bool CCodecs::GetCodecEncoderIsAssigned(UInt32 index)
{
  #ifdef EXPORT_CODECS
  if (index < g_NumCodecs)
  {
    NCOM::CPropVariant prop;
    if (GetProperty(index, NMethodPropID::kEncoder, &prop) == S_OK)
      if (prop.vt != VT_EMPTY)
        return true;
    return false;
  }
  #endif
  #ifdef EXTERNAL_CODECS
  const CDllCodecInfo &ci = Codecs[index - NUM_EXPORT_CODECS];
  return ci.EncoderIsAssigned;
  #else
  return false;
  #endif
}

HRESULT CCodecs::GetCodecId(UInt32 index, UInt64 &id)
{
  NCOM::CPropVariant prop;
  RINOK(GetProperty(index, NMethodPropID::kID, &prop));
  if (prop.vt != VT_UI8)
    return E_INVALIDARG;
  id = prop.uhVal.QuadPart;
  return S_OK;
}

UString CCodecs::GetCodecName(UInt32 index)
{
  UString s;
  NCOM::CPropVariant prop;
  if (GetProperty(index, NMethodPropID::kName, &prop) == S_OK)
    if (prop.vt == VT_BSTR)
      s = prop.bstrVal;
  return s;
}

UInt64 CCodecs::GetHasherId(UInt32 index)
{
  NCOM::CPropVariant prop;
  RINOK(GetHasherProp(index, NMethodPropID::kID, &prop));
  if (prop.vt != VT_UI8)
    return 0;
  return prop.uhVal.QuadPart;
}

UString CCodecs::GetHasherName(UInt32 index)
{
  UString s;
  NCOM::CPropVariant prop;
  if (GetHasherProp(index, NMethodPropID::kName, &prop) == S_OK)
    if (prop.vt == VT_BSTR)
      s = prop.bstrVal;
  return s;
}

UInt32 CCodecs::GetHasherDigestSize(UInt32 index)
{
  NCOM::CPropVariant prop;
  RINOK(GetHasherProp(index, NMethodPropID::kDigestSize, &prop));
  if (prop.vt != VT_UI4)
    return 0;
  return prop.ulVal;
}

#endif // EXTERNAL_CODECS