summaryrefslogtreecommitdiffstats
path: root/xbmc/addons/AddonDll.h
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/addons/AddonDll.h')
-rw-r--r--xbmc/addons/AddonDll.h572
1 files changed, 572 insertions, 0 deletions
diff --git a/xbmc/addons/AddonDll.h b/xbmc/addons/AddonDll.h
new file mode 100644
index 0000000..d5e4e9e
--- /dev/null
+++ b/xbmc/addons/AddonDll.h
@@ -0,0 +1,572 @@
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 <math.h>
22#include <string>
23
24#include "Addon.h"
25#include "DllAddon.h"
26#include "AddonManager.h"
27#include "AddonStatusHandler.h"
28#include "AddonCallbacks.h"
29#include "utils/URIUtils.h"
30#include "filesystem/File.h"
31#include "filesystem/SpecialProtocol.h"
32#include "filesystem/Directory.h"
33#include "utils/log.h"
34#include "interfaces/IAnnouncer.h"
35#include "interfaces/AnnouncementManager.h"
36#include "utils/XMLUtils.h"
37#include "Util.h"
38
39namespace ADDON
40{
41 template<class TheDll, typename TheStruct, typename TheProps>
42 class CAddonDll : public CAddon, public ANNOUNCEMENT::IAnnouncer
43 {
44 public:
45 CAddonDll(const AddonProps &props);
46 CAddonDll(const cp_extension_t *ext);
47 CAddonDll(const CAddonDll<TheDll, TheStruct, TheProps> &rhs);
48 virtual ~CAddonDll();
49 virtual AddonPtr Clone() const;
50 virtual ADDON_STATUS GetStatus();
51
52 // addon settings
53 virtual void SaveSettings();
54 virtual std::string GetSetting(const std::string& key);
55
56 ADDON_STATUS Create();
57 virtual void Stop();
58 virtual bool CheckAPIVersion(void) { return true; }
59 void Destroy();
60
61 bool DllLoaded(void) const;
62
63 void Announce(ANNOUNCEMENT::AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data);
64
65 protected:
66 void HandleException(std::exception &e, const char* context);
67 bool Initialized() { return m_initialized; }
68 virtual void BuildLibName(const cp_extension_t *ext = NULL) {}
69 virtual bool LoadSettings();
70 TheStruct* m_pStruct;
71 TheProps* m_pInfo;
72 CAddonCallbacks* m_pHelpers;
73 bool m_bIsChild;
74
75 private:
76 TheDll* m_pDll;
77 bool m_initialized;
78 bool LoadDll();
79 bool m_needsavedsettings;
80
81 virtual ADDON_STATUS TransferSettings();
82 TiXmlElement MakeSetting(DllSetting& setting) const;
83
84 static void AddOnStatusCallback(void *userData, const ADDON_STATUS status, const char* msg);
85 static bool AddOnGetSetting(void *userData, const char *settingName, void *settingValue);
86 static void AddOnOpenSettings(const char *url, bool bReload);
87 static void AddOnOpenOwnSettings(void *userData, bool bReload);
88 static const char* AddOnGetAddonDirectory(void *userData);
89 static const char* AddOnGetUserDirectory(void *userData);
90 };
91
92template<class TheDll, typename TheStruct, typename TheProps>
93CAddonDll<TheDll, TheStruct, TheProps>::CAddonDll(const cp_extension_t *ext)
94 : CAddon(ext),
95 m_bIsChild(false)
96{
97 // if library attribute isn't present, look for a system-dependent one
98 if (ext && m_strLibName.empty())
99 {
100#if defined(TARGET_ANDROID)
101 m_strLibName = CAddonMgr::Get().GetExtValue(ext->configuration, "@library_android");
102#elif defined(TARGET_LINUX) || defined(TARGET_FREEBSD)
103 m_strLibName = CAddonMgr::Get().GetExtValue(ext->configuration, "@library_linux");
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
119template<class TheDll, typename TheStruct, typename TheProps>
120CAddonDll<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
132template<class TheDll, typename TheStruct, typename TheProps>
133CAddonDll<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
145template<class TheDll, typename TheStruct, typename TheProps>
146CAddonDll<TheDll, TheStruct, TheProps>::~CAddonDll()
147{
148 if (m_initialized)
149 Destroy();
150}
151
152template<class TheDll, typename TheStruct, typename TheProps>
153AddonPtr CAddonDll<TheDll, TheStruct, TheProps>::Clone() const
154{
155 return AddonPtr(new CAddonDll<TheDll, TheStruct, TheProps>(*this));
156}
157
158template<class TheDll, typename TheStruct, typename TheProps>
159bool CAddonDll<TheDll, TheStruct, TheProps>::LoadDll()
160{
161 std::string strFileName;
162 if (!m_bIsChild)
163 {
164 strFileName = LibPath();
165 }
166 else
167 { //FIXME hack to load same Dll twice
168 std::string extension = URIUtils::GetExtension(m_strLibName);
169 strFileName = "special://temp/" + m_strLibName;
170 URIUtils::RemoveExtension(strFileName);
171 strFileName += "-" + ID() + extension;
172
173 if (!XFILE::CFile::Exists(strFileName))
174 XFILE::CFile::Copy(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 (!XFILE::CFile::Exists(strFileName))
184 {
185 std::string tempbin = getenv("XBMC_ANDROID_LIBS");
186 strFileName = tempbin + "/" + m_strLibName;
187 }
188#endif
189 if (!XFILE::CFile::Exists(strFileName))
190 {
191 std::string temp = CSpecialProtocol::TranslatePath("special://xbmc/");
192 std::string tempbin = CSpecialProtocol::TranslatePath("special://xbmcbin/");
193 strFileName.erase(0, temp.size());
194 strFileName = tempbin + strFileName;
195 if (!XFILE::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
225template<class TheDll, typename TheStruct, typename TheProps>
226ADDON_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::Get().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
274template<class TheDll, typename TheStruct, typename TheProps>
275void 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
310template<class TheDll, typename TheStruct, typename TheProps>
311void CAddonDll<TheDll, TheStruct, TheProps>::Destroy()
312{
313 ANNOUNCEMENT::CAnnouncementManager::Get().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
341template<class TheDll, typename TheStruct, typename TheProps>
342bool CAddonDll<TheDll, TheStruct, TheProps>::DllLoaded(void) const
343{
344 return m_pDll != NULL;
345}
346
347template<class TheDll, typename TheStruct, typename TheProps>
348ADDON_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
361template<class TheDll, typename TheStruct, typename TheProps>
362bool 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
407template<class TheDll, typename TheStruct, typename TheProps>
408TiXmlElement 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 std::string 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
442template<class TheDll, typename TheStruct, typename TheProps>
443void 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
451template<class TheDll, typename TheStruct, typename TheProps>
452std::string CAddonDll<TheDll, TheStruct, TheProps>::GetSetting(const std::string& key)
453{
454 return CAddon::GetSetting(key);
455}
456
457template<class TheDll, typename TheStruct, typename TheProps>
458ADDON_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 std::string type = XMLUtils::GetAttribute(setting, "type");
479 const char *option = setting->Attribute("option");
480
481 if (id && !type.empty())
482 {
483 if (type == "sep" || type == "lsep")
484 {
485 /* Don't propagate separators */
486 }
487 else if (type == "text" || type == "ipaddress" ||
488 type == "video" || type == "audio" ||
489 type == "image" || type == "folder" ||
490 type == "executable" || type == "file" ||
491 type == "action" || type == "date" ||
492 type == "time" || type == "select" ||
493 type == "addon" || type == "labelenum" ||
494 type == "fileenum" )
495 {
496 status = m_pDll->SetSetting(id, (const char*) GetSetting(id).c_str());
497 }
498 else if (type == "enum" || type =="integer" ||
499 type == "labelenum" || type == "rangeofnum")
500 {
501 int tmp = atoi(GetSetting(id).c_str());
502 status = m_pDll->SetSetting(id, (int*) &tmp);
503 }
504 else if (type == "bool")
505 {
506 bool tmp = (GetSetting(id) == "true") ? true : false;
507 status = m_pDll->SetSetting(id, (bool*) &tmp);
508 }
509 else if (type == "rangeofnum" || type == "slider" ||
510 type == "number")
511 {
512 float tmpf = (float)atof(GetSetting(id).c_str());
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.c_str(), 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
550template<class TheDll, typename TheStruct, typename TheProps>
551void 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
563template<class TheDll, typename TheStruct, typename TheProps>
564void 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