1// LoadCodecs.cpp
2
3/*
4Z7_EXTERNAL_CODECS
5---------------
6  CCodecs::Load() tries to detect the directory with plugins.
7  It stops the checking, if it can find any of the following items:
8    - 7z.dll file
9    - "Formats" subdir
10    - "Codecs"  subdir
11  The order of check:
12    1) directory of client executable
13    2) WIN32: directory for REGISTRY item [HKEY_*\Software\7-Zip\Path**]
14       The order for HKEY_* : Path** :
15         - HKEY_CURRENT_USER  : PathXX
16         - HKEY_LOCAL_MACHINE : PathXX
17         - HKEY_CURRENT_USER  : Path
18         - HKEY_LOCAL_MACHINE : Path
19       PathXX is Path32 in 32-bit code
20       PathXX is Path64 in 64-bit code
21
22
23EXPORT_CODECS
24-------------
25  if (Z7_EXTERNAL_CODECS) is defined, then the code exports internal
26  codecs of client from CCodecs object to external plugins.
27  7-Zip doesn't use that feature. 7-Zip uses the scheme:
28    - client application without internal plugins.
29    - 7z.dll module contains all (or almost all) plugins.
30      7z.dll can use codecs from another plugins, if required.
31*/
32
33
34#include "StdAfx.h"
35
36#include "../../../Common/MyCom.h"
37#include "../../../Common/StringToInt.h"
38#include "../../../Common/StringConvert.h"
39
40#include "../../../Windows/ErrorMsg.h"
41#include "../../../Windows/FileIO.h"
42#include "../../../Windows/PropVariant.h"
43
44#include "LoadCodecs.h"
45
46#include "../../ICoder.h"
47#include "../../Common/RegisterArc.h"
48#include "../../Common/RegisterCodec.h"
49
50#ifdef Z7_EXTERNAL_CODECS
51// #define EXPORT_CODECS
52#endif
53
54#ifdef Z7_EXTERNAL_CODECS
55
56#include "../../../Windows/FileFind.h"
57#include "../../../Windows/DLL.h"
58
59#ifdef _WIN32
60#include "../../../Windows/FileName.h"
61#include "../../../Windows/Registry.h"
62#endif
63
64using namespace NWindows;
65using namespace NFile;
66
67
68#define kCodecsFolderName FTEXT("Codecs")
69#define kFormatsFolderName FTEXT("Formats")
70
71
72static CFSTR const kMainDll =
73  #ifdef _WIN32
74    FTEXT("7z.dll");
75  #else
76    FTEXT("7z.so");
77  #endif
78
79
80#ifdef _WIN32
81
82static LPCTSTR const kRegistryPath = TEXT("Software") TEXT(STRING_PATH_SEPARATOR) TEXT("7-zip");
83static LPCWSTR const kProgramPathValue = L"Path";
84static LPCWSTR const kProgramPath2Value = L"Path"
85  #ifdef _WIN64
86  L"64";
87  #else
88  L"32";
89  #endif
90
91static bool ReadPathFromRegistry(HKEY baseKey, LPCWSTR value, FString &path)
92{
93  NRegistry::CKey key;
94  if (key.Open(baseKey, kRegistryPath, KEY_READ) == ERROR_SUCCESS)
95  {
96    UString pathU;
97    if (key.QueryValue(value, pathU) == ERROR_SUCCESS)
98    {
99      path = us2fs(pathU);
100      NName::NormalizeDirPathPrefix(path);
101      return NFind::DoesFileExist_Raw(path + kMainDll);
102    }
103  }
104  return false;
105}
106
107#endif // _WIN32
108
109#endif // Z7_EXTERNAL_CODECS
110
111
112static const unsigned kNumArcsMax = 72;
113static unsigned g_NumArcs = 0;
114static const CArcInfo *g_Arcs[kNumArcsMax];
115
116void RegisterArc(const CArcInfo *arcInfo) throw()
117{
118  if (g_NumArcs < kNumArcsMax)
119  {
120    g_Arcs[g_NumArcs] = arcInfo;
121    g_NumArcs++;
122  }
123  // else throw 1;
124}
125
126/*
127static void SplitString(const UString &srcString, UStringVector &destStrings)
128{
129  destStrings.Clear();
130  UString s;
131  unsigned len = srcString.Len();
132  if (len == 0)
133    return;
134  for (unsigned i = 0; i < len; i++)
135  {
136    wchar_t c = srcString[i];
137    if (c == L' ')
138    {
139      if (!s.IsEmpty())
140      {
141        destStrings.Add(s);
142        s.Empty();
143      }
144    }
145    else
146      s += c;
147  }
148  if (!s.IsEmpty())
149    destStrings.Add(s);
150}
151*/
152
153int CArcInfoEx::FindExtension(const UString &ext) const
154{
155  FOR_VECTOR (i, Exts)
156    if (ext.IsEqualTo_NoCase(Exts[i].Ext))
157      return (int)i;
158  return -1;
159}
160
161void CArcInfoEx::AddExts(const UString &ext, const UString &addExt)
162{
163  UStringVector exts, addExts;
164  SplitString(ext, exts);
165  SplitString(addExt, addExts);
166  FOR_VECTOR (i, exts)
167  {
168    CArcExtInfo extInfo;
169    extInfo.Ext = exts[i];
170    if (i < addExts.Size())
171    {
172      extInfo.AddExt = addExts[i];
173      if (extInfo.AddExt == L"*")
174        extInfo.AddExt.Empty();
175    }
176    Exts.Add(extInfo);
177  }
178}
179
180#ifndef Z7_SFX
181
182static bool ParseSignatures(const Byte *data, unsigned size, CObjectVector<CByteBuffer> &signatures)
183{
184  signatures.Clear();
185  while (size != 0)
186  {
187    const unsigned len = *data++;
188    size--;
189    if (len > size)
190      return false;
191    signatures.AddNew().CopyFrom(data, len);
192    data += len;
193    size -= len;
194  }
195  return true;
196}
197
198#endif // Z7_SFX
199
200// #include <stdio.h>
201
202#ifdef Z7_EXTERNAL_CODECS
203
204static FString GetBaseFolderPrefixFromRegistry()
205{
206  FString moduleFolderPrefix = NDLL::GetModuleDirPrefix();
207
208  #ifdef _WIN32
209  if (   !NFind::DoesFileOrDirExist(moduleFolderPrefix + kMainDll)
210      && !NFind::DoesFileOrDirExist(moduleFolderPrefix + kCodecsFolderName)
211      && !NFind::DoesFileOrDirExist(moduleFolderPrefix + kFormatsFolderName))
212  {
213    FString path;
214    if (ReadPathFromRegistry(HKEY_CURRENT_USER,  kProgramPath2Value, path)) return path;
215    if (ReadPathFromRegistry(HKEY_LOCAL_MACHINE, kProgramPath2Value, path)) return path;
216    if (ReadPathFromRegistry(HKEY_CURRENT_USER,  kProgramPathValue,  path)) return path;
217    if (ReadPathFromRegistry(HKEY_LOCAL_MACHINE, kProgramPathValue,  path)) return path;
218  }
219  #endif
220
221  // printf("\nmoduleFolderPrefix = %s\n", (const char *)GetAnsiString(moduleFolderPrefix));
222  return moduleFolderPrefix;
223}
224
225
226static HRESULT GetCoderClass(Func_GetMethodProperty getMethodProperty, UInt32 index,
227    PROPID propId, CLSID &clsId, bool &isAssigned)
228{
229  NCOM::CPropVariant prop;
230  isAssigned = false;
231  RINOK(getMethodProperty(index, propId, &prop))
232  if (prop.vt == VT_BSTR)
233  {
234    if (::SysStringByteLen(prop.bstrVal) != sizeof(GUID))
235      return E_FAIL;
236    isAssigned = true;
237    clsId = *(const GUID *)(const void *)prop.bstrVal;
238  }
239  else if (prop.vt != VT_EMPTY)
240    return E_FAIL;
241  return S_OK;
242}
243
244
245static HRESULT GetMethodBoolProp(Func_GetMethodProperty getMethodProperty, UInt32 index,
246    PROPID propId, bool &resVal, bool &isAssigned)
247{
248  NCOM::CPropVariant prop;
249  resVal = false;
250  isAssigned = false;
251  RINOK(getMethodProperty(index, propId, &prop))
252  if (prop.vt == VT_BOOL)
253  {
254    isAssigned = true;
255    resVal = VARIANT_BOOLToBool(prop.boolVal);
256  }
257  else if (prop.vt != VT_EMPTY)
258    return E_FAIL;
259  return S_OK;
260}
261
262#if defined(__clang__)
263#pragma GCC diagnostic ignored "-Wc++98-compat-pedantic"
264#endif
265
266#define MY_GET_FUNC(dest, type, lib, func)  \
267  dest = Z7_GET_PROC_ADDRESS(type, lib.Get_HMODULE(), func);
268// #define MY_GET_FUNC(dest, type, func)  dest = (type)(func);
269
270#define MY_GET_FUNC_LOC(dest, type, lib, func) \
271  type dest;  MY_GET_FUNC(dest, type, lib, func)
272
273HRESULT CCodecs::LoadCodecs()
274{
275  CCodecLib &lib = Libs.Back();
276
277  MY_GET_FUNC (lib.CreateDecoder,     Func_CreateDecoder,     lib.Lib, "CreateDecoder")
278  MY_GET_FUNC (lib.CreateEncoder,     Func_CreateEncoder,     lib.Lib, "CreateEncoder")
279  MY_GET_FUNC (lib.GetMethodProperty, Func_GetMethodProperty, lib.Lib, "GetMethodProperty")
280
281  if (lib.GetMethodProperty)
282  {
283    UInt32 numMethods = 1;
284    MY_GET_FUNC_LOC (getNumberOfMethods, Func_GetNumberOfMethods, lib.Lib, "GetNumberOfMethods")
285    if (getNumberOfMethods)
286    {
287      RINOK(getNumberOfMethods(&numMethods))
288    }
289    for (UInt32 i = 0; i < numMethods; i++)
290    {
291      CDllCodecInfo info;
292      info.LibIndex = Libs.Size() - 1;
293      info.CodecIndex = i;
294      RINOK(GetCoderClass(lib.GetMethodProperty, i, NMethodPropID::kEncoder, info.Encoder, info.EncoderIsAssigned))
295      RINOK(GetCoderClass(lib.GetMethodProperty, i, NMethodPropID::kDecoder, info.Decoder, info.DecoderIsAssigned))
296      RINOK(GetMethodBoolProp(lib.GetMethodProperty, i, NMethodPropID::kIsFilter, info.IsFilter, info.IsFilter_Assigned))
297      Codecs.Add(info);
298    }
299  }
300
301  MY_GET_FUNC_LOC (getHashers, Func_GetHashers, lib.Lib, "GetHashers")
302  if (getHashers)
303  {
304    RINOK(getHashers(&lib.ComHashers))
305    if (lib.ComHashers)
306    {
307      UInt32 numMethods = lib.ComHashers->GetNumHashers();
308      for (UInt32 i = 0; i < numMethods; i++)
309      {
310        CDllHasherInfo info;
311        info.LibIndex = Libs.Size() - 1;
312        info.HasherIndex = i;
313        Hashers.Add(info);
314      }
315    }
316  }
317
318  return S_OK;
319}
320
321static HRESULT GetProp(
322    Func_GetHandlerProperty getProp,
323    Func_GetHandlerProperty2 getProp2,
324    UInt32 index, PROPID propID, NCOM::CPropVariant &prop)
325{
326  if (getProp2)
327    return getProp2(index, propID, &prop);
328  return getProp(propID, &prop);
329}
330
331static HRESULT GetProp_Bool(
332    Func_GetHandlerProperty getProp,
333    Func_GetHandlerProperty2 getProp2,
334    UInt32 index, PROPID propID, bool &res)
335{
336  res = false;
337  NCOM::CPropVariant prop;
338  RINOK(GetProp(getProp, getProp2, index, propID, prop))
339  if (prop.vt == VT_BOOL)
340    res = VARIANT_BOOLToBool(prop.boolVal);
341  else if (prop.vt != VT_EMPTY)
342    return E_FAIL;
343  return S_OK;
344}
345
346static HRESULT GetProp_UInt32(
347    Func_GetHandlerProperty getProp,
348    Func_GetHandlerProperty2 getProp2,
349    UInt32 index, PROPID propID, UInt32 &res, bool &defined)
350{
351  res = 0;
352  defined = false;
353  NCOM::CPropVariant prop;
354  RINOK(GetProp(getProp, getProp2, index, propID, prop))
355  if (prop.vt == VT_UI4)
356  {
357    res = prop.ulVal;
358    defined = true;
359  }
360  else if (prop.vt != VT_EMPTY)
361    return E_FAIL;
362  return S_OK;
363}
364
365static HRESULT GetProp_String(
366    Func_GetHandlerProperty getProp,
367    Func_GetHandlerProperty2 getProp2,
368    UInt32 index, PROPID propID, UString &res)
369{
370  res.Empty();
371  NCOM::CPropVariant prop;
372  RINOK(GetProp(getProp, getProp2, index, propID, prop))
373  if (prop.vt == VT_BSTR)
374    res.SetFromBstr(prop.bstrVal);
375  else if (prop.vt != VT_EMPTY)
376    return E_FAIL;
377  return S_OK;
378}
379
380static HRESULT GetProp_RawData(
381    Func_GetHandlerProperty getProp,
382    Func_GetHandlerProperty2 getProp2,
383    UInt32 index, PROPID propID, CByteBuffer &bb)
384{
385  bb.Free();
386  NCOM::CPropVariant prop;
387  RINOK(GetProp(getProp, getProp2, index, propID, prop))
388  if (prop.vt == VT_BSTR)
389  {
390    UINT len = ::SysStringByteLen(prop.bstrVal);
391    bb.CopyFrom((const Byte *)prop.bstrVal, len);
392  }
393  else if (prop.vt != VT_EMPTY)
394    return E_FAIL;
395  return S_OK;
396}
397
398static const UInt32 kArcFlagsPars[] =
399{
400  NArchive::NHandlerPropID::kKeepName, NArcInfoFlags::kKeepName,
401  NArchive::NHandlerPropID::kAltStreams, NArcInfoFlags::kAltStreams,
402  NArchive::NHandlerPropID::kNtSecure, NArcInfoFlags::kNtSecure
403};
404
405HRESULT CCodecs::LoadFormats()
406{
407  const NDLL::CLibrary &lib = Libs.Back().Lib;
408
409  Func_GetHandlerProperty getProp = NULL;
410  MY_GET_FUNC_LOC (getProp2, Func_GetHandlerProperty2, lib, "GetHandlerProperty2")
411  MY_GET_FUNC_LOC (getIsArc, Func_GetIsArc, lib, "GetIsArc")
412
413  UInt32 numFormats = 1;
414
415  if (getProp2)
416  {
417    MY_GET_FUNC_LOC (getNumberOfFormats, Func_GetNumberOfFormats, lib, "GetNumberOfFormats")
418    if (getNumberOfFormats)
419    {
420      RINOK(getNumberOfFormats(&numFormats))
421    }
422  }
423  else
424  {
425    MY_GET_FUNC (getProp, Func_GetHandlerProperty, lib, "GetHandlerProperty")
426    if (!getProp)
427      return S_OK;
428  }
429
430  for (UInt32 i = 0; i < numFormats; i++)
431  {
432    CArcInfoEx item;
433    item.LibIndex = (int)(Libs.Size() - 1);
434    item.FormatIndex = i;
435
436    RINOK(GetProp_String(getProp, getProp2, i, NArchive::NHandlerPropID::kName, item.Name))
437
438    {
439      NCOM::CPropVariant prop;
440      if (GetProp(getProp, getProp2, i, NArchive::NHandlerPropID::kClassID, prop) != S_OK)
441        continue;
442      if (prop.vt != VT_BSTR)
443        continue;
444      if (::SysStringByteLen(prop.bstrVal) != sizeof(GUID))
445        return E_FAIL;
446      item.ClassID = *(const GUID *)(const void *)prop.bstrVal;
447      prop.Clear();
448    }
449
450    UString ext, addExt;
451    RINOK(GetProp_String(getProp, getProp2, i, NArchive::NHandlerPropID::kExtension, ext))
452    RINOK(GetProp_String(getProp, getProp2, i, NArchive::NHandlerPropID::kAddExtension, addExt))
453    item.AddExts(ext, addExt);
454
455    GetProp_Bool(getProp, getProp2, i, NArchive::NHandlerPropID::kUpdate, item.UpdateEnabled);
456    bool flags_Defined = false;
457    RINOK(GetProp_UInt32(getProp, getProp2, i, NArchive::NHandlerPropID::kFlags, item.Flags, flags_Defined))
458    item.NewInterface = flags_Defined;
459    if (!flags_Defined) // && item.UpdateEnabled
460    {
461      // support for DLL version before 9.31:
462      for (unsigned j = 0; j < Z7_ARRAY_SIZE(kArcFlagsPars); j += 2)
463      {
464        bool val = false;
465        GetProp_Bool(getProp, getProp2, i, kArcFlagsPars[j], val);
466        if (val)
467          item.Flags |= kArcFlagsPars[j + 1];
468      }
469    }
470
471    {
472      bool defined = false;
473      RINOK(GetProp_UInt32(getProp, getProp2, i, NArchive::NHandlerPropID::kTimeFlags, item.TimeFlags, defined))
474    }
475
476    CByteBuffer sig;
477    RINOK(GetProp_RawData(getProp, getProp2, i, NArchive::NHandlerPropID::kSignature, sig))
478    if (sig.Size() != 0)
479      item.Signatures.Add(sig);
480    else
481    {
482      RINOK(GetProp_RawData(getProp, getProp2, i, NArchive::NHandlerPropID::kMultiSignature, sig))
483      ParseSignatures(sig, (unsigned)sig.Size(), item.Signatures);
484    }
485
486    bool signatureOffset_Defined;
487    RINOK(GetProp_UInt32(getProp, getProp2, i, NArchive::NHandlerPropID::kSignatureOffset, item.SignatureOffset, signatureOffset_Defined))
488
489    // bool version_Defined;
490    // RINOK(GetProp_UInt32(getProp, getProp2, i, NArchive::NHandlerPropID::kVersion, item.Version, version_Defined));
491
492    if (getIsArc)
493      getIsArc(i, &item.IsArcFunc);
494
495    Formats.Add(item);
496  }
497  return S_OK;
498}
499
500#ifdef Z7_LARGE_PAGES
501extern "C"
502{
503  extern SIZE_T g_LargePageSize;
504}
505#endif
506
507
508void CCodecs::AddLastError(const FString &path)
509{
510  const HRESULT res = GetLastError_noZero_HRESULT();
511  CCodecError &error = Errors.AddNew();
512  error.Path = path;
513  error.ErrorCode = res;
514}
515
516
517static bool IsSupportedDll(CCodecLib &lib)
518{
519  MY_GET_FUNC_LOC (
520     f_GetModuleProp,
521  Func_GetModuleProp, lib.Lib,
522      "GetModuleProp")
523  /* p7zip and 7-Zip before v23 used virtual destructor in IUnknown,
524     if _WIN32 is not defined */
525  UInt32 flags =
526    #ifdef _WIN32
527      NModuleInterfaceType::k_IUnknown_VirtDestructor_No;
528    #else
529      NModuleInterfaceType::k_IUnknown_VirtDestructor_Yes;
530    #endif
531  if (f_GetModuleProp)
532  {
533    {
534      NCOM::CPropVariant prop;
535      if (f_GetModuleProp(NModulePropID::kInterfaceType, &prop) == S_OK)
536      {
537        if (prop.vt == VT_UI4)
538          flags = prop.ulVal;
539        else if (prop.vt != VT_EMPTY)
540          return false;
541      }
542    }
543    {
544      NCOM::CPropVariant prop;
545      if (f_GetModuleProp(NModulePropID::kVersion, &prop) == S_OK)
546      {
547        if (prop.vt == VT_UI4)
548          lib.Version = prop.ulVal;
549      }
550    }
551  }
552  if (
553      flags
554      // (flags & NModuleFlags::kMask)
555      != NModuleInterfaceType::k_IUnknown_VirtDestructor_ThisModule)
556    return false;
557  return true;
558}
559
560
561HRESULT CCodecs::LoadDll(const FString &dllPath, bool needCheckDll, bool *loadedOK)
562{
563  if (loadedOK)
564    *loadedOK = false;
565
566  // needCheckDll = 1;
567
568  #ifdef _WIN32
569  if (needCheckDll)
570  {
571    NDLL::CLibrary lib;
572    if (!lib.LoadEx(dllPath, LOAD_LIBRARY_AS_DATAFILE))
573    {
574      /* if is not win32
575      //  %1 is not a valid Win32 application.
576      //  #define ERROR_BAD_EXE_FORMAT             193L
577      */
578      // return GetLastError_noZero_HRESULT();
579      const DWORD lastError = GetLastError();
580      if (lastError != ERROR_BAD_EXE_FORMAT)
581      {
582        CCodecError &error = Errors.AddNew();
583        error.Path = dllPath;
584        error.Message = "cannot load file as datafile library";
585        error.ErrorCode = HRESULT_FROM_WIN32(lastError);
586      }
587      return S_OK;
588    }
589  }
590  #else
591  UNUSED_VAR(needCheckDll)
592  #endif
593
594  Libs.AddNew();
595  CCodecLib &lib = Libs.Back();
596  lib.Path = dllPath;
597  bool used = false;
598  // HRESULT res = S_OK;
599
600 if (lib.Lib.Load(dllPath))
601 {
602  if (!IsSupportedDll(lib))
603  {
604    CCodecError &error = Errors.AddNew();
605    error.Path = dllPath;
606    error.Message = "the module is not compatible with program";
607  }
608  else
609  {
610    if (loadedOK)
611      *loadedOK = true;
612    /*
613    #ifdef NEW_FOLDER_INTERFACE
614    lib.LoadIcons();
615    #endif
616    */
617
618    /*
619    {
620      MY_GET_FUNC_LOC (_libStartup, Func_libStartup, lib.Lib, "LibStartup")
621      if (_libStartup)
622      {
623        HRESULT res = _libStartup();
624        if (res != 0)
625        {
626          CCodecError &error = Errors.AddNew();
627          error.Path = dllPath;
628          error.ErrorCode = res;
629        }
630      }
631    }
632    */
633
634    #ifdef Z7_LARGE_PAGES
635    if (g_LargePageSize != 0)
636    {
637      MY_GET_FUNC_LOC (setLargePageMode, Func_SetLargePageMode, lib.Lib, "SetLargePageMode")
638      if (setLargePageMode)
639        setLargePageMode();
640    }
641    #endif
642
643    if (CaseSensitive_Change)
644    {
645      MY_GET_FUNC_LOC (setCaseSensitive, Func_SetCaseSensitive, lib.Lib, "SetCaseSensitive")
646      if (setCaseSensitive)
647        setCaseSensitive(CaseSensitive ? 1 : 0);
648    }
649
650    /*
651    {
652      MY_GET_FUNC_LOC (setClientVersion, Func_SetClientVersion, lib.Lib, "SetClientVersion")
653      if (setClientVersion)
654      {
655        // const UInt32 kVersion = (MY_VER_MAJOR << 16) | MY_VER_MINOR;
656        setClientVersion(g_ClientVersion);
657      }
658    }
659    */
660
661
662    MY_GET_FUNC (lib.CreateObject, Func_CreateObject, lib.Lib, "CreateObject")
663    {
664      unsigned startSize = Codecs.Size() + Hashers.Size();
665      HRESULT res = LoadCodecs();
666      if (startSize != Codecs.Size() + Hashers.Size())
667        used = true;
668      if (res == S_OK && lib.CreateObject)
669      {
670        startSize = Formats.Size();
671        res = LoadFormats();
672        if (startSize != Formats.Size())
673          used = true;
674      }
675      if (res != S_OK)
676      {
677        CCodecError &error = Errors.AddNew();
678        error.Path = dllPath;
679        error.ErrorCode = res;
680      }
681    }
682    // plugins can use non-7-zip dlls, so we silently ignore non7zip DLLs
683    /*
684    if (!used)
685    {
686      CCodecError &error = Errors.AddNew();
687      error.Path = dllPath;
688      error.Message = "no 7-Zip code";
689    }
690    */
691  }
692 }
693 else
694  {
695    AddLastError(dllPath);
696  }
697
698  if (!used)
699    Libs.DeleteBack();
700
701  return S_OK;
702}
703
704HRESULT CCodecs::LoadDllsFromFolder(const FString &folderPath)
705{
706  if (!NFile::NFind::DoesDirExist_FollowLink(folderPath))
707  // if (!NFile::NFind::DoesDirExist(folderPath))
708  {
709    // AddLastError(folderPath);
710    return S_OK;
711  }
712
713  FString folderPrefix = folderPath;
714  folderPrefix.Add_PathSepar();
715
716  NFile::NFind::CEnumerator enumerator;
717  enumerator.SetDirPrefix(folderPrefix);
718  NFile::NFind::CDirEntry fi;
719  for (;;)
720  {
721    bool found;
722    if (!enumerator.Next(fi, found))
723    {
724      // it can be wrong Symbolic link to folder here
725      AddLastError(folderPath);
726      break;
727      // return GetLastError_noZero_HRESULT();
728    }
729    if (!found)
730      break;
731    #ifdef _WIN32
732    if (fi.IsDir())
733      continue;
734    #else
735    if (enumerator.DirEntry_IsDir(fi, true)) // followLink
736      continue;
737    #endif
738
739    RINOK(LoadDll(folderPrefix + fi.Name, true))
740  }
741  return S_OK;
742}
743
744void CCodecs::CloseLibs()
745{
746  // OutputDebugStringA("~CloseLibs start");
747  /*
748  WIN32: FreeLibrary() (CLibrary::Free()) function doesn't work as expected,
749  if it's called from another FreeLibrary() call.
750  So we need to call FreeLibrary() before global destructors.
751
752  Also we free global links from DLLs to object of this module before CLibrary::Free() call.
753  */
754
755  FOR_VECTOR(i, Libs)
756  {
757    const CCodecLib &lib = Libs[i];
758    if (lib.SetCodecs)
759      lib.SetCodecs(NULL);
760  }
761
762  // OutputDebugStringA("~CloseLibs after SetCodecs");
763  Libs.Clear();
764  // OutputDebugStringA("~CloseLibs end");
765}
766
767#endif // Z7_EXTERNAL_CODECS
768
769
770HRESULT CCodecs::Load()
771{
772  /*
773  #ifdef NEW_FOLDER_INTERFACE
774  InternalIcons.LoadIcons(g_hInstance);
775  #endif
776  */
777
778  Formats.Clear();
779
780  #ifdef Z7_EXTERNAL_CODECS
781    Errors.Clear();
782    MainDll_ErrorPath.Empty();
783    Codecs.Clear();
784    Hashers.Clear();
785  #endif
786
787  for (UInt32 i = 0; i < g_NumArcs; i++)
788  {
789    const CArcInfo &arc = *g_Arcs[i];
790    CArcInfoEx item;
791
792    item.Name = arc.Name;
793    item.CreateInArchive = arc.CreateInArchive;
794    item.IsArcFunc = arc.IsArc;
795    item.Flags = arc.Flags;
796
797    {
798      UString e, ae;
799      if (arc.Ext)
800        e = arc.Ext;
801      if (arc.AddExt)
802        ae = arc.AddExt;
803      item.AddExts(e, ae);
804    }
805
806    #ifndef Z7_SFX
807
808    item.CreateOutArchive = arc.CreateOutArchive;
809    item.UpdateEnabled = (arc.CreateOutArchive != NULL);
810    item.SignatureOffset = arc.SignatureOffset;
811    // item.Version = MY_VER_MIX;
812    item.NewInterface = true;
813
814    if (arc.IsMultiSignature())
815      ParseSignatures(arc.Signature, arc.SignatureSize, item.Signatures);
816    else
817    {
818      if (arc.SignatureSize != 0) // 21.04
819        item.Signatures.AddNew().CopyFrom(arc.Signature, arc.SignatureSize);
820    }
821
822    #endif
823
824    Formats.Add(item);
825  }
826
827  // printf("\nLoad codecs \n");
828
829  #ifdef Z7_EXTERNAL_CODECS
830    const FString baseFolder = GetBaseFolderPrefixFromRegistry();
831    {
832      bool loadedOK;
833      RINOK(LoadDll(baseFolder + kMainDll, false, &loadedOK))
834      if (!loadedOK)
835        MainDll_ErrorPath = kMainDll;
836    }
837    RINOK(LoadDllsFromFolder(baseFolder + kCodecsFolderName))
838    RINOK(LoadDllsFromFolder(baseFolder + kFormatsFolderName))
839
840  NeedSetLibCodecs = true;
841
842  if (Libs.Size() == 0)
843    NeedSetLibCodecs = false;
844  else if (Libs.Size() == 1)
845  {
846    // we don't need to set ISetCompressCodecsInfo, if all arcs and codecs are in one external module.
847    #ifndef EXPORT_CODECS
848    if (g_NumArcs == 0)
849      NeedSetLibCodecs = false;
850    #endif
851  }
852
853  if (NeedSetLibCodecs)
854  {
855    /* 15.00: now we call global function in DLL: SetCompressCodecsInfo(c)
856       old versions called only ISetCompressCodecsInfo::SetCompressCodecsInfo(c) for each archive handler */
857
858    FOR_VECTOR(i, Libs)
859    {
860      CCodecLib &lib = Libs[i];
861      MY_GET_FUNC (lib.SetCodecs, Func_SetCodecs, lib.Lib, "SetCodecs")
862      if (lib.SetCodecs)
863      {
864        RINOK(lib.SetCodecs(this))
865      }
866    }
867  }
868
869  #endif
870
871  // we sort Formats to get fixed order of Formats after compilation.
872  Formats.Sort();
873  return S_OK;
874}
875
876#ifndef Z7_SFX
877
878int CCodecs::FindFormatForArchiveName(const UString &arcPath) const
879{
880  int dotPos = arcPath.ReverseFind_Dot();
881  if (dotPos <= arcPath.ReverseFind_PathSepar())
882    return -1;
883  const UString ext = arcPath.Ptr((unsigned)(dotPos + 1));
884  if (ext.IsEmpty())
885    return -1;
886  if (ext.IsEqualTo_Ascii_NoCase("exe"))
887    return -1;
888  FOR_VECTOR (i, Formats)
889  {
890    const CArcInfoEx &arc = Formats[i];
891    /*
892    if (!arc.UpdateEnabled)
893      continue;
894    */
895    if (arc.FindExtension(ext) >= 0)
896      return (int)i;
897  }
898  return -1;
899}
900
901int CCodecs::FindFormatForExtension(const UString &ext) const
902{
903  if (ext.IsEmpty())
904    return -1;
905  FOR_VECTOR (i, Formats)
906    if (Formats[i].FindExtension(ext) >= 0)
907      return (int)i;
908  return -1;
909}
910
911int CCodecs::FindFormatForArchiveType(const UString &arcType) const
912{
913  FOR_VECTOR (i, Formats)
914    if (Formats[i].Name.IsEqualTo_NoCase(arcType))
915      return (int)i;
916  return -1;
917}
918
919bool CCodecs::FindFormatForArchiveType(const UString &arcType, CIntVector &formatIndices) const
920{
921  formatIndices.Clear();
922  for (unsigned pos = 0; pos < arcType.Len();)
923  {
924    int pos2 = arcType.Find(L'.', pos);
925    if (pos2 < 0)
926      pos2 = (int)arcType.Len();
927    const UString name = arcType.Mid(pos, (unsigned)pos2 - pos);
928    if (name.IsEmpty())
929      return false;
930    int index = FindFormatForArchiveType(name);
931    if (index < 0 && name != L"*")
932    {
933      formatIndices.Clear();
934      return false;
935    }
936    formatIndices.Add(index);
937    pos = (unsigned)pos2 + 1;
938  }
939  return true;
940}
941
942#endif // Z7_SFX
943
944
945#ifdef Z7_EXTERNAL_CODECS
946
947// #define EXPORT_CODECS
948
949#ifdef EXPORT_CODECS
950
951extern unsigned g_NumCodecs;
952STDAPI CreateDecoder(UInt32 index, const GUID *iid, void **outObject);
953STDAPI CreateEncoder(UInt32 index, const GUID *iid, void **outObject);
954STDAPI GetMethodProperty(UInt32 codecIndex, PROPID propID, PROPVARIANT *value);
955#define NUM_EXPORT_CODECS g_NumCodecs
956
957extern unsigned g_NumHashers;
958STDAPI CreateHasher(UInt32 index, IHasher **hasher);
959STDAPI GetHasherProp(UInt32 codecIndex, PROPID propID, PROPVARIANT *value);
960#define NUM_EXPORT_HASHERS g_NumHashers
961
962#else // EXPORT_CODECS
963
964#define NUM_EXPORT_CODECS 0
965#define NUM_EXPORT_HASHERS 0
966
967#endif // EXPORT_CODECS
968
969Z7_COM7F_IMF(CCodecs::GetNumMethods(UInt32 *numMethods))
970{
971  *numMethods = NUM_EXPORT_CODECS
972    #ifdef Z7_EXTERNAL_CODECS
973    + Codecs.Size()
974    #endif
975    ;
976  return S_OK;
977}
978
979Z7_COM7F_IMF(CCodecs::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
980{
981  #ifdef EXPORT_CODECS
982  if (index < g_NumCodecs)
983    return GetMethodProperty(index, propID, value);
984  #endif
985
986  #ifdef Z7_EXTERNAL_CODECS
987  const CDllCodecInfo &ci = Codecs[index - NUM_EXPORT_CODECS];
988
989  if (propID == NMethodPropID::kDecoderIsAssigned ||
990      propID == NMethodPropID::kEncoderIsAssigned)
991  {
992    NCOM::CPropVariant prop;
993    prop = (bool)((propID == NMethodPropID::kDecoderIsAssigned) ?
994        ci.DecoderIsAssigned :
995        ci.EncoderIsAssigned);
996    prop.Detach(value);
997    return S_OK;
998  }
999
1000  if (propID == NMethodPropID::kIsFilter && ci.IsFilter_Assigned)
1001  {
1002    NCOM::CPropVariant prop;
1003    prop = (bool)ci.IsFilter;
1004    prop.Detach(value);
1005    return S_OK;
1006  }
1007
1008  const CCodecLib &lib = Libs[ci.LibIndex];
1009  return lib.GetMethodProperty(ci.CodecIndex, propID, value);
1010  #else
1011  return E_FAIL;
1012  #endif
1013}
1014
1015Z7_COM7F_IMF(CCodecs::CreateDecoder(UInt32 index, const GUID *iid, void **coder))
1016{
1017  #ifdef EXPORT_CODECS
1018  if (index < g_NumCodecs)
1019    return CreateDecoder(index, iid, coder);
1020  #endif
1021
1022  #ifdef Z7_EXTERNAL_CODECS
1023  const CDllCodecInfo &ci = Codecs[index - NUM_EXPORT_CODECS];
1024  if (ci.DecoderIsAssigned)
1025  {
1026    const CCodecLib &lib = Libs[ci.LibIndex];
1027    if (lib.CreateDecoder)
1028      return lib.CreateDecoder(ci.CodecIndex, iid, (void **)coder);
1029    if (lib.CreateObject)
1030      return lib.CreateObject(&ci.Decoder, iid, (void **)coder);
1031  }
1032  return S_OK;
1033  #else
1034  return E_FAIL;
1035  #endif
1036}
1037
1038Z7_COM7F_IMF(CCodecs::CreateEncoder(UInt32 index, const GUID *iid, void **coder))
1039{
1040  #ifdef EXPORT_CODECS
1041  if (index < g_NumCodecs)
1042    return CreateEncoder(index, iid, coder);
1043  #endif
1044
1045  #ifdef Z7_EXTERNAL_CODECS
1046  const CDllCodecInfo &ci = Codecs[index - NUM_EXPORT_CODECS];
1047  if (ci.EncoderIsAssigned)
1048  {
1049    const CCodecLib &lib = Libs[ci.LibIndex];
1050    if (lib.CreateEncoder)
1051      return lib.CreateEncoder(ci.CodecIndex, iid, (void **)coder);
1052    if (lib.CreateObject)
1053      return lib.CreateObject(&ci.Encoder, iid, (void **)coder);
1054  }
1055  return S_OK;
1056  #else
1057  return E_FAIL;
1058  #endif
1059}
1060
1061
1062Z7_COM7F_IMF2(UInt32, CCodecs::GetNumHashers())
1063{
1064  return NUM_EXPORT_HASHERS
1065    #ifdef Z7_EXTERNAL_CODECS
1066    + Hashers.Size()
1067    #endif
1068    ;
1069}
1070
1071Z7_COM7F_IMF(CCodecs::GetHasherProp(UInt32 index, PROPID propID, PROPVARIANT *value))
1072{
1073  #ifdef EXPORT_CODECS
1074  if (index < g_NumHashers)
1075    return ::GetHasherProp(index, propID, value);
1076  #endif
1077
1078  #ifdef Z7_EXTERNAL_CODECS
1079  const CDllHasherInfo &ci = Hashers[index - NUM_EXPORT_HASHERS];
1080  return Libs[ci.LibIndex].ComHashers->GetHasherProp(ci.HasherIndex, propID, value);
1081  #else
1082  return E_FAIL;
1083  #endif
1084}
1085
1086Z7_COM7F_IMF(CCodecs::CreateHasher(UInt32 index, IHasher **hasher))
1087{
1088  #ifdef EXPORT_CODECS
1089  if (index < g_NumHashers)
1090    return CreateHasher(index, hasher);
1091  #endif
1092  #ifdef Z7_EXTERNAL_CODECS
1093  const CDllHasherInfo &ci = Hashers[index - NUM_EXPORT_HASHERS];
1094  return Libs[ci.LibIndex].ComHashers->CreateHasher(ci.HasherIndex, hasher);
1095  #else
1096  return E_FAIL;
1097  #endif
1098}
1099
1100int CCodecs::GetCodec_LibIndex(UInt32 index) const
1101{
1102  #ifdef EXPORT_CODECS
1103  if (index < g_NumCodecs)
1104    return -1;
1105  #endif
1106
1107  #ifdef Z7_EXTERNAL_CODECS
1108  const CDllCodecInfo &ci = Codecs[index - NUM_EXPORT_CODECS];
1109  return (int)ci.LibIndex;
1110  #else
1111  return -1;
1112  #endif
1113}
1114
1115int CCodecs::GetHasherLibIndex(UInt32 index)
1116{
1117  #ifdef EXPORT_CODECS
1118  if (index < g_NumHashers)
1119    return -1;
1120  #endif
1121
1122  #ifdef Z7_EXTERNAL_CODECS
1123  const CDllHasherInfo &ci = Hashers[index - NUM_EXPORT_HASHERS];
1124  return (int)ci.LibIndex;
1125  #else
1126  return -1;
1127  #endif
1128}
1129
1130bool CCodecs::GetCodec_DecoderIsAssigned(UInt32 index) const
1131{
1132  #ifdef EXPORT_CODECS
1133  if (index < g_NumCodecs)
1134  {
1135    NCOM::CPropVariant prop;
1136    if (GetProperty(index, NMethodPropID::kDecoderIsAssigned, &prop) == S_OK)
1137    {
1138      if (prop.vt == VT_BOOL)
1139        return VARIANT_BOOLToBool(prop.boolVal);
1140    }
1141    return false;
1142  }
1143  #endif
1144
1145  #ifdef Z7_EXTERNAL_CODECS
1146  return Codecs[index - NUM_EXPORT_CODECS].DecoderIsAssigned;
1147  #else
1148  return false;
1149  #endif
1150}
1151
1152
1153bool CCodecs::GetCodec_EncoderIsAssigned(UInt32 index) const
1154{
1155  #ifdef EXPORT_CODECS
1156  if (index < g_NumCodecs)
1157  {
1158    NCOM::CPropVariant prop;
1159    if (GetProperty(index, NMethodPropID::kEncoderIsAssigned, &prop) == S_OK)
1160    {
1161      if (prop.vt == VT_BOOL)
1162        return VARIANT_BOOLToBool(prop.boolVal);
1163    }
1164    return false;
1165  }
1166  #endif
1167
1168  #ifdef Z7_EXTERNAL_CODECS
1169  return Codecs[index - NUM_EXPORT_CODECS].EncoderIsAssigned;
1170  #else
1171  return false;
1172  #endif
1173}
1174
1175
1176bool CCodecs::GetCodec_IsFilter(UInt32 index, bool &isAssigned) const
1177{
1178  isAssigned = false;
1179  #ifdef EXPORT_CODECS
1180  if (index < g_NumCodecs)
1181  {
1182    NCOM::CPropVariant prop;
1183    if (GetProperty(index, NMethodPropID::kIsFilter, &prop) == S_OK)
1184    {
1185      if (prop.vt == VT_BOOL)
1186      {
1187        isAssigned = true;
1188        return VARIANT_BOOLToBool(prop.boolVal);
1189      }
1190    }
1191    return false;
1192  }
1193  #endif
1194
1195  #ifdef Z7_EXTERNAL_CODECS
1196  {
1197    const CDllCodecInfo &c = Codecs[index - NUM_EXPORT_CODECS];
1198    isAssigned = c.IsFilter_Assigned;
1199    return c.IsFilter;
1200  }
1201  #else
1202  return false;
1203  #endif
1204}
1205
1206
1207UInt32 CCodecs::GetCodec_NumStreams(UInt32 index)
1208{
1209  NCOM::CPropVariant prop;
1210  if (GetProperty(index, NMethodPropID::kPackStreams, &prop) != S_OK)
1211    return 0;
1212  if (prop.vt == VT_UI4)
1213    return (UInt32)prop.ulVal;
1214  if (prop.vt == VT_EMPTY)
1215    return 1;
1216  return 0;
1217}
1218
1219HRESULT CCodecs::GetCodec_Id(UInt32 index, UInt64 &id)
1220{
1221  NCOM::CPropVariant prop;
1222  RINOK(GetProperty(index, NMethodPropID::kID, &prop))
1223  if (prop.vt != VT_UI8)
1224    return E_INVALIDARG;
1225  id = prop.uhVal.QuadPart;
1226  return S_OK;
1227}
1228
1229AString CCodecs::GetCodec_Name(UInt32 index)
1230{
1231  AString s;
1232  NCOM::CPropVariant prop;
1233  if (GetProperty(index, NMethodPropID::kName, &prop) == S_OK)
1234    if (prop.vt == VT_BSTR)
1235      s.SetFromWStr_if_Ascii(prop.bstrVal);
1236  return s;
1237}
1238
1239UInt64 CCodecs::GetHasherId(UInt32 index)
1240{
1241  NCOM::CPropVariant prop;
1242  if (GetHasherProp(index, NMethodPropID::kID, &prop) != S_OK)
1243    return 0;
1244  if (prop.vt != VT_UI8)
1245    return 0;
1246  return prop.uhVal.QuadPart;
1247}
1248
1249AString CCodecs::GetHasherName(UInt32 index)
1250{
1251  AString s;
1252  NCOM::CPropVariant prop;
1253  if (GetHasherProp(index, NMethodPropID::kName, &prop) == S_OK)
1254    if (prop.vt == VT_BSTR)
1255      s.SetFromWStr_if_Ascii(prop.bstrVal);
1256  return s;
1257}
1258
1259UInt32 CCodecs::GetHasherDigestSize(UInt32 index)
1260{
1261  NCOM::CPropVariant prop;
1262  if (GetHasherProp(index, NMethodPropID::kDigestSize, &prop) != S_OK)
1263    return 0;
1264  if (prop.vt != VT_UI4)
1265    return 0;
1266  return prop.ulVal;
1267}
1268
1269void CCodecs::GetCodecsErrorMessage(UString &s)
1270{
1271  s.Empty();
1272  FOR_VECTOR (i, Errors)
1273  {
1274    const CCodecError &ce = Errors[i];
1275    s += "Codec Load Error: ";
1276    s += fs2us(ce.Path);
1277    if (ce.ErrorCode != 0)
1278    {
1279      s += " : ";
1280      s += NWindows::NError::MyFormatMessage(ce.ErrorCode);
1281    }
1282    if (!ce.Message.IsEmpty())
1283    {
1284      s += " : ";
1285      s += ce.Message;
1286    }
1287    s.Add_LF();
1288  }
1289}
1290
1291#endif // Z7_EXTERNAL_CODECS
1292
1293#ifndef Z7_SFX
1294
1295extern unsigned g_NumCodecs;
1296extern const CCodecInfo *g_Codecs[];
1297
1298void CCodecs::Get_CodecsInfoUser_Vector(CObjectVector<CCodecInfoUser> &v)
1299{
1300  v.Clear();
1301  {
1302    for (unsigned i = 0; i < g_NumCodecs; i++)
1303    {
1304      const CCodecInfo &cod = *g_Codecs[i];
1305      CCodecInfoUser &u = v.AddNew();
1306      u.EncoderIsAssigned = (cod.CreateEncoder != NULL);
1307      u.DecoderIsAssigned = (cod.CreateDecoder != NULL);
1308      u.IsFilter_Assigned = true;
1309      u.IsFilter = cod.IsFilter;
1310      u.NumStreams = cod.NumStreams;
1311      u.Name = cod.Name;
1312    }
1313  }
1314
1315
1316  #ifdef Z7_EXTERNAL_CODECS
1317  {
1318    UInt32 numMethods;
1319    if (GetNumMethods(&numMethods) == S_OK)
1320    for (UInt32 j = 0; j < numMethods; j++)
1321    {
1322      CCodecInfoUser &u = v.AddNew();
1323      u.EncoderIsAssigned = GetCodec_EncoderIsAssigned(j);
1324      u.DecoderIsAssigned = GetCodec_DecoderIsAssigned(j);
1325      u.IsFilter = GetCodec_IsFilter(j, u.IsFilter_Assigned);
1326      u.NumStreams = GetCodec_NumStreams(j);
1327      u.Name = GetCodec_Name(j);
1328    }
1329  }
1330  #endif
1331}
1332
1333#endif
1334