3 * Copyright (C) 2005-2013 Team XBMC
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)
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.
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/>.
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"
35 using namespace XFILE;
39 template<class TheDll, typename TheStruct, typename TheProps>
40 class CAddonDll : public CAddon, public ANNOUNCEMENT::IAnnouncer
43 CAddonDll(const AddonProps &props);
44 CAddonDll(const cp_extension_t *ext);
45 CAddonDll(const CAddonDll<TheDll, TheStruct, TheProps> &rhs);
47 virtual AddonPtr Clone() const;
48 virtual ADDON_STATUS GetStatus();
51 virtual void SaveSettings();
52 virtual CStdString GetSetting(const CStdString& key);
54 ADDON_STATUS Create();
56 virtual bool CheckAPIVersion(void) { return true; }
59 bool DllLoaded(void) const;
61 void Announce(ANNOUNCEMENT::AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data);
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();
70 CAddonCallbacks* m_pHelpers;
77 bool m_needsavedsettings;
79 virtual ADDON_STATUS TransferSettings();
80 TiXmlElement MakeSetting(DllSetting& setting) const;
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);
90 template<class TheDll, typename TheStruct, typename TheProps>
91 CAddonDll<TheDll, TheStruct, TheProps>::CAddonDll(const cp_extension_t *ext)
95 // if library attribute isn't present, look for a system-dependent one
96 if (ext && m_strLibName.IsEmpty())
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");
112 m_initialized = false;
116 m_needsavedsettings = false;
119 template<class TheDll, typename TheStruct, typename TheProps>
120 CAddonDll<TheDll, TheStruct, TheProps>::CAddonDll(const AddonProps &props)
125 m_initialized = false;
129 m_needsavedsettings = false;
132 template<class TheDll, typename TheStruct, typename TheProps>
133 CAddonDll<TheDll, TheStruct, TheProps>::CAddonDll(const CAddonDll<TheDll, TheStruct, TheProps> &rhs)
137 m_pStruct = rhs.m_pStruct;
138 m_initialized = rhs.m_initialized;
140 m_pInfo = rhs.m_pInfo;
141 m_pHelpers = rhs.m_pHelpers;
142 m_needsavedsettings = rhs.m_needsavedsettings;
145 template<class TheDll, typename TheStruct, typename TheProps>
146 CAddonDll<TheDll, TheStruct, TheProps>::~CAddonDll()
152 template<class TheDll, typename TheStruct, typename TheProps>
153 AddonPtr CAddonDll<TheDll, TheStruct, TheProps>::Clone() const
155 return AddonPtr(new CAddonDll<TheDll, TheStruct, TheProps>(*this));
158 template<class TheDll, typename TheStruct, typename TheProps>
159 bool CAddonDll<TheDll, TheStruct, TheProps>::LoadDll()
161 CStdString strFileName;
164 strFileName = LibPath();
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;
173 if (!CFile::Exists(strFileName))
174 CFile::Cache(LibPath(), strFileName);
176 CLog::Log(LOGNOTICE, "ADDON: Loaded virtual child addon %s", strFileName.c_str());
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))
185 CStdString tempbin = getenv("XBMC_ANDROID_LIBS");
186 strFileName = tempbin + "/" + m_strLibName;
189 if (!CFile::Exists(strFileName))
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))
197 CLog::Log(LOGERROR, "ADDON: Could not locate %s", m_strLibName.c_str());
204 m_pDll->SetFile(strFileName);
205 m_pDll->EnableDelayedUnload(false);
210 new CAddonStatusHandler(ID(), ADDON_STATUS_UNKNOWN, "Can't load Dll", false);
214 m_pStruct = (TheStruct*)malloc(sizeof(TheStruct));
217 ZeroMemory(m_pStruct, sizeof(TheStruct));
218 m_pDll->GetAddon(m_pStruct);
225 template<class TheDll, typename TheStruct, typename TheProps>
226 ADDON_STATUS CAddonDll<TheDll, TheStruct, TheProps>::Create()
228 ADDON_STATUS status(ADDON_STATUS_UNKNOWN);
229 CLog::Log(LOGDEBUG, "ADDON: Dll Initializing - %s", Name().c_str());
230 m_initialized = false;
232 if (!LoadDll() || !CheckAPIVersion())
233 return ADDON_STATUS_PERMANENT_FAILURE;
235 /* Allocate the helper function class to allow crosstalk over
237 m_pHelpers = new CAddonCallbacks(this);
239 /* Call Create to make connections, initializing data or whatever is
240 needed to become the AddOn running */
243 status = m_pDll->Create(m_pHelpers->GetCallbacks(), m_pInfo);
244 if (status == ADDON_STATUS_OK)
246 m_initialized = true;
247 ANNOUNCEMENT::CAnnouncementManager::AddAnnouncer(this);
249 else if ((status == ADDON_STATUS_NEED_SETTINGS) || (status == ADDON_STATUS_NEED_SAVEDSETTINGS))
251 m_needsavedsettings = (status == ADDON_STATUS_NEED_SAVEDSETTINGS);
252 if ((status = TransferSettings()) == ADDON_STATUS_OK)
253 m_initialized = true;
255 new CAddonStatusHandler(ID(), status, "", false);
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);
263 catch (std::exception &e)
265 HandleException(e, "m_pDll->Create");
269 SAFE_DELETE(m_pHelpers);
274 template<class TheDll, typename TheStruct, typename TheProps>
275 void CAddonDll<TheDll, TheStruct, TheProps>::Stop()
277 /* Inform dll to stop all activities */
280 if (m_needsavedsettings) // If the addon supports it we save some settings to settings.xml before stop
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++)
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);
291 if (status == ADDON_STATUS_UNKNOWN)
294 if (strcmp(str_id,"###End") != 0) UpdateSetting(str_id, str_value);
296 CAddon::SaveSettings();
301 CLog::Log(LOGINFO, "ADDON: Dll Stopped - %s", Name().c_str());
304 catch (std::exception &e)
306 HandleException(e, "m_pDll->Stop");
310 template<class TheDll, typename TheStruct, typename TheProps>
311 void CAddonDll<TheDll, TheStruct, TheProps>::Destroy()
313 ANNOUNCEMENT::CAnnouncementManager::RemoveAnnouncer(this);
315 /* Unload library file */
324 catch (std::exception &e)
326 HandleException(e, "m_pDll->Unload");
336 CLog::Log(LOGINFO, "ADDON: Dll Destroyed - %s", Name().c_str());
338 m_initialized = false;
341 template<class TheDll, typename TheStruct, typename TheProps>
342 bool CAddonDll<TheDll, TheStruct, TheProps>::DllLoaded(void) const
344 return m_pDll != NULL;
347 template<class TheDll, typename TheStruct, typename TheProps>
348 ADDON_STATUS CAddonDll<TheDll, TheStruct, TheProps>::GetStatus()
352 return m_pDll->GetStatus();
354 catch (std::exception &e)
356 HandleException(e, "m_pDll->GetStatus()");
358 return ADDON_STATUS_UNKNOWN;
361 template<class TheDll, typename TheStruct, typename TheProps>
362 bool CAddonDll<TheDll, TheStruct, TheProps>::LoadSettings()
364 if (m_settingsLoaded)
370 ADDON_StructSetting** sSet;
371 std::vector<DllSetting> vSet;
372 unsigned entries = 0;
375 entries = m_pDll->GetSettings(&sSet);
376 DllUtils::StructToVec(entries, &sSet, &vSet);
377 m_pDll->FreeSettings();
379 catch (std::exception &e)
381 HandleException(e, "m_pDll->GetSettings()");
387 // regenerate XML doc
388 m_addonXmlDoc.Clear();
389 TiXmlElement node("settings");
390 m_addonXmlDoc.InsertEndChild(node);
392 for (unsigned i=0; i < entries; i++)
394 DllSetting& setting = vSet[i];
395 m_addonXmlDoc.RootElement()->InsertEndChild(MakeSetting(setting));
397 CAddon::SettingsFromXML(m_addonXmlDoc, true);
400 return CAddon::LoadSettings();
402 m_settingsLoaded = true;
403 CAddon::LoadUserSettings();
407 template<class TheDll, typename TheStruct, typename TheProps>
408 TiXmlElement CAddonDll<TheDll, TheStruct, TheProps>::MakeSetting(DllSetting& setting) const
410 TiXmlElement node("setting");
412 switch (setting.type)
414 case DllSetting::CHECK:
416 node.SetAttribute("id", setting.id);
417 node.SetAttribute("type", "bool");
418 node.SetAttribute("label", setting.label);
421 case DllSetting::SPIN:
423 node.SetAttribute("id", setting.id);
424 node.SetAttribute("type", "enum");
425 node.SetAttribute("label", setting.label);
427 for (unsigned int i = 0; i < setting.entry.size(); i++)
429 values.append(setting.entry[i]);
432 node.SetAttribute("values", values.c_str());
442 template<class TheDll, typename TheStruct, typename TheProps>
443 void CAddonDll<TheDll, TheStruct, TheProps>::SaveSettings()
445 // must save first, as TransferSettings() reloads saved settings!
446 CAddon::SaveSettings();
451 template<class TheDll, typename TheStruct, typename TheProps>
452 CStdString CAddonDll<TheDll, TheStruct, TheProps>::GetSetting(const CStdString& key)
454 return CAddon::GetSetting(key);
457 template<class TheDll, typename TheStruct, typename TheProps>
458 ADDON_STATUS CAddonDll<TheDll, TheStruct, TheProps>::TransferSettings()
460 bool restart = false;
461 ADDON_STATUS reportStatus = ADDON_STATUS_OK;
463 CLog::Log(LOGDEBUG, "Calling TransferSettings for: %s", Name().c_str());
467 const TiXmlElement *category = m_addonXmlDoc.RootElement() ? m_addonXmlDoc.RootElement()->FirstChildElement("category") : NULL;
469 category = m_addonXmlDoc.RootElement(); // no categories
473 const TiXmlElement *setting = category->FirstChildElement("setting");
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");
483 if (strcmpi(type,"sep") == 0 || strcmpi(type,"lsep") == 0)
485 /* Don't propagate separators */
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 )
496 status = m_pDll->SetSetting(id, (const char*) GetSetting(id).c_str());
498 else if ((strcmpi(type, "enum") == 0 || strcmpi(type,"integer") == 0) ||
499 strcmpi(type, "labelenum") == 0 || strcmpi(type, "rangeofnum") == 0)
501 int tmp = atoi(GetSetting(id));
502 status = m_pDll->SetSetting(id, (int*) &tmp);
504 else if (strcmpi(type, "bool") == 0)
506 bool tmp = (GetSetting(id) == "true") ? true : false;
507 status = m_pDll->SetSetting(id, (bool*) &tmp);
509 else if (strcmpi(type, "rangeofnum") == 0 || strcmpi(type, "slider") == 0 ||
510 strcmpi(type, "number") == 0)
512 float tmpf = (float)atof(GetSetting(id));
515 if (option && strcmpi(option,"int") == 0)
517 tmpi = (int)floor(tmpf);
518 status = m_pDll->SetSetting(id, (int*) &tmpi);
522 status = m_pDll->SetSetting(id, (float*) &tmpf);
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());
532 if (status == ADDON_STATUS_NEED_RESTART)
534 else if (status != ADDON_STATUS_OK)
535 reportStatus = status;
537 setting = setting->NextSiblingElement("setting");
539 category = category->NextSiblingElement("category");
542 if (restart || reportStatus != ADDON_STATUS_OK)
544 new CAddonStatusHandler(ID(), restart ? ADDON_STATUS_NEED_RESTART : reportStatus, "", true);
547 return ADDON_STATUS_OK;
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)
555 m_pDll->Announce(ANNOUNCEMENT::AnnouncementFlagToString(flag), sender, message, &data);
557 catch (std::exception &e)
559 HandleException(e, "m_pDll->Announce()");
563 template<class TheDll, typename TheStruct, typename TheProps>
564 void CAddonDll<TheDll, TheStruct, TheProps>::HandleException(std::exception &e, const char* context)
566 m_initialized = false;
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());
571 }; /* namespace ADDON */