[Windows] Fix driver version detection of AMD RDNA+ GPU on Windows 10
[xbmc.git] / xbmc / addons / addoninfo / AddonInfoBuilder.cpp
blob6550057795a0ed19ee680586d0fd8be0dbca1ad5
1 /*
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.
7 */
9 #include "AddonInfoBuilder.h"
11 #include "CompileInfo.h"
12 #include "LangInfo.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"
26 #include <algorithm>
27 #include <memory>
28 #include <regex>
30 namespace
32 // Note that all of these characters are url-safe
33 const std::string VALID_ADDON_IDENTIFIER_CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_@!$";
36 namespace ADDON
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);
198 return nullptr;
201 AddonInfoPtr addon = std::make_shared<CAddonInfo>();
202 addon->m_id = id;
203 addon->m_mainType = type;
204 return addon;
207 AddonInfoPtr CAddonInfoBuilder::Generate(const std::string& addonPath, bool platformCheck /*= true*/)
209 auto addonRealPath = CSpecialProtocol::TranslatePath(addonPath);
211 CXBMCTinyXML xmlDoc;
212 if (!xmlDoc.LoadFile(URIUtils::AddFileToFolder(addonRealPath, "addon.xml")))
214 CLog::Log(LOGERROR, "CAddonInfoBuilder::{}: Unable to load '{}', Line {}\n{}",
215 __FUNCTION__,
216 URIUtils::AddFileToFolder(addonRealPath, "addon.xml"),
217 xmlDoc.ErrorRow(),
218 xmlDoc.ErrorDesc());
219 return nullptr;
222 AddonInfoPtr addon = std::make_shared<CAddonInfo>();
223 if (!ParseXML(addon, xmlDoc.RootElement(), addonRealPath))
224 return nullptr;
226 if (!platformCheck || PlatformSupportsAddon(addon))
227 return addon;
229 return nullptr;
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))
238 return nullptr;
240 if (!platformCheck || PlatformSupportsAddon(addon))
241 return addon;
243 return nullptr;
246 void CAddonInfoBuilder::SetInstallData(const AddonInfoPtr& addon, const CDateTime& installDate, const CDateTime& lastUpdated,
247 const CDateTime& lastUsed, const std::string& origin)
249 if (!addon)
250 return;
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);
281 return false;
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();
291 * Parse addon.xml:
292 * <addon id="???"
293 * name="???"
294 * version="???"
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='{}'",
307 __FUNCTION__,
308 addonPath,
309 addon->m_id.empty() ? "missing" : addon->m_id,
310 addon->m_version.empty() ? "missing" : addon->m_version.asString());
311 return false;
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);
320 return false;
324 * Parse addon.xml:
325 * <backwards-compatibility abi="???"/>
327 const TiXmlElement* backwards = element->FirstChildElement("backwards-compatibility");
328 if (backwards)
330 const std::string minVersion = StringUtils::CreateFromCString(backwards->Attribute("abi"));
331 addon->m_minversion = CAddonVersion(minVersion);
335 * Parse addon.xml:
336 * <requires>
337 * <import addon="???" minversion="???" version="???" optional="???"/>
338 * </requires>
340 const TiXmlElement* _requires = element->FirstChildElement("requires");
341 if (_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;
368 else
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);
377 * Parse addon.xml:
378 * <extension>
379 * ...
380 * </extension>
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");
419 if (element)
421 for (const TiXmlElement* elementsAssets = element->FirstChildElement(); elementsAssets != nullptr; elementsAssets = elementsAssets->NextSiblingElement())
423 std::string value = elementsAssets->Value();
424 if (value == "icon")
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(); }),
466 platforms.cend());
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,
502 element->GetText());
505 /* Parse addon.xml "<lifecyclestate">...</lifecyclestate>" */
506 element = child->FirstChildElement("lifecyclestate");
507 if (element && element->GetText() != nullptr)
509 const char* lang = element->Attribute("type");
510 if (lang)
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;
516 else
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())
546 using XFILE::CFile;
548 const std::string changelog = URIUtils::AddFileToFolder(addonPath, "changelog.txt");
549 if (CFile::Exists(changelog))
551 CFile file;
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());
559 else
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);
565 return false;
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));
604 return false;
608 if (!isRepoXMLContent)
610 using XFILE::CFile;
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);
619 return true;
622 bool CAddonInfoBuilder::ParseXMLTypes(CAddonType& addonType,
623 const AddonInfoPtr& info,
624 const TiXmlElement* child)
626 if (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__,
648 info->ID());
651 catch (const std::regex_error& e)
653 CLog::Log(LOGERROR, "CAddonInfoBuilder::{}: Regex error caught: {}", __func__,
654 e.what());
658 if (!ParseXMLExtension(addonType, child))
660 CLog::Log(LOGERROR, "CAddonInfoBuilder::{}: addon.xml file doesn't contain a valid add-on extensions ({})", __FUNCTION__, info->ID());
661 return false;
663 if (!addonType.GetValue("provides").empty())
664 addonType.SetProvides(addonType.GetValue("provides").asString());
665 return true;
667 return false;
670 bool CAddonInfoBuilder::ParseXMLExtension(CAddonExtensions& addonExt, const TiXmlElement* element)
672 addonExt.m_point = StringUtils::CreateFromCString(element->Attribute("point"));
674 EXT_VALUE extension;
675 const TiXmlAttribute* attribute = element->FirstAttribute();
676 while (attribute)
678 std::string name = attribute->Name();
679 if (name != "point")
681 const std::string value = StringUtils::CreateFromCString(attribute->Value());
682 if (!value.empty())
684 name = "@" + name;
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();
694 while (childElement)
696 const std::string id = StringUtils::CreateFromCString(childElement->Value());
697 if (!id.empty())
699 EXT_VALUE extension;
700 const TiXmlAttribute* attribute = childElement->FirstAttribute();
701 while (attribute)
703 std::string name = attribute->Name();
704 if (name != "point")
706 const std::string value = StringUtils::CreateFromCString(attribute->Value());
707 if (!value.empty())
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();
729 if (childSubElement)
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();
740 return true;
743 bool CAddonInfoBuilder::GetTextList(const TiXmlElement* element, const std::string& tag, std::unordered_map<std::string, std::string>& translatedValues)
745 if (!element)
746 return false;
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();
754 if (lang != nullptr)
756 if (strcmp(lang, "no") == 0)
757 translatedValues.insert(std::make_pair("nb_NO", text != nullptr ? text : ""));
758 else
759 translatedValues.insert(std::make_pair(lang, text != nullptr ? text : ""));
761 else
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)
778 #endif
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");
789 #else
790 libraryName = element->Attribute("library_osx");
791 #endif
792 #endif
794 return libraryName;
797 bool CAddonInfoBuilder::PlatformSupportsAddon(const AddonInfoPtr& addon)
799 if (addon->m_platforms.empty())
800 return true;
802 std::vector<std::string> supportedPlatforms = {
803 "all",
804 #if defined(TARGET_ANDROID)
805 "android",
806 #if defined(__ARM_ARCH_7A__)
807 "android-armv7",
808 #elif defined(__aarch64__)
809 "android-aarch64",
810 #elif defined(__i686__)
811 "android-i686",
812 #elif defined(__x86_64__)
813 "android-x86_64",
814 #else
815 #warning no architecture dependant platform tag
816 #endif
817 #elif defined(TARGET_FREEBSD)
818 "freebsd",
819 #elif defined(TARGET_LINUX)
820 "linux",
821 #if defined(__ARM_ARCH_7A__)
822 "linux-armv7",
823 #elif defined(__aarch64__)
824 "linux-aarch64",
825 #elif defined(__i686__)
826 "linux-i686",
827 #elif defined(__x86_64__)
828 "linux-x86_64",
829 #else
830 #warning no architecture dependant platform tag
831 #endif
832 #elif defined(TARGET_WINDOWS_DESKTOP)
833 "windx",
834 "windows",
835 #if defined(_M_IX86)
836 "windows-i686",
837 #elif defined(_M_AMD64)
838 "windows-x86_64",
839 #else
840 #error no architecture dependant platform tag
841 #endif
842 #elif defined(TARGET_WINDOWS_STORE)
843 "windowsstore",
844 #elif defined(TARGET_DARWIN_EMBEDDED)
845 "darwin_embedded",
846 #if defined(TARGET_DARWIN_IOS)
847 "ios",
848 #if defined(__aarch64__)
849 "ios-aarch64",
850 #else
851 #warning no architecture dependant platform tag
852 #endif
853 #elif defined(TARGET_DARWIN_TVOS)
854 "tvos",
855 "tvos-aarch64",
856 #endif
857 #elif defined(TARGET_DARWIN_OSX)
858 "osx",
859 #if defined(__x86_64__)
860 "osx64",
861 "osx-x86_64",
862 #elif defined(__aarch64__)
863 "osxarm64",
864 "osx-arm64",
865 #else
866 #warning no architecture dependant platform tag
867 #endif
868 #endif
871 return std::find_first_of(addon->m_platforms.begin(), addon->m_platforms.end(),
872 supportedPlatforms.begin(), supportedPlatforms.end()) != addon->m_platforms.end();