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