1// LoadCodecs.cpp
2
3#include "StdAfx.h"
4
5#include "LoadCodecs.h"
6
7#include "../../../Common/MyCom.h"
8#ifdef NEW_FOLDER_INTERFACE
9#include "../../../Common/StringToInt.h"
10#endif
11#include "../../../Windows/PropVariant.h"
12
13#include "../../ICoder.h"
14#include "../../Common/RegisterArc.h"
15
16#ifdef EXTERNAL_CODECS
17#include "../../../Windows/FileFind.h"
18#include "../../../Windows/DLL.h"
19#ifdef NEW_FOLDER_INTERFACE
20#include "../../../Windows/ResourceString.h"
21static const UINT kIconTypesResId = 100;
22#endif
23
24#ifdef _WIN32
25#include "Windows/Registry.h"
26#endif
27
28using namespace NWindows;
29using namespace NFile;
30
31#ifdef _WIN32
32extern HINSTANCE g_hInstance;
33#endif
34
35static CSysString GetLibraryFolderPrefix()
36{
37  #ifdef _WIN32
38  TCHAR fullPath[MAX_PATH + 1];
39  ::GetModuleFileName(g_hInstance, fullPath, MAX_PATH);
40  CSysString path = fullPath;
41  int pos = path.ReverseFind(TEXT(CHAR_PATH_SEPARATOR));
42  return path.Left(pos + 1);
43  #else
44  return CSysString(); // FIX IT
45  #endif
46}
47
48#define kCodecsFolderName TEXT("Codecs")
49#define kFormatsFolderName TEXT("Formats")
50static const TCHAR *kMainDll = TEXT("7z.dll");
51
52#ifdef _WIN32
53static LPCTSTR kRegistryPath = TEXT("Software") TEXT(STRING_PATH_SEPARATOR) TEXT("7-zip");
54static LPCTSTR kProgramPathValue = TEXT("Path");
55static bool ReadPathFromRegistry(HKEY baseKey, CSysString &path)
56{
57  NRegistry::CKey key;
58  if(key.Open(baseKey, kRegistryPath, KEY_READ) == ERROR_SUCCESS)
59    if (key.QueryValue(kProgramPathValue, path) == ERROR_SUCCESS)
60    {
61      NName::NormalizeDirPathPrefix(path);
62      return true;
63    }
64  return false;
65}
66
67#endif
68
69CSysString GetBaseFolderPrefixFromRegistry()
70{
71  CSysString moduleFolderPrefix = GetLibraryFolderPrefix();
72  #ifdef _WIN32
73  if (!NFind::DoesFileExist(moduleFolderPrefix + kMainDll) &&
74      !NFind::DoesDirExist(moduleFolderPrefix + kCodecsFolderName) &&
75      !NFind::DoesDirExist(moduleFolderPrefix + kFormatsFolderName))
76  {
77    CSysString path;
78    if (ReadPathFromRegistry(HKEY_CURRENT_USER, path))
79      return path;
80    if (ReadPathFromRegistry(HKEY_LOCAL_MACHINE, path))
81      return path;
82  }
83  #endif
84  return moduleFolderPrefix;
85}
86
87typedef UInt32 (WINAPI *GetNumberOfMethodsFunc)(UInt32 *numMethods);
88typedef UInt32 (WINAPI *GetNumberOfFormatsFunc)(UInt32 *numFormats);
89typedef UInt32 (WINAPI *GetHandlerPropertyFunc)(PROPID propID, PROPVARIANT *value);
90typedef UInt32 (WINAPI *GetHandlerPropertyFunc2)(UInt32 index, PROPID propID, PROPVARIANT *value);
91typedef UInt32 (WINAPI *CreateObjectFunc)(const GUID *clsID, const GUID *iid, void **outObject);
92typedef UInt32 (WINAPI *SetLargePageModeFunc)();
93
94
95static HRESULT GetCoderClass(GetMethodPropertyFunc getMethodProperty, UInt32 index,
96    PROPID propId, CLSID &clsId, bool &isAssigned)
97{
98  NWindows::NCOM::CPropVariant prop;
99  isAssigned = false;
100  RINOK(getMethodProperty(index, propId, &prop));
101  if (prop.vt == VT_BSTR)
102  {
103    isAssigned = true;
104    clsId = *(const GUID *)prop.bstrVal;
105  }
106  else if (prop.vt != VT_EMPTY)
107    return E_FAIL;
108  return S_OK;
109}
110
111HRESULT CCodecs::LoadCodecs()
112{
113  CCodecLib &lib = Libs.Back();
114  lib.GetMethodProperty = (GetMethodPropertyFunc)lib.Lib.GetProc("GetMethodProperty");
115  if (lib.GetMethodProperty == NULL)
116    return S_OK;
117
118  UInt32 numMethods = 1;
119  GetNumberOfMethodsFunc getNumberOfMethodsFunc = (GetNumberOfMethodsFunc)lib.Lib.GetProc("GetNumberOfMethods");
120  if (getNumberOfMethodsFunc != NULL)
121  {
122    RINOK(getNumberOfMethodsFunc(&numMethods));
123  }
124
125  for(UInt32 i = 0; i < numMethods; i++)
126  {
127    CDllCodecInfo info;
128    info.LibIndex = Libs.Size() - 1;
129    info.CodecIndex = i;
130
131    RINOK(GetCoderClass(lib.GetMethodProperty, i, NMethodPropID::kEncoder, info.Encoder, info.EncoderIsAssigned));
132    RINOK(GetCoderClass(lib.GetMethodProperty, i, NMethodPropID::kDecoder, info.Decoder, info.DecoderIsAssigned));
133
134    Codecs.Add(info);
135  }
136  return S_OK;
137}
138
139static HRESULT ReadProp(
140    GetHandlerPropertyFunc getProp,
141    GetHandlerPropertyFunc2 getProp2,
142    UInt32 index, PROPID propID, NCOM::CPropVariant &prop)
143{
144  if (getProp2)
145    return getProp2(index, propID, &prop);;
146  return getProp(propID, &prop);
147}
148
149static HRESULT ReadBoolProp(
150    GetHandlerPropertyFunc getProp,
151    GetHandlerPropertyFunc2 getProp2,
152    UInt32 index, PROPID propID, bool &res)
153{
154  NCOM::CPropVariant prop;
155  RINOK(ReadProp(getProp, getProp2, index, propID, prop));
156  if (prop.vt == VT_BOOL)
157    res = VARIANT_BOOLToBool(prop.boolVal);
158  else if (prop.vt != VT_EMPTY)
159    return E_FAIL;
160  return S_OK;
161}
162
163static HRESULT ReadStringProp(
164    GetHandlerPropertyFunc getProp,
165    GetHandlerPropertyFunc2 getProp2,
166    UInt32 index, PROPID propID, UString &res)
167{
168  NCOM::CPropVariant prop;
169  RINOK(ReadProp(getProp, getProp2, index, propID, prop));
170  if (prop.vt == VT_BSTR)
171    res = prop.bstrVal;
172  else if (prop.vt != VT_EMPTY)
173    return E_FAIL;
174  return S_OK;
175}
176
177#endif
178
179static const unsigned int kNumArcsMax = 48;
180static unsigned int g_NumArcs = 0;
181static const CArcInfo *g_Arcs[kNumArcsMax];
182void RegisterArc(const CArcInfo *arcInfo)
183{
184  if (g_NumArcs < kNumArcsMax)
185    g_Arcs[g_NumArcs++] = arcInfo;
186}
187
188static void SplitString(const UString &srcString, UStringVector &destStrings)
189{
190  destStrings.Clear();
191  UString s;
192  int len = srcString.Length();
193  if (len == 0)
194    return;
195  for (int i = 0; i < len; i++)
196  {
197    wchar_t c = srcString[i];
198    if (c == L' ')
199    {
200      if (!s.IsEmpty())
201      {
202        destStrings.Add(s);
203        s.Empty();
204      }
205    }
206    else
207      s += c;
208  }
209  if (!s.IsEmpty())
210    destStrings.Add(s);
211}
212
213void CArcInfoEx::AddExts(const wchar_t *ext, const wchar_t *addExt)
214{
215  UStringVector exts, addExts;
216  if (ext != 0)
217    SplitString(ext, exts);
218  if (addExt != 0)
219    SplitString(addExt, addExts);
220  for (int i = 0; i < exts.Size(); i++)
221  {
222    CArcExtInfo extInfo;
223    extInfo.Ext = exts[i];
224    if (i < addExts.Size())
225    {
226      extInfo.AddExt = addExts[i];
227      if (extInfo.AddExt == L"*")
228        extInfo.AddExt.Empty();
229    }
230    Exts.Add(extInfo);
231  }
232}
233
234#ifdef EXTERNAL_CODECS
235
236HRESULT CCodecs::LoadFormats()
237{
238  const NDLL::CLibrary &lib = Libs.Back().Lib;
239  GetHandlerPropertyFunc getProp = 0;
240  GetHandlerPropertyFunc2 getProp2 = (GetHandlerPropertyFunc2)lib.GetProc("GetHandlerProperty2");
241  if (getProp2 == NULL)
242  {
243    getProp = (GetHandlerPropertyFunc)lib.GetProc("GetHandlerProperty");
244    if (getProp == NULL)
245      return S_OK;
246  }
247
248  UInt32 numFormats = 1;
249  GetNumberOfFormatsFunc getNumberOfFormats = (GetNumberOfFormatsFunc)lib.GetProc("GetNumberOfFormats");
250  if (getNumberOfFormats != NULL)
251  {
252    RINOK(getNumberOfFormats(&numFormats));
253  }
254  if (getProp2 == NULL)
255    numFormats = 1;
256
257  for(UInt32 i = 0; i < numFormats; i++)
258  {
259    CArcInfoEx item;
260    item.LibIndex = Libs.Size() - 1;
261    item.FormatIndex = i;
262
263    RINOK(ReadStringProp(getProp, getProp2, i, NArchive::kName, item.Name));
264
265    NCOM::CPropVariant prop;
266    if (ReadProp(getProp, getProp2, i, NArchive::kClassID, prop) != S_OK)
267      continue;
268    if (prop.vt != VT_BSTR)
269      continue;
270    item.ClassID = *(const GUID *)prop.bstrVal;
271    prop.Clear();
272
273    UString ext, addExt;
274    RINOK(ReadStringProp(getProp, getProp2, i, NArchive::kExtension, ext));
275    RINOK(ReadStringProp(getProp, getProp2, i, NArchive::kAddExtension, addExt));
276    item.AddExts(ext, addExt);
277
278    ReadBoolProp(getProp, getProp2, i, NArchive::kUpdate, item.UpdateEnabled);
279    if (item.UpdateEnabled)
280      ReadBoolProp(getProp, getProp2, i, NArchive::kKeepName, item.KeepName);
281
282    if (ReadProp(getProp, getProp2, i, NArchive::kStartSignature, prop) == S_OK)
283      if (prop.vt == VT_BSTR)
284      {
285        UINT len = ::SysStringByteLen(prop.bstrVal);
286        item.StartSignature.SetCapacity(len);
287        memmove(item.StartSignature, prop.bstrVal, len);
288      }
289    Formats.Add(item);
290  }
291  return S_OK;
292}
293
294#ifdef NEW_FOLDER_INTERFACE
295void CCodecIcons::LoadIcons(HMODULE m)
296{
297  UString iconTypes = MyLoadStringW(m, kIconTypesResId);
298  UStringVector pairs;
299  SplitString(iconTypes, pairs);
300  for (int i = 0; i < pairs.Size(); i++)
301  {
302    const UString &s = pairs[i];
303    int pos = s.Find(L':');
304    CIconPair iconPair;
305    iconPair.IconIndex = -1;
306    if (pos < 0)
307      pos = s.Length();
308    else
309    {
310      UString num = s.Mid(pos + 1);
311      if (!num.IsEmpty())
312      {
313        const wchar_t *end;
314        iconPair.IconIndex = (UInt32)ConvertStringToUInt64(num, &end);
315        if (*end != L'\0')
316          continue;
317      }
318    }
319    iconPair.Ext = s.Left(pos);
320    IconPairs.Add(iconPair);
321  }
322}
323
324bool CCodecIcons::FindIconIndex(const UString &ext, int &iconIndex) const
325{
326  iconIndex = -1;
327  for (int i = 0; i < IconPairs.Size(); i++)
328  {
329    const CIconPair &pair = IconPairs[i];
330    if (ext.CompareNoCase(pair.Ext) == 0)
331    {
332      iconIndex = pair.IconIndex;
333      return true;
334    }
335  }
336  return false;
337}
338#endif
339
340#ifdef _7ZIP_LARGE_PAGES
341extern "C"
342{
343  extern SIZE_T g_LargePageSize;
344}
345#endif
346
347HRESULT CCodecs::LoadDll(const CSysString &dllPath, bool needCheckDll)
348{
349  if (needCheckDll)
350  {
351    NDLL::CLibrary library;
352    if (!library.LoadEx(dllPath, LOAD_LIBRARY_AS_DATAFILE))
353      return S_OK;
354  }
355  Libs.Add(CCodecLib());
356  CCodecLib &lib = Libs.Back();
357  #ifdef NEW_FOLDER_INTERFACE
358  lib.Path = dllPath;
359  #endif
360  bool used = false;
361  HRESULT res = S_OK;
362  if (lib.Lib.Load(dllPath))
363  {
364    #ifdef NEW_FOLDER_INTERFACE
365    lib.LoadIcons();
366    #endif
367
368    #ifdef _7ZIP_LARGE_PAGES
369    if (g_LargePageSize != 0)
370    {
371      SetLargePageModeFunc setLargePageMode = (SetLargePageModeFunc)lib.Lib.GetProc("SetLargePageMode");
372      if (setLargePageMode != 0)
373        setLargePageMode();
374    }
375    #endif
376
377    lib.CreateObject = (CreateObjectFunc)lib.Lib.GetProc("CreateObject");
378    if (lib.CreateObject != 0)
379    {
380      int startSize = Codecs.Size();
381      res = LoadCodecs();
382      used = (Codecs.Size() != startSize);
383      if (res == S_OK)
384      {
385        startSize = Formats.Size();
386        res = LoadFormats();
387        used = used || (Formats.Size() != startSize);
388      }
389    }
390  }
391  if (!used)
392    Libs.DeleteBack();
393  return res;
394}
395
396HRESULT CCodecs::LoadDllsFromFolder(const CSysString &folderPrefix)
397{
398  NFile::NFind::CEnumerator enumerator(folderPrefix + CSysString(TEXT("*")));
399  NFile::NFind::CFileInfo fi;
400  while (enumerator.Next(fi))
401  {
402    if (fi.IsDir())
403      continue;
404    RINOK(LoadDll(folderPrefix + fi.Name, true));
405  }
406  return S_OK;
407}
408
409#endif
410
411#ifndef _SFX
412static inline void SetBuffer(CByteBuffer &bb, const Byte *data, int size)
413{
414  bb.SetCapacity(size);
415  memmove((Byte *)bb, data, size);
416}
417#endif
418
419HRESULT CCodecs::Load()
420{
421  #ifdef NEW_FOLDER_INTERFACE
422  InternalIcons.LoadIcons(g_hInstance);
423  #endif
424
425  Formats.Clear();
426  #ifdef EXTERNAL_CODECS
427  Codecs.Clear();
428  #endif
429  for (UInt32 i = 0; i < g_NumArcs; i++)
430  {
431    const CArcInfo &arc = *g_Arcs[i];
432    CArcInfoEx item;
433    item.Name = arc.Name;
434    item.CreateInArchive = arc.CreateInArchive;
435    item.CreateOutArchive = arc.CreateOutArchive;
436    item.AddExts(arc.Ext, arc.AddExt);
437    item.UpdateEnabled = (arc.CreateOutArchive != 0);
438    item.KeepName = arc.KeepName;
439
440    #ifndef _SFX
441    SetBuffer(item.StartSignature, arc.Signature, arc.SignatureSize);
442    #endif
443    Formats.Add(item);
444  }
445  #ifdef EXTERNAL_CODECS
446  const CSysString baseFolder = GetBaseFolderPrefixFromRegistry();
447  RINOK(LoadDll(baseFolder + kMainDll, false));
448  RINOK(LoadDllsFromFolder(baseFolder + kCodecsFolderName TEXT(STRING_PATH_SEPARATOR)));
449  RINOK(LoadDllsFromFolder(baseFolder + kFormatsFolderName TEXT(STRING_PATH_SEPARATOR)));
450  #endif
451  return S_OK;
452}
453
454#ifndef _SFX
455
456int CCodecs::FindFormatForArchiveName(const UString &arcPath) const
457{
458  int slashPos1 = arcPath.ReverseFind(WCHAR_PATH_SEPARATOR);
459  int slashPos2 = arcPath.ReverseFind(L'.');
460  int dotPos = arcPath.ReverseFind(L'.');
461  if (dotPos < 0 || dotPos < slashPos1 || dotPos < slashPos2)
462    return -1;
463  UString ext = arcPath.Mid(dotPos + 1);
464  for (int i = 0; i < Formats.Size(); i++)
465  {
466    const CArcInfoEx &arc = Formats[i];
467    if (!arc.UpdateEnabled)
468      continue;
469    if (arc.FindExtension(ext) >= 0)
470      return i;
471  }
472  return -1;
473}
474
475int CCodecs::FindFormatForExtension(const UString &ext) const
476{
477  if (ext.IsEmpty())
478    return -1;
479  for (int i = 0; i < Formats.Size(); i++)
480    if (Formats[i].FindExtension(ext) >= 0)
481      return i;
482  return -1;
483}
484
485int CCodecs::FindFormatForArchiveType(const UString &arcType) const
486{
487  for (int i = 0; i < Formats.Size(); i++)
488    if (Formats[i].Name.CompareNoCase(arcType) == 0)
489      return i;
490  return -1;
491}
492
493bool CCodecs::FindFormatForArchiveType(const UString &arcType, CIntVector &formatIndices) const
494{
495  formatIndices.Clear();
496  for (int pos = 0; pos < arcType.Length();)
497  {
498    int pos2 = arcType.Find('.', pos);
499    if (pos2 < 0)
500      pos2 = arcType.Length();
501    const UString name = arcType.Mid(pos, pos2 - pos);
502    int index = FindFormatForArchiveType(name);
503    if (index < 0 && name != L"*")
504    {
505      formatIndices.Clear();
506      return false;
507    }
508    formatIndices.Add(index);
509    pos = pos2 + 1;
510  }
511  return true;
512}
513
514#endif
515
516#ifdef EXTERNAL_CODECS
517
518#ifdef EXPORT_CODECS
519extern unsigned int g_NumCodecs;
520STDAPI CreateCoder2(bool encode, UInt32 index, const GUID *iid, void **outObject);
521STDAPI GetMethodProperty(UInt32 codecIndex, PROPID propID, PROPVARIANT *value);
522// STDAPI GetNumberOfMethods(UInt32 *numCodecs);
523#endif
524
525STDMETHODIMP CCodecs::GetNumberOfMethods(UInt32 *numMethods)
526{
527  *numMethods =
528      #ifdef EXPORT_CODECS
529      g_NumCodecs +
530      #endif
531      Codecs.Size();
532  return S_OK;
533}
534
535STDMETHODIMP CCodecs::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
536{
537  #ifdef EXPORT_CODECS
538  if (index < g_NumCodecs)
539    return GetMethodProperty(index, propID, value);
540  #endif
541
542  const CDllCodecInfo &ci = Codecs[index
543      #ifdef EXPORT_CODECS
544      - g_NumCodecs
545      #endif
546      ];
547
548  if (propID == NMethodPropID::kDecoderIsAssigned)
549  {
550    NWindows::NCOM::CPropVariant propVariant;
551    propVariant = ci.DecoderIsAssigned;
552    propVariant.Detach(value);
553    return S_OK;
554  }
555  if (propID == NMethodPropID::kEncoderIsAssigned)
556  {
557    NWindows::NCOM::CPropVariant propVariant;
558    propVariant = ci.EncoderIsAssigned;
559    propVariant.Detach(value);
560    return S_OK;
561  }
562  return Libs[ci.LibIndex].GetMethodProperty(ci.CodecIndex, propID, value);
563}
564
565STDMETHODIMP CCodecs::CreateDecoder(UInt32 index, const GUID *iid, void **coder)
566{
567  #ifdef EXPORT_CODECS
568  if (index < g_NumCodecs)
569    return CreateCoder2(false, index, iid, coder);
570  #endif
571  const CDllCodecInfo &ci = Codecs[index
572      #ifdef EXPORT_CODECS
573      - g_NumCodecs
574      #endif
575      ];
576  if (ci.DecoderIsAssigned)
577    return Libs[ci.LibIndex].CreateObject(&ci.Decoder, iid, (void **)coder);
578  return S_OK;
579}
580
581STDMETHODIMP CCodecs::CreateEncoder(UInt32 index, const GUID *iid, void **coder)
582{
583  #ifdef EXPORT_CODECS
584  if (index < g_NumCodecs)
585    return CreateCoder2(true, index, iid, coder);
586  #endif
587  const CDllCodecInfo &ci = Codecs[index
588      #ifdef EXPORT_CODECS
589      - g_NumCodecs
590      #endif
591      ];
592  if (ci.EncoderIsAssigned)
593    return Libs[ci.LibIndex].CreateObject(&ci.Encoder, iid, (void **)coder);
594  return S_OK;
595}
596
597HRESULT CCodecs::CreateCoder(const UString &name, bool encode, CMyComPtr<ICompressCoder> &coder) const
598{
599  for (int i = 0; i < Codecs.Size(); i++)
600  {
601    const CDllCodecInfo &codec = Codecs[i];
602    if (encode && !codec.EncoderIsAssigned || !encode && !codec.DecoderIsAssigned)
603      continue;
604    const CCodecLib &lib = Libs[codec.LibIndex];
605    UString res;
606    NWindows::NCOM::CPropVariant prop;
607    RINOK(lib.GetMethodProperty(codec.CodecIndex, NMethodPropID::kName, &prop));
608    if (prop.vt == VT_BSTR)
609      res = prop.bstrVal;
610    else if (prop.vt != VT_EMPTY)
611      continue;
612    if (name.CompareNoCase(res) == 0)
613      return lib.CreateObject(encode ? &codec.Encoder : &codec.Decoder, &IID_ICompressCoder, (void **)&coder);
614  }
615  return CLASS_E_CLASSNOTAVAILABLE;
616}
617
618int CCodecs::GetCodecLibIndex(UInt32 index)
619{
620  #ifdef EXPORT_CODECS
621  if (index < g_NumCodecs)
622    return -1;
623  #endif
624  #ifdef EXTERNAL_CODECS
625  const CDllCodecInfo &ci = Codecs[index
626      #ifdef EXPORT_CODECS
627      - g_NumCodecs
628      #endif
629      ];
630  return ci.LibIndex;
631  #else
632  return -1;
633  #endif
634}
635
636bool CCodecs::GetCodecEncoderIsAssigned(UInt32 index)
637{
638  #ifdef EXPORT_CODECS
639  if (index < g_NumCodecs)
640  {
641    NWindows::NCOM::CPropVariant prop;
642    if (GetProperty(index, NMethodPropID::kEncoder, &prop) == S_OK)
643      if (prop.vt != VT_EMPTY)
644        return true;
645    return false;
646  }
647  #endif
648  #ifdef EXTERNAL_CODECS
649  const CDllCodecInfo &ci = Codecs[index
650      #ifdef EXPORT_CODECS
651      - g_NumCodecs
652      #endif
653      ];
654  return ci.EncoderIsAssigned;
655  #else
656  return false;
657  #endif
658}
659
660HRESULT CCodecs::GetCodecId(UInt32 index, UInt64 &id)
661{
662  UString s;
663  NWindows::NCOM::CPropVariant prop;
664  RINOK(GetProperty(index, NMethodPropID::kID, &prop));
665  if (prop.vt != VT_UI8)
666    return E_INVALIDARG;
667  id = prop.uhVal.QuadPart;
668  return S_OK;
669}
670
671UString CCodecs::GetCodecName(UInt32 index)
672{
673  UString s;
674  NWindows::NCOM::CPropVariant prop;
675  if (GetProperty(index, NMethodPropID::kName, &prop) == S_OK)
676    if (prop.vt == VT_BSTR)
677      s = prop.bstrVal;
678  return s;
679}
680
681#endif
682