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/XBMCTinyXML2.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
);
211 CXBMCTinyXML2 xmlDoc
;
212 if (!xmlDoc
.LoadFile(URIUtils::AddFileToFolder(addonRealPath
, "addon.xml")))
214 CLog::Log(LOGERROR
, "CAddonInfoBuilder::{}: Unable to load '{}', Line {}\n{}", __FUNCTION__
,
215 URIUtils::AddFileToFolder(addonRealPath
, "addon.xml"), xmlDoc
.ErrorLineNum(),
220 AddonInfoPtr addon
= std::make_shared
<CAddonInfo
>();
221 if (!ParseXML(addon
, xmlDoc
.RootElement(), addonRealPath
))
224 if (!platformCheck
|| PlatformSupportsAddon(addon
))
227 CLog::Log(LOGERROR
, "CAddonInfoBuilder::{}: No platform for add-on {} (supported platforms: {})",
228 __FUNCTION__
, addon
->ID(), StringUtils::Join(addon
->m_platforms
, ", "));
233 AddonInfoPtr
CAddonInfoBuilder::Generate(const tinyxml2::XMLElement
* baseElement
,
234 const RepositoryDirInfo
& repo
,
235 bool platformCheck
/*= true*/)
237 AddonInfoPtr addon
= std::make_shared
<CAddonInfo
>();
238 if (!ParseXML(addon
, baseElement
, repo
.datadir
, repo
))
241 if (!platformCheck
|| PlatformSupportsAddon(addon
))
247 void CAddonInfoBuilder::SetInstallData(const AddonInfoPtr
& addon
, const CDateTime
& installDate
, const CDateTime
& lastUpdated
,
248 const CDateTime
& lastUsed
, const std::string
& origin
)
253 addon
->m_installDate
= installDate
;
254 addon
->m_lastUpdated
= lastUpdated
;
255 addon
->m_lastUsed
= lastUsed
;
256 addon
->m_origin
= origin
;
259 bool CAddonInfoBuilder::ParseXML(const AddonInfoPtr
& addon
,
260 const tinyxml2::XMLElement
* element
,
261 const std::string
& addonPath
)
263 return ParseXML(addon
, element
, addonPath
, {});
266 bool CAddonInfoBuilder::ParseXML(const AddonInfoPtr
& addon
,
267 const tinyxml2::XMLElement
* element
,
268 const std::string
& addonPath
,
269 const RepositoryDirInfo
& repo
)
272 * Following values currently not set from creator:
273 * - CDateTime installDate;
274 * - CDateTime lastUpdated;
275 * - CDateTime lastUsed;
276 * - std::string origin;
279 if (!StringUtils::EqualsNoCase(element
->Value(), "addon"))
281 CLog::Log(LOGERROR
, "CAddonInfoBuilder::{}: file from '{}' doesn't contain <addon>", __FUNCTION__
, addonPath
);
286 * The function variable "repo" is only used when reading data stored on the internet.
287 * A boolean value is then set here for easier identification.
289 const bool isRepoXMLContent
= !repo
.datadir
.empty();
296 * provider-name="???">
298 addon
->m_id
= StringUtils::CreateFromCString(element
->Attribute("id"));
299 addon
->m_name
= StringUtils::CreateFromCString(element
->Attribute("name"));
300 addon
->m_author
= StringUtils::CreateFromCString(element
->Attribute("provider-name"));
302 const std::string version
= StringUtils::CreateFromCString(element
->Attribute("version"));
303 addon
->m_version
= CAddonVersion(version
);
305 if (addon
->m_id
.empty() || addon
->m_version
.empty())
307 CLog::Log(LOGERROR
, "CAddonInfoBuilder::{}: file '{}' doesn't contain required values on <addon ... > id='{}', version='{}'",
310 addon
->m_id
.empty() ? "missing" : addon
->m_id
,
311 addon
->m_version
.empty() ? "missing" : addon
->m_version
.asString());
315 // Check addon identifier for forbidden characters
316 // The identifier is used e.g. in URLs so we shouldn't allow just
317 // any character to go through.
318 if (addon
->m_id
.find_first_not_of(VALID_ADDON_IDENTIFIER_CHARACTERS
) != std::string::npos
)
320 CLog::Log(LOGERROR
, "CAddonInfoBuilder::{}: identifier {} is invalid", __FUNCTION__
, addon
->m_id
);
326 * <backwards-compatibility abi="???"/>
328 const auto* backwards
= element
->FirstChildElement("backwards-compatibility");
331 const std::string minVersion
= StringUtils::CreateFromCString(backwards
->Attribute("abi"));
332 addon
->m_minversion
= CAddonVersion(minVersion
);
338 * <import addon="???" minversion="???" version="???" optional="???"/>
341 const auto* _requires
= element
->FirstChildElement("requires");
344 for (const auto* child
= _requires
->FirstChildElement("import"); child
!= nullptr;
345 child
= child
->NextSiblingElement("import"))
347 if (child
->Attribute("addon"))
349 const std::string minVersion
=
350 StringUtils::CreateFromCString(child
->Attribute("minversion"));
351 const std::string version
= StringUtils::CreateFromCString(child
->Attribute("version"));
353 bool optional
= false;
354 child
->QueryBoolAttribute("optional", &optional
);
356 addon
->m_dependencies
.emplace_back(child
->Attribute("addon"), CAddonVersion(minVersion
),
357 CAddonVersion(version
), optional
);
362 std::string assetBasePath
;
363 if (!isRepoXMLContent
&& !addonPath
.empty())
365 // Default for add-on information not loaded from repository
366 assetBasePath
= addonPath
;
367 addon
->m_path
= addonPath
;
371 assetBasePath
= URIUtils::AddFileToFolder(repo
.artdir
, addon
->m_id
);
372 addon
->m_path
= URIUtils::AddFileToFolder(repo
.datadir
, addon
->m_id
, StringUtils::Format("{}-{}.zip", addon
->m_id
, addon
->m_version
.asString()));
375 addon
->m_profilePath
= StringUtils::Format("special://profile/addon_data/{}/", addon
->m_id
);
383 for (const auto* child
= element
->FirstChildElement("extension"); child
!= nullptr;
384 child
= child
->NextSiblingElement("extension"))
386 const std::string point
= StringUtils::CreateFromCString(child
->Attribute("point"));
388 if (point
== "kodi.addon.metadata" || point
== "xbmc.addon.metadata")
391 * Parse addon.xml "<path">...</path>" (special related to repository path),
392 * do first and if present override the default. Also set assetBasePath to
393 * find screenshots and icons.
395 element
= child
->FirstChildElement("path");
396 if (element
&& element
->GetText() != nullptr && !repo
.datadir
.empty())
398 addon
->m_path
= URIUtils::AddFileToFolder(repo
.datadir
, element
->GetText());
399 assetBasePath
= URIUtils::GetDirectory(URIUtils::AddFileToFolder(repo
.artdir
, element
->GetText()));
403 * Parse addon.xml "<summary lang="..">...</summary>"
405 GetTextList(child
, "summary", addon
->m_summary
);
408 * Parse addon.xml "<description lang="..">...</description>"
410 GetTextList(child
, "description", addon
->m_description
);
413 * Parse addon.xml "<disclaimer lang="..">...</disclaimer>"
415 GetTextList(child
, "disclaimer", addon
->m_disclaimer
);
418 * Parse addon.xml "<assets>...</assets>"
420 const auto* element
= child
->FirstChildElement("assets");
423 for (const auto* elementsAssets
= element
->FirstChildElement(); elementsAssets
!= nullptr;
424 elementsAssets
= elementsAssets
->NextSiblingElement())
426 std::string value
= elementsAssets
->Value();
429 if (elementsAssets
->GetText() != nullptr)
430 addon
->m_icon
= URIUtils::AddFileToFolder(assetBasePath
, elementsAssets
->GetText());
432 else if (value
== "screenshot")
434 if (elementsAssets
->GetText() != nullptr)
435 addon
->m_screenshots
.emplace_back(URIUtils::AddFileToFolder(assetBasePath
, elementsAssets
->GetText()));
437 else if (value
== "fanart")
439 if (elementsAssets
->GetText() != nullptr)
440 addon
->m_art
[value
] = URIUtils::AddFileToFolder(assetBasePath
, elementsAssets
->GetText());
442 else if (value
== "banner")
444 if (elementsAssets
->GetText() != nullptr)
445 addon
->m_art
[value
] = URIUtils::AddFileToFolder(assetBasePath
, elementsAssets
->GetText());
447 else if (value
== "clearlogo")
449 if (elementsAssets
->GetText() != nullptr)
450 addon
->m_art
[value
] = URIUtils::AddFileToFolder(assetBasePath
, elementsAssets
->GetText());
452 else if (value
== "thumb")
454 if (elementsAssets
->GetText() != nullptr)
455 addon
->m_art
[value
] =
456 URIUtils::AddFileToFolder(assetBasePath
, elementsAssets
->GetText());
461 /* Parse addon.xml "<platform">...</platform>" */
462 element
= child
->FirstChildElement("platform");
463 if (element
&& element
->GetText() != nullptr)
465 auto platforms
= StringUtils::Split(element
->GetText(),
466 {" ", "\t", "\n", "\r"});
467 platforms
.erase(std::remove_if(platforms
.begin(), platforms
.end(),
468 [](const std::string
& platform
) { return platform
.empty(); }),
470 addon
->m_platforms
= platforms
;
473 /* Parse addon.xml "<license">...</license>" */
474 element
= child
->FirstChildElement("license");
475 if (element
&& element
->GetText() != nullptr)
476 addon
->m_license
= element
->GetText();
478 /* Parse addon.xml "<source">...</source>" */
479 element
= child
->FirstChildElement("source");
480 if (element
&& element
->GetText() != nullptr)
481 addon
->m_source
= element
->GetText();
483 /* Parse addon.xml "<email">...</email>" */
484 element
= child
->FirstChildElement("email");
485 if (element
&& element
->GetText() != nullptr)
486 addon
->m_email
= element
->GetText();
488 /* Parse addon.xml "<website">...</website>" */
489 element
= child
->FirstChildElement("website");
490 if (element
&& element
->GetText() != nullptr)
491 addon
->m_website
= element
->GetText();
493 /* Parse addon.xml "<forum">...</forum>" */
494 element
= child
->FirstChildElement("forum");
495 if (element
&& element
->GetText() != nullptr)
496 addon
->m_forum
= element
->GetText();
498 /* Parse addon.xml "<broken">...</broken>"
499 * NOTE: Replaced with <lifecyclestate>, available for backward compatibility */
500 element
= child
->FirstChildElement("broken");
501 if (element
&& element
->GetText() != nullptr)
503 addon
->m_lifecycleState
= AddonLifecycleState::BROKEN
;
504 addon
->m_lifecycleStateDescription
.emplace(KODI_ADDON_DEFAULT_LANGUAGE_CODE
,
508 /* Parse addon.xml "<lifecyclestate">...</lifecyclestate>" */
509 element
= child
->FirstChildElement("lifecyclestate");
510 if (element
&& element
->GetText() != nullptr)
512 const char* lang
= element
->Attribute("type");
515 if (strcmp(lang
, "broken") == 0)
516 addon
->m_lifecycleState
= AddonLifecycleState::BROKEN
;
517 else if (strcmp(lang
, "deprecated") == 0)
518 addon
->m_lifecycleState
= AddonLifecycleState::DEPRECATED
;
520 addon
->m_lifecycleState
= AddonLifecycleState::NORMAL
;
522 GetTextList(child
, "lifecyclestate", addon
->m_lifecycleStateDescription
);
526 /* Parse addon.xml "<language">...</language>" */
527 element
= child
->FirstChildElement("language");
528 if (element
&& element
->GetText() != nullptr)
529 addon
->AddExtraInfo("language", element
->GetText());
531 /* Parse addon.xml "<reuselanguageinvoker">...</reuselanguageinvoker>" */
532 element
= child
->FirstChildElement("reuselanguageinvoker");
533 if (element
&& element
->GetText() != nullptr)
534 addon
->AddExtraInfo("reuselanguageinvoker", element
->GetText());
536 /* Parse addon.xml "<size">...</size>" */
537 element
= child
->FirstChildElement("size");
538 if (element
&& element
->GetText() != nullptr)
539 addon
->m_packageSize
= StringUtils::ToUint64(element
->GetText(), 0);
541 /* Parse addon.xml "<news lang="..">...</news>"
543 * In the event that the changelog (news) in addon.xml is empty, check
544 * whether it is an installed addon and read a changelog.txt as a
545 * replacement, if available. */
546 GetTextList(child
, "news", addon
->m_changelog
);
547 if (addon
->m_changelog
.empty() && !isRepoXMLContent
&& !addonPath
.empty())
551 const std::string changelog
= URIUtils::AddFileToFolder(addonPath
, "changelog.txt");
552 if (CFile::Exists(changelog
))
555 std::vector
<uint8_t> buf
;
556 if (file
.LoadFile(changelog
, buf
) > 0)
557 addon
->m_changelog
[KODI_ADDON_DEFAULT_LANGUAGE_CODE
].assign(
558 reinterpret_cast<char*>(buf
.data()), buf
.size());
564 AddonType type
= CAddonInfo::TranslateType(point
);
565 if (type
== AddonType::UNKNOWN
|| type
>= AddonType::MAX_TYPES
)
567 CLog::Log(LOGERROR
, "CAddonInfoBuilder::{}: file '{}' doesn't contain a valid add-on type name ({})", __FUNCTION__
, addon
->m_path
, point
);
571 CAddonType
addonType(type
);
572 if (ParseXMLTypes(addonType
, addon
, child
))
573 addon
->m_types
.emplace_back(std::move(addonType
));
578 * If nothing is defined in addon.xml set addon as unknown to have minimum one
579 * instance type present.
581 if (addon
->m_types
.empty())
583 CAddonType
addonType(AddonType::UNKNOWN
);
584 addon
->m_types
.emplace_back(std::move(addonType
));
587 addon
->m_mainType
= addon
->m_types
[0].Type();
588 addon
->m_libname
= addon
->m_types
[0].m_libname
;
589 if (!addon
->m_types
[0].GetValue("provides").empty())
590 addon
->AddExtraInfo("provides", addon
->m_types
[0].GetValue("provides").asString());
592 // Ensure binary types have a valid library for the platform
593 if (addon
->m_mainType
== AddonType::VISUALIZATION
||
594 addon
->m_mainType
== AddonType::SCREENSAVER
|| addon
->m_mainType
== AddonType::PVRDLL
||
595 addon
->m_mainType
== AddonType::AUDIOENCODER
||
596 addon
->m_mainType
== AddonType::AUDIODECODER
|| addon
->m_mainType
== AddonType::VFS
||
597 addon
->m_mainType
== AddonType::IMAGEDECODER
|| addon
->m_mainType
== AddonType::INPUTSTREAM
||
598 addon
->m_mainType
== AddonType::PERIPHERALDLL
|| addon
->m_mainType
== AddonType::GAMEDLL
)
600 if (addon
->m_libname
.empty())
602 // Prevent log file entry if data is from repository, there normal on
603 // addons for other OS's
604 if (!isRepoXMLContent
)
605 CLog::Log(LOGERROR
, "CAddonInfoBuilder::{}: addon.xml from '{}' for binary type '{}' doesn't contain library and addon becomes ignored",
606 __FUNCTION__
, addon
->ID(), CAddonInfo::TranslateType(addon
->m_mainType
));
611 if (!isRepoXMLContent
)
614 if (CFile::Exists(URIUtils::AddFileToFolder(addonPath
, "resources", "settings.xml")))
615 addon
->m_supportsAddonSettings
= true;
616 if (CFile::Exists(URIUtils::AddFileToFolder(addonPath
, "resources", "instance-settings.xml")))
617 addon
->m_supportsInstanceSettings
= true;
620 addon
->m_addonInstanceSupportType
= CAddonInfo::InstanceSupportType(addon
->m_mainType
);
625 bool CAddonInfoBuilder::ParseXMLTypes(CAddonType
& addonType
,
626 const AddonInfoPtr
& info
,
627 const tinyxml2::XMLElement
* child
)
631 addonType
.m_path
= info
->Path();
633 // Get add-on library file name (if present)
634 const char* library
= child
->Attribute("library");
635 if (library
== nullptr)
636 library
= GetPlatformLibraryName(child
);
637 if (library
!= nullptr)
639 addonType
.m_libname
= library
;
643 // linux is different and has the version number after the suffix
644 static const std::regex
libRegex("^.*" +
645 CCompileInfo::CCompileInfo::GetSharedLibrarySuffix() +
646 "\\.?[0-9]*\\.?[0-9]*\\.?[0-9]*$");
647 if (std::regex_match(library
, libRegex
))
649 info
->SetBinary(true);
650 CLog::Log(LOGDEBUG
, "CAddonInfoBuilder::{}: Binary addon found: {}", __func__
,
654 catch (const std::regex_error
& e
)
656 CLog::Log(LOGERROR
, "CAddonInfoBuilder::{}: Regex error caught: {}", __func__
,
661 if (!ParseXMLExtension(addonType
, child
))
663 CLog::Log(LOGERROR
, "CAddonInfoBuilder::{}: addon.xml file doesn't contain a valid add-on extensions ({})", __FUNCTION__
, info
->ID());
666 if (!addonType
.GetValue("provides").empty())
667 addonType
.SetProvides(addonType
.GetValue("provides").asString());
673 bool CAddonInfoBuilder::ParseXMLExtension(CAddonExtensions
& addonExt
,
674 const tinyxml2::XMLElement
* element
)
676 addonExt
.m_point
= StringUtils::CreateFromCString(element
->Attribute("point"));
679 const auto* attribute
= element
->FirstAttribute();
682 std::string name
= attribute
->Name();
685 const std::string value
= StringUtils::CreateFromCString(attribute
->Value());
689 extension
.emplace_back(std::make_pair(name
, SExtValue(value
)));
692 attribute
= attribute
->Next();
694 if (!extension
.empty())
695 addonExt
.m_values
.emplace_back(std::pair
<std::string
, EXT_VALUE
>("", std::move(extension
)));
697 const auto* childElement
= element
->FirstChildElement();
700 const std::string id
= StringUtils::CreateFromCString(childElement
->Value());
703 EXT_VALUE childExtension
;
704 const auto* attribute
= childElement
->FirstAttribute();
707 std::string name
= attribute
->Name();
710 const std::string value
= StringUtils::CreateFromCString(attribute
->Value());
713 name
= id
+ "@" + name
;
714 childExtension
.emplace_back(std::make_pair(name
, SExtValue(value
)));
717 attribute
= attribute
->Next();
720 const std::string childElementText
= StringUtils::CreateFromCString(childElement
->GetText());
722 if (!childElementText
.empty())
724 childExtension
.emplace_back(std::make_pair(id
, SExtValue(childElementText
)));
727 if (!childExtension
.empty())
728 addonExt
.m_values
.emplace_back(std::make_pair(id
, std::move(childExtension
)));
730 if (childElementText
.empty())
732 const tinyxml2::XMLElement
* childSubElement
= childElement
->FirstChildElement();
735 CAddonExtensions subElement
;
736 if (ParseXMLExtension(subElement
, childElement
))
737 addonExt
.m_children
.emplace_back(std::make_pair(id
, std::move(subElement
)));
741 childElement
= childElement
->NextSiblingElement();
747 bool CAddonInfoBuilder::GetTextList(const tinyxml2::XMLElement
* element
,
748 const std::string
& tag
,
749 std::unordered_map
<std::string
, std::string
>& translatedValues
)
754 translatedValues
.clear();
756 for (const auto* child
= element
->FirstChildElement(tag
.c_str()); child
!= nullptr;
757 child
= child
->NextSiblingElement(tag
.c_str()))
759 const char* lang
= child
->Attribute("lang");
760 const char* text
= child
->GetText();
763 if (strcmp(lang
, "no") == 0)
764 translatedValues
.insert(std::make_pair("nb_NO", text
!= nullptr ? text
: ""));
766 translatedValues
.insert(std::make_pair(lang
, text
!= nullptr ? text
: ""));
769 translatedValues
.insert(
770 std::make_pair(KODI_ADDON_DEFAULT_LANGUAGE_CODE
, text
!= nullptr ? text
: ""));
773 return !translatedValues
.empty();
776 const char* CAddonInfoBuilder::GetPlatformLibraryName(const tinyxml2::XMLElement
* element
)
778 const char* libraryName
= nullptr;
779 #if defined(TARGET_ANDROID)
780 libraryName
= element
->Attribute("library_android");
781 #elif defined(TARGET_LINUX) || defined(TARGET_FREEBSD)
782 #if defined(TARGET_FREEBSD)
783 libraryName
= element
->Attribute("library_freebsd");
784 if (libraryName
== nullptr)
786 libraryName
= element
->Attribute("library_linux");
787 #elif defined(TARGET_WINDOWS_DESKTOP)
788 libraryName
= element
->Attribute("library_windx");
789 if (libraryName
== nullptr)
790 libraryName
= element
->Attribute("library_windows");
791 #elif defined(TARGET_WINDOWS_STORE)
792 libraryName
= element
->Attribute("library_windowsstore");
793 #elif defined(TARGET_DARWIN)
794 #if defined(TARGET_DARWIN_EMBEDDED)
795 libraryName
= element
->Attribute("library_darwin_embedded");
797 libraryName
= element
->Attribute("library_osx");
804 bool CAddonInfoBuilder::PlatformSupportsAddon(const AddonInfoPtr
& addon
)
806 if (addon
->m_platforms
.empty())
809 std::vector
<std::string
> supportedPlatforms
= {
811 #if defined(TARGET_ANDROID)
813 #if defined(__ARM_ARCH_7A__)
815 #elif defined(__aarch64__)
817 #elif defined(__i686__)
819 #elif defined(__x86_64__)
822 #warning no architecture dependant platform tag
824 #elif defined(TARGET_FREEBSD)
826 #elif defined(TARGET_LINUX)
828 #if defined(__ARM_ARCH_7A__)
830 #elif defined(__aarch64__)
832 #elif defined(__i686__)
834 #elif defined(__x86_64__)
837 #warning no architecture dependant platform tag
839 #elif defined(TARGET_WINDOWS_DESKTOP)
844 #elif defined(_M_AMD64)
847 #error no architecture dependant platform tag
849 #elif defined(TARGET_WINDOWS_STORE)
851 #elif defined(TARGET_DARWIN_EMBEDDED)
853 #if defined(TARGET_DARWIN_IOS)
855 #if defined(__aarch64__)
858 #warning no architecture dependant platform tag
860 #elif defined(TARGET_DARWIN_TVOS)
864 #elif defined(TARGET_DARWIN_OSX)
866 #if defined(__x86_64__)
869 #elif defined(__aarch64__)
873 #warning no architecture dependant platform tag
878 return std::find_first_of(addon
->m_platforms
.begin(), addon
->m_platforms
.end(),
879 supportedPlatforms
.begin(), supportedPlatforms
.end()) != addon
->m_platforms
.end();