3007fbb72f23f8b77dcb3975a5d8c64c547e90e6
[vuplus_xbmc] / xbmc / addons / AddonDll.h
1 #pragma once
2 /*
3  *      Copyright (C) 2005-2013 Team XBMC
4  *      http://xbmc.org
5  *
6  *  This Program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2, or (at your option)
9  *  any later version.
10  *
11  *  This Program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with XBMC; see the file COPYING.  If not, see
18  *  <http://www.gnu.org/licenses/>.
19  *
20  */
21 #include "Addon.h"
22 #include "DllAddon.h"
23 #include "AddonManager.h"
24 #include "AddonStatusHandler.h"
25 #include "AddonCallbacks.h"
26 #include "settings/dialogs/GUIDialogSettings.h"
27 #include "utils/URIUtils.h"
28 #include "filesystem/File.h"
29 #include "filesystem/SpecialProtocol.h"
30 #include "filesystem/Directory.h"
31 #include "utils/log.h"
32 #include "interfaces/IAnnouncer.h"
33 #include "interfaces/AnnouncementManager.h"
34
35 using namespace XFILE;
36
37 namespace ADDON
38 {
39   template<class TheDll, typename TheStruct, typename TheProps>
40   class CAddonDll : public CAddon, public ANNOUNCEMENT::IAnnouncer
41   {
42   public:
43     CAddonDll(const AddonProps &props);
44     CAddonDll(const cp_extension_t *ext);
45     CAddonDll(const CAddonDll<TheDll, TheStruct, TheProps> &rhs);
46     virtual ~CAddonDll();
47     virtual AddonPtr Clone() const;
48     virtual ADDON_STATUS GetStatus();
49
50     // addon settings
51     virtual void SaveSettings();
52     virtual CStdString GetSetting(const CStdString& key);
53
54     ADDON_STATUS Create();
55     virtual void Stop();
56     virtual bool CheckAPIVersion(void) { return true; }
57     void Destroy();
58
59     bool DllLoaded(void) const;
60
61     void Announce(ANNOUNCEMENT::AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data);
62
63   protected:
64     void HandleException(std::exception &e, const char* context);
65     bool Initialized() { return m_initialized; }
66     virtual void BuildLibName(const cp_extension_t *ext = NULL) {}
67     virtual bool LoadSettings();
68     TheStruct* m_pStruct;
69     TheProps*     m_pInfo;
70     CAddonCallbacks* m_pHelpers;
71     bool m_bIsChild;
72
73   private:
74     TheDll* m_pDll;
75     bool m_initialized;
76     bool LoadDll();
77     bool m_needsavedsettings;
78
79     virtual ADDON_STATUS TransferSettings();
80     TiXmlElement MakeSetting(DllSetting& setting) const;
81
82     static void AddOnStatusCallback(void *userData, const ADDON_STATUS status, const char* msg);
83     static bool AddOnGetSetting(void *userData, const char *settingName, void *settingValue);
84     static void AddOnOpenSettings(const char *url, bool bReload);
85     static void AddOnOpenOwnSettings(void *userData, bool bReload);
86     static const char* AddOnGetAddonDirectory(void *userData);
87     static const char* AddOnGetUserDirectory(void *userData);
88   };
89
90 template<class TheDll, typename TheStruct, typename TheProps>
91 CAddonDll<TheDll, TheStruct, TheProps>::CAddonDll(const cp_extension_t *ext)
92   : CAddon(ext),
93     m_bIsChild(false)
94 {
95   // if library attribute isn't present, look for a system-dependent one
96   if (ext && m_strLibName.IsEmpty())
97   {
98 #if defined(TARGET_ANDROID)
99   m_strLibName = CAddonMgr::Get().GetExtValue(ext->configuration, "@library_android");
100 #elif defined(TARGET_LINUX) || defined(TARGET_FREEBSD)
101     m_strLibName = CAddonMgr::Get().GetExtValue(ext->configuration, "@library_linux");
102 #elif defined(TARGET_WINDOWS) && defined(HAS_SDL_OPENGL)
103     m_strLibName = CAddonMgr::Get().GetExtValue(ext->configuration, "@library_wingl");
104 #elif defined(TARGET_WINDOWS) && defined(HAS_DX)
105     m_strLibName = CAddonMgr::Get().GetExtValue(ext->configuration, "@library_windx");
106 #elif defined(TARGET_DARWIN)
107     m_strLibName = CAddonMgr::Get().GetExtValue(ext->configuration, "@library_osx");
108 #endif
109   }
110
111   m_pStruct     = NULL;
112   m_initialized = false;
113   m_pDll        = NULL;
114   m_pInfo       = NULL;
115   m_pHelpers    = NULL;
116   m_needsavedsettings = false;
117 }
118
119 template<class TheDll, typename TheStruct, typename TheProps>
120 CAddonDll<TheDll, TheStruct, TheProps>::CAddonDll(const AddonProps &props)
121   : CAddon(props),
122     m_bIsChild(false)
123 {
124   m_pStruct     = NULL;
125   m_initialized = false;
126   m_pDll        = NULL;
127   m_pInfo       = NULL;
128   m_pHelpers    = NULL;
129   m_needsavedsettings = false;
130 }
131
132 template<class TheDll, typename TheStruct, typename TheProps>
133 CAddonDll<TheDll, TheStruct, TheProps>::CAddonDll(const CAddonDll<TheDll, TheStruct, TheProps> &rhs)
134   : CAddon(rhs),
135     m_bIsChild(true)
136 {
137   m_pStruct           = rhs.m_pStruct;
138   m_initialized       = rhs.m_initialized;
139   m_pDll              = rhs.m_pDll;
140   m_pInfo             = rhs.m_pInfo;
141   m_pHelpers          = rhs.m_pHelpers;
142   m_needsavedsettings = rhs.m_needsavedsettings;
143 }
144
145 template<class TheDll, typename TheStruct, typename TheProps>
146 CAddonDll<TheDll, TheStruct, TheProps>::~CAddonDll()
147 {
148   if (m_initialized)
149     Destroy();
150 }
151
152 template<class TheDll, typename TheStruct, typename TheProps>
153 AddonPtr CAddonDll<TheDll, TheStruct, TheProps>::Clone() const
154 {
155   return AddonPtr(new CAddonDll<TheDll, TheStruct, TheProps>(*this));
156 }
157
158 template<class TheDll, typename TheStruct, typename TheProps>
159 bool CAddonDll<TheDll, TheStruct, TheProps>::LoadDll()
160 {
161   CStdString strFileName;
162   if (!m_bIsChild)
163   {
164     strFileName = LibPath();
165   }
166   else
167   { //FIXME hack to load same Dll twice
168     CStdString extension = URIUtils::GetExtension(m_strLibName);
169     strFileName = "special://temp/" + m_strLibName;
170     URIUtils::RemoveExtension(strFileName);
171     strFileName += "-" + ID() + extension;
172
173     if (!CFile::Exists(strFileName))
174       CFile::Cache(LibPath(), strFileName);
175
176     CLog::Log(LOGNOTICE, "ADDON: Loaded virtual child addon %s", strFileName.c_str());
177   }
178
179   /* Check if lib being loaded exists, else check in XBMC binary location */
180 #if defined(TARGET_ANDROID)
181   // Android libs MUST live in this path, else multi-arch will break.
182   // The usual soname requirements apply. no subdirs, and filename is ^lib.*\.so$
183   if (!CFile::Exists(strFileName))
184   {
185     CStdString tempbin = getenv("XBMC_ANDROID_LIBS");
186     strFileName = tempbin + "/" + m_strLibName;
187   }
188 #endif
189   if (!CFile::Exists(strFileName))
190   {
191     CStdString temp = CSpecialProtocol::TranslatePath("special://xbmc/");
192     CStdString tempbin = CSpecialProtocol::TranslatePath("special://xbmcbin/");
193     strFileName.erase(0, temp.size());
194     strFileName = tempbin + strFileName;
195     if (!CFile::Exists(strFileName))
196     {
197       CLog::Log(LOGERROR, "ADDON: Could not locate %s", m_strLibName.c_str());
198       return false;
199     }
200   }
201
202   /* Load the Dll */
203   m_pDll = new TheDll;
204   m_pDll->SetFile(strFileName);
205   m_pDll->EnableDelayedUnload(false);
206   if (!m_pDll->Load())
207   {
208     delete m_pDll;
209     m_pDll = NULL;
210     new CAddonStatusHandler(ID(), ADDON_STATUS_UNKNOWN, "Can't load Dll", false);
211     return false;
212   }
213
214   m_pStruct = (TheStruct*)malloc(sizeof(TheStruct));
215   if (m_pStruct)
216   {
217     ZeroMemory(m_pStruct, sizeof(TheStruct));
218     m_pDll->GetAddon(m_pStruct);
219     return true;
220   }
221
222   return false;
223 }
224
225 template<class TheDll, typename TheStruct, typename TheProps>
226 ADDON_STATUS CAddonDll<TheDll, TheStruct, TheProps>::Create()
227 {
228   ADDON_STATUS status(ADDON_STATUS_UNKNOWN);
229   CLog::Log(LOGDEBUG, "ADDON: Dll Initializing - %s", Name().c_str());
230   m_initialized = false;
231
232   if (!LoadDll() || !CheckAPIVersion())
233     return ADDON_STATUS_PERMANENT_FAILURE;
234
235   /* Allocate the helper function class to allow crosstalk over
236      helper libraries */
237   m_pHelpers = new CAddonCallbacks(this);
238
239   /* Call Create to make connections, initializing data or whatever is
240      needed to become the AddOn running */
241   try
242   {
243     status = m_pDll->Create(m_pHelpers->GetCallbacks(), m_pInfo);
244     if (status == ADDON_STATUS_OK)
245     {
246       m_initialized = true;
247       ANNOUNCEMENT::CAnnouncementManager::AddAnnouncer(this);
248     }
249     else if ((status == ADDON_STATUS_NEED_SETTINGS) || (status == ADDON_STATUS_NEED_SAVEDSETTINGS))
250     {
251       m_needsavedsettings = (status == ADDON_STATUS_NEED_SAVEDSETTINGS);
252       if ((status = TransferSettings()) == ADDON_STATUS_OK)
253         m_initialized = true;
254       else
255         new CAddonStatusHandler(ID(), status, "", false);
256     }
257     else
258     { // Addon failed initialization
259       CLog::Log(LOGERROR, "ADDON: Dll %s - Client returned bad status (%i) from Create and is not usable", Name().c_str(), status);
260       new CAddonStatusHandler(ID(), status, "", false);
261     }
262   }
263   catch (std::exception &e)
264   {
265     HandleException(e, "m_pDll->Create");
266   }
267
268   if (!m_initialized)
269     SAFE_DELETE(m_pHelpers);
270
271   return status;
272 }
273
274 template<class TheDll, typename TheStruct, typename TheProps>
275 void CAddonDll<TheDll, TheStruct, TheProps>::Stop()
276 {
277   /* Inform dll to stop all activities */
278   try
279   {
280     if (m_needsavedsettings)  // If the addon supports it we save some settings to settings.xml before stop
281     {
282       char   str_id[64] = "";
283       char   str_value[1024];
284       CAddon::LoadUserSettings();
285       for (unsigned int i=0; (strcmp(str_id,"###End") != 0); i++)
286       {
287         strcpy(str_id, "###GetSavedSettings");
288         sprintf (str_value, "%i", i);
289         ADDON_STATUS status = m_pDll->SetSetting((const char*)&str_id, (void*)&str_value);
290
291         if (status == ADDON_STATUS_UNKNOWN)
292           break;
293
294         if (strcmp(str_id,"###End") != 0) UpdateSetting(str_id, str_value);
295       }
296       CAddon::SaveSettings();
297     }
298     if (m_pDll)
299     {
300       m_pDll->Stop();
301       CLog::Log(LOGINFO, "ADDON: Dll Stopped - %s", Name().c_str());
302     }
303   }
304   catch (std::exception &e)
305   {
306     HandleException(e, "m_pDll->Stop");
307   }
308 }
309
310 template<class TheDll, typename TheStruct, typename TheProps>
311 void CAddonDll<TheDll, TheStruct, TheProps>::Destroy()
312 {
313   ANNOUNCEMENT::CAnnouncementManager::RemoveAnnouncer(this);
314
315   /* Unload library file */
316   try
317   {
318     if (m_pDll)
319     {
320       m_pDll->Destroy();
321       m_pDll->Unload();
322     }
323   }
324   catch (std::exception &e)
325   {
326     HandleException(e, "m_pDll->Unload");
327   }
328   delete m_pHelpers;
329   m_pHelpers = NULL;
330   free(m_pStruct);
331   m_pStruct = NULL;
332   if (m_pDll)
333   {
334     delete m_pDll;
335     m_pDll = NULL;
336     CLog::Log(LOGINFO, "ADDON: Dll Destroyed - %s", Name().c_str());
337   }
338   m_initialized = false;
339 }
340
341 template<class TheDll, typename TheStruct, typename TheProps>
342 bool CAddonDll<TheDll, TheStruct, TheProps>::DllLoaded(void) const
343 {
344   return m_pDll != NULL;
345 }
346
347 template<class TheDll, typename TheStruct, typename TheProps>
348 ADDON_STATUS CAddonDll<TheDll, TheStruct, TheProps>::GetStatus()
349 {
350   try
351   {
352     return m_pDll->GetStatus();
353   }
354   catch (std::exception &e)
355   {
356     HandleException(e, "m_pDll->GetStatus()");
357   }
358   return ADDON_STATUS_UNKNOWN;
359 }
360
361 template<class TheDll, typename TheStruct, typename TheProps>
362 bool CAddonDll<TheDll, TheStruct, TheProps>::LoadSettings()
363 {
364   if (m_settingsLoaded)
365     return true;
366
367   if (!LoadDll())
368     return false;
369
370   ADDON_StructSetting** sSet;
371   std::vector<DllSetting> vSet;
372   unsigned entries = 0;
373   try
374   {
375     entries = m_pDll->GetSettings(&sSet);
376     DllUtils::StructToVec(entries, &sSet, &vSet);
377     m_pDll->FreeSettings();
378   }
379   catch (std::exception &e)
380   {
381     HandleException(e, "m_pDll->GetSettings()");
382     return false;
383   }
384
385   if (vSet.size())
386   {
387     // regenerate XML doc
388     m_addonXmlDoc.Clear();
389     TiXmlElement node("settings");
390     m_addonXmlDoc.InsertEndChild(node);
391
392     for (unsigned i=0; i < entries; i++)
393     {
394        DllSetting& setting = vSet[i];
395        m_addonXmlDoc.RootElement()->InsertEndChild(MakeSetting(setting));
396     }
397     CAddon::SettingsFromXML(m_addonXmlDoc, true);
398   }
399   else
400     return CAddon::LoadSettings();
401
402   m_settingsLoaded = true;
403   CAddon::LoadUserSettings();
404   return true;
405 }
406
407 template<class TheDll, typename TheStruct, typename TheProps>
408 TiXmlElement CAddonDll<TheDll, TheStruct, TheProps>::MakeSetting(DllSetting& setting) const
409 {
410   TiXmlElement node("setting");
411
412   switch (setting.type)
413   {
414     case DllSetting::CHECK:
415     {
416       node.SetAttribute("id", setting.id);
417       node.SetAttribute("type", "bool");
418       node.SetAttribute("label", setting.label);
419       break;
420     }
421     case DllSetting::SPIN:
422     {
423       node.SetAttribute("id", setting.id);
424       node.SetAttribute("type", "enum");
425       node.SetAttribute("label", setting.label);
426       CStdString values;
427       for (unsigned int i = 0; i < setting.entry.size(); i++)
428       {
429         values.append(setting.entry[i]);
430         values.append("|");
431       }
432       node.SetAttribute("values", values.c_str());
433       break;
434     }
435   default:
436     break;
437   }
438
439   return node;
440 }
441
442 template<class TheDll, typename TheStruct, typename TheProps>
443 void CAddonDll<TheDll, TheStruct, TheProps>::SaveSettings()
444 {
445   // must save first, as TransferSettings() reloads saved settings!
446   CAddon::SaveSettings();
447   if (m_initialized)
448     TransferSettings();
449 }
450
451 template<class TheDll, typename TheStruct, typename TheProps>
452 CStdString CAddonDll<TheDll, TheStruct, TheProps>::GetSetting(const CStdString& key)
453 {
454   return CAddon::GetSetting(key);
455 }
456
457 template<class TheDll, typename TheStruct, typename TheProps>
458 ADDON_STATUS CAddonDll<TheDll, TheStruct, TheProps>::TransferSettings()
459 {
460   bool restart = false;
461   ADDON_STATUS reportStatus = ADDON_STATUS_OK;
462
463   CLog::Log(LOGDEBUG, "Calling TransferSettings for: %s", Name().c_str());
464
465   LoadSettings();
466
467   const TiXmlElement *category = m_addonXmlDoc.RootElement() ? m_addonXmlDoc.RootElement()->FirstChildElement("category") : NULL;
468   if (!category)
469     category = m_addonXmlDoc.RootElement(); // no categories
470
471   while (category)
472   {
473     const TiXmlElement *setting = category->FirstChildElement("setting");
474     while (setting)
475     {
476       ADDON_STATUS status = ADDON_STATUS_OK;
477       const char *id = setting->Attribute("id");
478       const char *type = setting->Attribute("type");
479       const char *option = setting->Attribute("option");
480
481       if (type)
482       {
483         if (strcmpi(type,"sep") == 0 || strcmpi(type,"lsep") == 0)
484         {
485           /* Don't propagate separators */
486         }
487         else if (strcmpi(type, "text") == 0 || strcmpi(type, "ipaddress") == 0 ||
488           strcmpi(type, "video") == 0 || strcmpi(type, "audio") == 0 ||
489           strcmpi(type, "image") == 0 || strcmpi(type, "folder") == 0 ||
490           strcmpi(type, "executable") == 0 || strcmpi(type, "file") == 0 ||
491           strcmpi(type, "action") == 0 || strcmpi(type, "date") == 0 ||
492           strcmpi(type, "time") == 0 || strcmpi(type, "select") == 0 ||
493           strcmpi(type, "addon") == 0 || strcmpi(type, "labelenum") == 0 ||
494           strcmpi(type, "fileenum") == 0 )
495         {
496           status = m_pDll->SetSetting(id, (const char*) GetSetting(id).c_str());
497         }
498         else if ((strcmpi(type, "enum") == 0 || strcmpi(type,"integer") == 0) ||
499           strcmpi(type, "labelenum") == 0 || strcmpi(type, "rangeofnum") == 0)
500         {
501           int tmp = atoi(GetSetting(id));
502           status = m_pDll->SetSetting(id, (int*) &tmp);
503         }
504         else if (strcmpi(type, "bool") == 0)
505         {
506           bool tmp = (GetSetting(id) == "true") ? true : false;
507           status = m_pDll->SetSetting(id, (bool*) &tmp);
508         }
509         else if (strcmpi(type, "rangeofnum") == 0 || strcmpi(type, "slider") == 0 ||
510                  strcmpi(type, "number") == 0)
511         {
512           float tmpf = (float)atof(GetSetting(id));
513           int   tmpi;
514
515           if (option && strcmpi(option,"int") == 0)
516           {
517             tmpi = (int)floor(tmpf);
518             status = m_pDll->SetSetting(id, (int*) &tmpi);
519           }
520           else
521           {
522             status = m_pDll->SetSetting(id, (float*) &tmpf);
523           }
524         }
525         else
526         {
527           /* Log unknowns as an error, but go ahead and transfer the string */
528           CLog::Log(LOGERROR, "Unknown setting type '%s' for %s", type, Name().c_str());
529           status = m_pDll->SetSetting(id, (const char*) GetSetting(id).c_str());
530         }
531
532         if (status == ADDON_STATUS_NEED_RESTART)
533           restart = true;
534         else if (status != ADDON_STATUS_OK)
535           reportStatus = status;
536       }
537       setting = setting->NextSiblingElement("setting");
538     }
539     category = category->NextSiblingElement("category");
540   }
541
542   if (restart || reportStatus != ADDON_STATUS_OK)
543   {
544     new CAddonStatusHandler(ID(), restart ? ADDON_STATUS_NEED_RESTART : reportStatus, "", true);
545   }
546
547   return ADDON_STATUS_OK;
548 }
549
550 template<class TheDll, typename TheStruct, typename TheProps>
551 void CAddonDll<TheDll, TheStruct, TheProps>::Announce(ANNOUNCEMENT::AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data)
552 {
553   try
554   {
555     m_pDll->Announce(ANNOUNCEMENT::AnnouncementFlagToString(flag), sender, message, &data);
556   }
557   catch (std::exception &e)
558   {
559     HandleException(e, "m_pDll->Announce()");
560   }
561 }
562
563 template<class TheDll, typename TheStruct, typename TheProps>
564 void CAddonDll<TheDll, TheStruct, TheProps>::HandleException(std::exception &e, const char* context)
565 {
566   m_initialized = false;
567   m_pDll->Unload();
568   CLog::Log(LOGERROR, "ADDON: Dll %s, throws an exception '%s' during %s. Contact developer '%s' with bug reports", Name().c_str(), e.what(), context, Author().c_str());
569 }
570
571 }; /* namespace ADDON */
572