2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "AddonInfoBuilder.h"
11 #include "CompileInfo.h"
13 #include "addons/Repository.h"
14 #include "addons/addoninfo/AddonInfo.h"
15 #include "addons/addoninfo/AddonType.h"
16 #include "filesystem/File.h"
17 #include "filesystem/SpecialProtocol.h"
18 #include "utils/JSONVariantParser.h"
19 #include "utils/JSONVariantWriter.h"
20 #include "utils/StringUtils.h"
21 #include "utils/URIUtils.h"
22 #include "utils/Variant.h"
23 #include "utils/XBMCTinyXML.h"
24 #include "utils/log.h"
32 // Note that all of these characters are url-safe
33 const std::string VALID_ADDON_IDENTIFIER_CHARACTERS
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_@!$";
39 CAddonInfoBuilderFromDB::CAddonInfoBuilderFromDB() : m_addonInfo(std::make_shared
<CAddonInfo
>())
43 void CAddonInfoBuilderFromDB::SetId(std::string id
)
45 m_addonInfo
->m_id
= std::move(id
);
48 void CAddonInfoBuilderFromDB::SetName(std::string name
)
50 m_addonInfo
->m_name
= std::move(name
);
53 void CAddonInfoBuilderFromDB::SetLicense(std::string license
)
55 m_addonInfo
->m_license
= std::move(license
);
58 void CAddonInfoBuilderFromDB::SetSummary(std::string summary
)
60 m_addonInfo
->m_summary
.insert(std::pair
<std::string
, std::string
>("unk", std::move(summary
)));
63 void CAddonInfoBuilderFromDB::SetDescription(std::string description
)
65 m_addonInfo
->m_description
.insert(
66 std::pair
<std::string
, std::string
>("unk", std::move(description
)));
69 void CAddonInfoBuilderFromDB::SetDisclaimer(std::string disclaimer
)
71 m_addonInfo
->m_disclaimer
.insert(
72 std::pair
<std::string
, std::string
>("unk", std::move(disclaimer
)));
75 void CAddonInfoBuilderFromDB::SetAuthor(std::string author
)
77 m_addonInfo
->m_author
= std::move(author
);
80 void CAddonInfoBuilderFromDB::SetSource(std::string source
)
82 m_addonInfo
->m_source
= std::move(source
);
85 void CAddonInfoBuilderFromDB::SetWebsite(std::string website
)
87 m_addonInfo
->m_website
= std::move(website
);
90 void CAddonInfoBuilderFromDB::SetForum(std::string forum
)
92 m_addonInfo
->m_forum
= std::move(forum
);
95 void CAddonInfoBuilderFromDB::SetEMail(std::string email
)
97 m_addonInfo
->m_email
= std::move(email
);
100 void CAddonInfoBuilderFromDB::SetIcon(std::string icon
)
102 m_addonInfo
->m_icon
= std::move(icon
);
105 void CAddonInfoBuilderFromDB::SetArt(const std::string
& type
, std::string value
)
107 m_addonInfo
->m_art
[type
] = std::move(value
);
110 void CAddonInfoBuilderFromDB::SetArt(std::map
<std::string
, std::string
> art
)
112 m_addonInfo
->m_art
= std::move(art
);
115 void CAddonInfoBuilderFromDB::SetScreenshots(std::vector
<std::string
> screenshots
)
117 m_addonInfo
->m_screenshots
= std::move(screenshots
);
120 void CAddonInfoBuilderFromDB::SetChangelog(std::string changelog
)
122 m_addonInfo
->m_changelog
.insert(std::pair
<std::string
, std::string
>("unk", std::move(changelog
)));
125 void CAddonInfoBuilderFromDB::SetLifecycleState(AddonLifecycleState state
, std::string description
)
127 m_addonInfo
->m_lifecycleState
= state
;
128 m_addonInfo
->m_lifecycleStateDescription
.emplace("unk", std::move(description
));
131 void CAddonInfoBuilderFromDB::SetPath(std::string path
)
133 m_addonInfo
->m_path
= std::move(path
);
136 void CAddonInfoBuilderFromDB::SetLibName(std::string libname
)
138 m_addonInfo
->m_libname
= std::move(libname
);
141 void CAddonInfoBuilderFromDB::SetVersion(CAddonVersion version
)
143 m_addonInfo
->m_version
= std::move(version
);
146 void CAddonInfoBuilderFromDB::SetDependencies(std::vector
<DependencyInfo
> dependencies
)
148 m_addonInfo
->m_dependencies
= std::move(dependencies
);
151 void CAddonInfoBuilderFromDB::SetExtrainfo(InfoMap extrainfo
)
153 m_addonInfo
->m_extrainfo
= std::move(extrainfo
);
156 void CAddonInfoBuilderFromDB::SetInstallDate(const CDateTime
& installDate
)
158 m_addonInfo
->m_installDate
= installDate
;
161 void CAddonInfoBuilderFromDB::SetLastUpdated(const CDateTime
& lastUpdated
)
163 m_addonInfo
->m_lastUpdated
= lastUpdated
;
166 void CAddonInfoBuilderFromDB::SetLastUsed(const CDateTime
& lastUsed
)
168 m_addonInfo
->m_lastUsed
= lastUsed
;
171 void CAddonInfoBuilderFromDB::SetOrigin(std::string origin
)
173 m_addonInfo
->m_origin
= std::move(origin
);
176 void CAddonInfoBuilderFromDB::SetPackageSize(uint64_t size
)
178 m_addonInfo
->m_packageSize
= size
;
181 void CAddonInfoBuilderFromDB::SetExtensions(CAddonType addonType
)
183 if (!addonType
.GetValue("provides").empty())
184 addonType
.SetProvides(addonType
.GetValue("provides").asString());
186 m_addonInfo
->m_types
.push_back(std::move(addonType
));
187 m_addonInfo
->m_mainType
= addonType
.m_type
;
190 AddonInfoPtr
CAddonInfoBuilder::Generate(const std::string
& id
, AddonType type
)
192 // Check addon identifier for forbidden characters
193 // The identifier is used e.g. in URLs so we shouldn't allow just
194 // any character to go through.
195 if (id
.empty() || id
.find_first_not_of(VALID_ADDON_IDENTIFIER_CHARACTERS
) != std::string::npos
)
197 CLog::Log(LOGERROR
, "CAddonInfoBuilder::{}: identifier '{}' is invalid", __FUNCTION__
, id
);
201 AddonInfoPtr addon
= std::make_shared
<CAddonInfo
>();
203 addon
->m_mainType
= type
;
207 AddonInfoPtr
CAddonInfoBuilder::Generate(const std::string
& addonPath
, bool platformCheck
/*= true*/)
209 auto addonRealPath
= CSpecialProtocol::TranslatePath(addonPath
);
212 if (!xmlDoc
.LoadFile(URIUtils::AddFileToFolder(addonRealPath
, "addon.xml")))
214 CLog::Log(LOGERROR
, "CAddonInfoBuilder::{}: Unable to load '{}', Line {}\n{}",
216 URIUtils::AddFileToFolder(addonRealPath
, "addon.xml"),
222 AddonInfoPtr addon
= std::make_shared
<CAddonInfo
>();
223 if (!ParseXML(addon
, xmlDoc
.RootElement(), addonRealPath
))
226 if (!platformCheck
|| PlatformSupportsAddon(addon
))
232 AddonInfoPtr
CAddonInfoBuilder::Generate(const TiXmlElement
* baseElement
,
233 const RepositoryDirInfo
& repo
,
234 bool platformCheck
/*= true*/)
236 AddonInfoPtr addon
= std::make_shared
<CAddonInfo
>();
237 if (!ParseXML(addon
, baseElement
, repo
.datadir
, repo
))
240 if (!platformCheck
|| PlatformSupportsAddon(addon
))
246 void CAddonInfoBuilder::SetInstallData(const AddonInfoPtr
& addon
, const CDateTime
& installDate
, const CDateTime
& lastUpdated
,
247 const CDateTime
& lastUsed
, const std::string
& origin
)
252 addon
->m_installDate
= installDate
;
253 addon
->m_lastUpdated
= lastUpdated
;
254 addon
->m_lastUsed
= lastUsed
;
255 addon
->m_origin
= origin
;
258 bool CAddonInfoBuilder::ParseXML(const AddonInfoPtr
& addon
,
259 const TiXmlElement
* element
,
260 const std::string
& addonPath
)
262 return ParseXML(addon
, element
, addonPath
, {});
265 bool CAddonInfoBuilder::ParseXML(const AddonInfoPtr
& addon
,
266 const TiXmlElement
* element
,
267 const std::string
& addonPath
,
268 const RepositoryDirInfo
& repo
)
271 * Following values currently not set from creator:
272 * - CDateTime installDate;
273 * - CDateTime lastUpdated;
274 * - CDateTime lastUsed;
275 * - std::string origin;
278 if (!StringUtils::EqualsNoCase(element
->Value(), "addon"))
280 CLog::Log(LOGERROR
, "CAddonInfoBuilder::{}: file from '{}' doesn't contain <addon>", __FUNCTION__
, addonPath
);
285 * The function variable "repo" is only used when reading data stored on the internet.
286 * A boolean value is then set here for easier identification.
288 const bool isRepoXMLContent
= !repo
.datadir
.empty();
295 * provider-name="???">
297 addon
->m_id
= StringUtils::CreateFromCString(element
->Attribute("id"));
298 addon
->m_name
= StringUtils::CreateFromCString(element
->Attribute("name"));
299 addon
->m_author
= StringUtils::CreateFromCString(element
->Attribute("provider-name"));
301 const std::string version
= StringUtils::CreateFromCString(element
->Attribute("version"));
302 addon
->m_version
= CAddonVersion(version
);
304 if (addon
->m_id
.empty() || addon
->m_version
.empty())
306 CLog::Log(LOGERROR
, "CAddonInfoBuilder::{}: file '{}' doesn't contain required values on <addon ... > id='{}', version='{}'",
309 addon
->m_id
.empty() ? "missing" : addon
->m_id
,
310 addon
->m_version
.empty() ? "missing" : addon
->m_version
.asString());
314 // Check addon identifier for forbidden characters
315 // The identifier is used e.g. in URLs so we shouldn't allow just
316 // any character to go through.
317 if (addon
->m_id
.find_first_not_of(VALID_ADDON_IDENTIFIER_CHARACTERS
) != std::string::npos
)
319 CLog::Log(LOGERROR
, "CAddonInfoBuilder::{}: identifier {} is invalid", __FUNCTION__
, addon
->m_id
);
325 * <backwards-compatibility abi="???"/>
327 const TiXmlElement
* backwards
= element
->FirstChildElement("backwards-compatibility");
330 const std::string minVersion
= StringUtils::CreateFromCString(backwards
->Attribute("abi"));
331 addon
->m_minversion
= CAddonVersion(minVersion
);
337 * <import addon="???" minversion="???" version="???" optional="???"/>
340 const TiXmlElement
* _requires
= element
->FirstChildElement("requires");
343 for (const TiXmlElement
* child
= _requires
->FirstChildElement("import"); child
!= nullptr;
344 child
= child
->NextSiblingElement("import"))
346 if (child
->Attribute("addon"))
348 const std::string minVersion
=
349 StringUtils::CreateFromCString(child
->Attribute("minversion"));
350 const std::string version
= StringUtils::CreateFromCString(child
->Attribute("version"));
352 bool optional
= false;
353 child
->QueryBoolAttribute("optional", &optional
);
355 addon
->m_dependencies
.emplace_back(child
->Attribute("addon"), CAddonVersion(minVersion
),
356 CAddonVersion(version
), optional
);
361 std::string assetBasePath
;
362 if (!isRepoXMLContent
&& !addonPath
.empty())
364 // Default for add-on information not loaded from repository
365 assetBasePath
= addonPath
;
366 addon
->m_path
= addonPath
;
370 assetBasePath
= URIUtils::AddFileToFolder(repo
.artdir
, addon
->m_id
);
371 addon
->m_path
= URIUtils::AddFileToFolder(repo
.datadir
, addon
->m_id
, StringUtils::Format("{}-{}.zip", addon
->m_id
, addon
->m_version
.asString()));
374 addon
->m_profilePath
= StringUtils::Format("special://profile/addon_data/{}/", addon
->m_id
);
382 for (const TiXmlElement
* child
= element
->FirstChildElement("extension"); child
!= nullptr; child
= child
->NextSiblingElement("extension"))
384 const std::string point
= StringUtils::CreateFromCString(child
->Attribute("point"));
386 if (point
== "kodi.addon.metadata" || point
== "xbmc.addon.metadata")
389 * Parse addon.xml "<path">...</path>" (special related to repository path),
390 * do first and if present override the default. Also set assetBasePath to
391 * find screenshots and icons.
393 element
= child
->FirstChildElement("path");
394 if (element
&& element
->GetText() != nullptr && !repo
.datadir
.empty())
396 addon
->m_path
= URIUtils::AddFileToFolder(repo
.datadir
, element
->GetText());
397 assetBasePath
= URIUtils::GetDirectory(URIUtils::AddFileToFolder(repo
.artdir
, element
->GetText()));
401 * Parse addon.xml "<summary lang="..">...</summary>"
403 GetTextList(child
, "summary", addon
->m_summary
);
406 * Parse addon.xml "<description lang="..">...</description>"
408 GetTextList(child
, "description", addon
->m_description
);
411 * Parse addon.xml "<disclaimer lang="..">...</disclaimer>"
413 GetTextList(child
, "disclaimer", addon
->m_disclaimer
);
416 * Parse addon.xml "<assets>...</assets>"
418 const TiXmlElement
* element
= child
->FirstChildElement("assets");
421 for (const TiXmlElement
* elementsAssets
= element
->FirstChildElement(); elementsAssets
!= nullptr; elementsAssets
= elementsAssets
->NextSiblingElement())
423 std::string value
= elementsAssets
->Value();
426 if (elementsAssets
->GetText() != nullptr)
427 addon
->m_icon
= URIUtils::AddFileToFolder(assetBasePath
, elementsAssets
->GetText());
429 else if (value
== "screenshot")
431 if (elementsAssets
->GetText() != nullptr)
432 addon
->m_screenshots
.emplace_back(URIUtils::AddFileToFolder(assetBasePath
, elementsAssets
->GetText()));
434 else if (value
== "fanart")
436 if (elementsAssets
->GetText() != nullptr)
437 addon
->m_art
[value
] = URIUtils::AddFileToFolder(assetBasePath
, elementsAssets
->GetText());
439 else if (value
== "banner")
441 if (elementsAssets
->GetText() != nullptr)
442 addon
->m_art
[value
] = URIUtils::AddFileToFolder(assetBasePath
, elementsAssets
->GetText());
444 else if (value
== "clearlogo")
446 if (elementsAssets
->GetText() != nullptr)
447 addon
->m_art
[value
] = URIUtils::AddFileToFolder(assetBasePath
, elementsAssets
->GetText());
449 else if (value
== "thumb")
451 if (elementsAssets
->GetText() != nullptr)
452 addon
->m_art
[value
] =
453 URIUtils::AddFileToFolder(assetBasePath
, elementsAssets
->GetText());
458 /* Parse addon.xml "<platform">...</platform>" */
459 element
= child
->FirstChildElement("platform");
460 if (element
&& element
->GetText() != nullptr)
462 auto platforms
= StringUtils::Split(element
->GetText(),
463 {" ", "\t", "\n", "\r"});
464 platforms
.erase(std::remove_if(platforms
.begin(), platforms
.end(),
465 [](const std::string
& platform
) { return platform
.empty(); }),
467 addon
->m_platforms
= platforms
;
470 /* Parse addon.xml "<license">...</license>" */
471 element
= child
->FirstChildElement("license");
472 if (element
&& element
->GetText() != nullptr)
473 addon
->m_license
= element
->GetText();
475 /* Parse addon.xml "<source">...</source>" */
476 element
= child
->FirstChildElement("source");
477 if (element
&& element
->GetText() != nullptr)
478 addon
->m_source
= element
->GetText();
480 /* Parse addon.xml "<email">...</email>" */
481 element
= child
->FirstChildElement("email");
482 if (element
&& element
->GetText() != nullptr)
483 addon
->m_email
= element
->GetText();
485 /* Parse addon.xml "<website">...</website>" */
486 element
= child
->FirstChildElement("website");
487 if (element
&& element
->GetText() != nullptr)
488 addon
->m_website
= element
->GetText();
490 /* Parse addon.xml "<forum">...</forum>" */
491 element
= child
->FirstChildElement("forum");
492 if (element
&& element
->GetText() != nullptr)
493 addon
->m_forum
= element
->GetText();
495 /* Parse addon.xml "<broken">...</broken>"
496 * NOTE: Replaced with <lifecyclestate>, available for backward compatibility */
497 element
= child
->FirstChildElement("broken");
498 if (element
&& element
->GetText() != nullptr)
500 addon
->m_lifecycleState
= AddonLifecycleState::BROKEN
;
501 addon
->m_lifecycleStateDescription
.emplace(KODI_ADDON_DEFAULT_LANGUAGE_CODE
,
505 /* Parse addon.xml "<lifecyclestate">...</lifecyclestate>" */
506 element
= child
->FirstChildElement("lifecyclestate");
507 if (element
&& element
->GetText() != nullptr)
509 const char* lang
= element
->Attribute("type");
512 if (strcmp(lang
, "broken") == 0)
513 addon
->m_lifecycleState
= AddonLifecycleState::BROKEN
;
514 else if (strcmp(lang
, "deprecated") == 0)
515 addon
->m_lifecycleState
= AddonLifecycleState::DEPRECATED
;
517 addon
->m_lifecycleState
= AddonLifecycleState::NORMAL
;
519 GetTextList(child
, "lifecyclestate", addon
->m_lifecycleStateDescription
);
523 /* Parse addon.xml "<language">...</language>" */
524 element
= child
->FirstChildElement("language");
525 if (element
&& element
->GetText() != nullptr)
526 addon
->AddExtraInfo("language", element
->GetText());
528 /* Parse addon.xml "<reuselanguageinvoker">...</reuselanguageinvoker>" */
529 element
= child
->FirstChildElement("reuselanguageinvoker");
530 if (element
&& element
->GetText() != nullptr)
531 addon
->AddExtraInfo("reuselanguageinvoker", element
->GetText());
533 /* Parse addon.xml "<size">...</size>" */
534 element
= child
->FirstChildElement("size");
535 if (element
&& element
->GetText() != nullptr)
536 addon
->m_packageSize
= StringUtils::ToUint64(element
->GetText(), 0);
538 /* Parse addon.xml "<news lang="..">...</news>"
540 * In the event that the changelog (news) in addon.xml is empty, check
541 * whether it is an installed addon and read a changelog.txt as a
542 * replacement, if available. */
543 GetTextList(child
, "news", addon
->m_changelog
);
544 if (addon
->m_changelog
.empty() && !isRepoXMLContent
&& !addonPath
.empty())
548 const std::string changelog
= URIUtils::AddFileToFolder(addonPath
, "changelog.txt");
549 if (CFile::Exists(changelog
))
552 std::vector
<uint8_t> buf
;
553 if (file
.LoadFile(changelog
, buf
) > 0)
554 addon
->m_changelog
[KODI_ADDON_DEFAULT_LANGUAGE_CODE
].assign(
555 reinterpret_cast<char*>(buf
.data()), buf
.size());
561 AddonType type
= CAddonInfo::TranslateType(point
);
562 if (type
== AddonType::UNKNOWN
|| type
>= AddonType::MAX_TYPES
)
564 CLog::Log(LOGERROR
, "CAddonInfoBuilder::{}: file '{}' doesn't contain a valid add-on type name ({})", __FUNCTION__
, addon
->m_path
, point
);
568 CAddonType
addonType(type
);
569 if (ParseXMLTypes(addonType
, addon
, child
))
570 addon
->m_types
.emplace_back(std::move(addonType
));
575 * If nothing is defined in addon.xml set addon as unknown to have minimum one
576 * instance type present.
578 if (addon
->m_types
.empty())
580 CAddonType
addonType(AddonType::UNKNOWN
);
581 addon
->m_types
.emplace_back(std::move(addonType
));
584 addon
->m_mainType
= addon
->m_types
[0].Type();
585 addon
->m_libname
= addon
->m_types
[0].m_libname
;
586 if (!addon
->m_types
[0].GetValue("provides").empty())
587 addon
->AddExtraInfo("provides", addon
->m_types
[0].GetValue("provides").asString());
589 // Ensure binary types have a valid library for the platform
590 if (addon
->m_mainType
== AddonType::VISUALIZATION
||
591 addon
->m_mainType
== AddonType::SCREENSAVER
|| addon
->m_mainType
== AddonType::PVRDLL
||
592 addon
->m_mainType
== AddonType::AUDIOENCODER
||
593 addon
->m_mainType
== AddonType::AUDIODECODER
|| addon
->m_mainType
== AddonType::VFS
||
594 addon
->m_mainType
== AddonType::IMAGEDECODER
|| addon
->m_mainType
== AddonType::INPUTSTREAM
||
595 addon
->m_mainType
== AddonType::PERIPHERALDLL
|| addon
->m_mainType
== AddonType::GAMEDLL
)
597 if (addon
->m_libname
.empty())
599 // Prevent log file entry if data is from repository, there normal on
600 // addons for other OS's
601 if (!isRepoXMLContent
)
602 CLog::Log(LOGERROR
, "CAddonInfoBuilder::{}: addon.xml from '{}' for binary type '{}' doesn't contain library and addon becomes ignored",
603 __FUNCTION__
, addon
->ID(), CAddonInfo::TranslateType(addon
->m_mainType
));
608 if (!isRepoXMLContent
)
611 if (CFile::Exists(URIUtils::AddFileToFolder(addonPath
, "resources", "settings.xml")))
612 addon
->m_supportsAddonSettings
= true;
613 if (CFile::Exists(URIUtils::AddFileToFolder(addonPath
, "resources", "instance-settings.xml")))
614 addon
->m_supportsInstanceSettings
= true;
617 addon
->m_addonInstanceSupportType
= CAddonInfo::InstanceSupportType(addon
->m_mainType
);
622 bool CAddonInfoBuilder::ParseXMLTypes(CAddonType
& addonType
,
623 const AddonInfoPtr
& info
,
624 const TiXmlElement
* child
)
628 addonType
.m_path
= info
->Path();
630 // Get add-on library file name (if present)
631 const char* library
= child
->Attribute("library");
632 if (library
== nullptr)
633 library
= GetPlatformLibraryName(child
);
634 if (library
!= nullptr)
636 addonType
.m_libname
= library
;
640 // linux is different and has the version number after the suffix
641 static const std::regex
libRegex("^.*" +
642 CCompileInfo::CCompileInfo::GetSharedLibrarySuffix() +
643 "\\.?[0-9]*\\.?[0-9]*\\.?[0-9]*$");
644 if (std::regex_match(library
, libRegex
))
646 info
->SetBinary(true);
647 CLog::Log(LOGDEBUG
, "CAddonInfoBuilder::{}: Binary addon found: {}", __func__
,
651 catch (const std::regex_error
& e
)
653 CLog::Log(LOGERROR
, "CAddonInfoBuilder::{}: Regex error caught: {}", __func__
,
658 if (!ParseXMLExtension(addonType
, child
))
660 CLog::Log(LOGERROR
, "CAddonInfoBuilder::{}: addon.xml file doesn't contain a valid add-on extensions ({})", __FUNCTION__
, info
->ID());
663 if (!addonType
.GetValue("provides").empty())
664 addonType
.SetProvides(addonType
.GetValue("provides").asString());
670 bool CAddonInfoBuilder::ParseXMLExtension(CAddonExtensions
& addonExt
, const TiXmlElement
* element
)
672 addonExt
.m_point
= StringUtils::CreateFromCString(element
->Attribute("point"));
675 const TiXmlAttribute
* attribute
= element
->FirstAttribute();
678 std::string name
= attribute
->Name();
681 const std::string value
= StringUtils::CreateFromCString(attribute
->Value());
685 extension
.emplace_back(std::make_pair(name
, SExtValue(value
)));
688 attribute
= attribute
->Next();
690 if (!extension
.empty())
691 addonExt
.m_values
.emplace_back(std::pair
<std::string
, EXT_VALUE
>("", std::move(extension
)));
693 const TiXmlElement
* childElement
= element
->FirstChildElement();
696 const std::string id
= StringUtils::CreateFromCString(childElement
->Value());
700 const TiXmlAttribute
* attribute
= childElement
->FirstAttribute();
703 std::string name
= attribute
->Name();
706 const std::string value
= StringUtils::CreateFromCString(attribute
->Value());
709 name
= id
+ "@" + name
;
710 extension
.emplace_back(std::make_pair(name
, SExtValue(value
)));
713 attribute
= attribute
->Next();
716 const std::string childElementText
= StringUtils::CreateFromCString(childElement
->GetText());
718 if (!childElementText
.empty())
720 extension
.emplace_back(std::make_pair(id
, SExtValue(childElementText
)));
723 if (!extension
.empty())
724 addonExt
.m_values
.emplace_back(std::make_pair(id
, std::move(extension
)));
726 if (childElementText
.empty())
728 const TiXmlElement
* childSubElement
= childElement
->FirstChildElement();
731 CAddonExtensions subElement
;
732 if (ParseXMLExtension(subElement
, childElement
))
733 addonExt
.m_children
.emplace_back(std::make_pair(id
, std::move(subElement
)));
737 childElement
= childElement
->NextSiblingElement();
743 bool CAddonInfoBuilder::GetTextList(const TiXmlElement
* element
, const std::string
& tag
, std::unordered_map
<std::string
, std::string
>& translatedValues
)
748 translatedValues
.clear();
750 for (const TiXmlElement
* child
= element
->FirstChildElement(tag
); child
!= nullptr; child
= child
->NextSiblingElement(tag
))
752 const char* lang
= child
->Attribute("lang");
753 const char* text
= child
->GetText();
756 if (strcmp(lang
, "no") == 0)
757 translatedValues
.insert(std::make_pair("nb_NO", text
!= nullptr ? text
: ""));
759 translatedValues
.insert(std::make_pair(lang
, text
!= nullptr ? text
: ""));
762 translatedValues
.insert(
763 std::make_pair(KODI_ADDON_DEFAULT_LANGUAGE_CODE
, text
!= nullptr ? text
: ""));
766 return !translatedValues
.empty();
769 const char* CAddonInfoBuilder::GetPlatformLibraryName(const TiXmlElement
* element
)
771 const char* libraryName
;
772 #if defined(TARGET_ANDROID)
773 libraryName
= element
->Attribute("library_android");
774 #elif defined(TARGET_LINUX) || defined(TARGET_FREEBSD)
775 #if defined(TARGET_FREEBSD)
776 libraryName
= element
->Attribute("library_freebsd");
777 if (libraryName
== nullptr)
779 libraryName
= element
->Attribute("library_linux");
780 #elif defined(TARGET_WINDOWS_DESKTOP)
781 libraryName
= element
->Attribute("library_windx");
782 if (libraryName
== nullptr)
783 libraryName
= element
->Attribute("library_windows");
784 #elif defined(TARGET_WINDOWS_STORE)
785 libraryName
= element
->Attribute("library_windowsstore");
786 #elif defined(TARGET_DARWIN)
787 #if defined(TARGET_DARWIN_EMBEDDED)
788 libraryName
= element
->Attribute("library_darwin_embedded");
790 libraryName
= element
->Attribute("library_osx");
797 bool CAddonInfoBuilder::PlatformSupportsAddon(const AddonInfoPtr
& addon
)
799 if (addon
->m_platforms
.empty())
802 std::vector
<std::string
> supportedPlatforms
= {
804 #if defined(TARGET_ANDROID)
806 #if defined(__ARM_ARCH_7A__)
808 #elif defined(__aarch64__)
810 #elif defined(__i686__)
812 #elif defined(__x86_64__)
815 #warning no architecture dependant platform tag
817 #elif defined(TARGET_FREEBSD)
819 #elif defined(TARGET_LINUX)
821 #if defined(__ARM_ARCH_7A__)
823 #elif defined(__aarch64__)
825 #elif defined(__i686__)
827 #elif defined(__x86_64__)
830 #warning no architecture dependant platform tag
832 #elif defined(TARGET_WINDOWS_DESKTOP)
837 #elif defined(_M_AMD64)
840 #error no architecture dependant platform tag
842 #elif defined(TARGET_WINDOWS_STORE)
844 #elif defined(TARGET_DARWIN_EMBEDDED)
846 #if defined(TARGET_DARWIN_IOS)
848 #if defined(__aarch64__)
851 #warning no architecture dependant platform tag
853 #elif defined(TARGET_DARWIN_TVOS)
857 #elif defined(TARGET_DARWIN_OSX)
859 #if defined(__x86_64__)
862 #elif defined(__aarch64__)
866 #warning no architecture dependant platform tag
871 return std::find_first_of(addon
->m_platforms
.begin(), addon
->m_platforms
.end(),
872 supportedPlatforms
.begin(), supportedPlatforms
.end()) != addon
->m_platforms
.end();