summaryrefslogtreecommitdiffstats
path: root/xbmc/addons/addoninfo
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/addons/addoninfo')
-rw-r--r--xbmc/addons/addoninfo/AddonExtensions.cpp68
-rw-r--r--xbmc/addons/addoninfo/AddonExtensions.h76
-rw-r--r--xbmc/addons/addoninfo/AddonInfo.cpp312
-rw-r--r--xbmc/addons/addoninfo/AddonInfo.h309
-rw-r--r--xbmc/addons/addoninfo/AddonInfoBuilder.cpp874
-rw-r--r--xbmc/addons/addoninfo/AddonInfoBuilder.h109
-rw-r--r--xbmc/addons/addoninfo/AddonType.cpp59
-rw-r--r--xbmc/addons/addoninfo/AddonType.h125
-rw-r--r--xbmc/addons/addoninfo/CMakeLists.txt11
9 files changed, 1943 insertions, 0 deletions
diff --git a/xbmc/addons/addoninfo/AddonExtensions.cpp b/xbmc/addons/addoninfo/AddonExtensions.cpp
new file mode 100644
index 0000000..b0bc3c5
--- /dev/null
+++ b/xbmc/addons/addoninfo/AddonExtensions.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "AddonExtensions.h"
+
+#include "utils/StringUtils.h"
+
+using namespace ADDON;
+
+bool SExtValue::asBoolean() const
+{
+ return StringUtils::EqualsNoCase(str, "true");
+}
+
+const SExtValue CAddonExtensions::GetValue(const std::string& id) const
+{
+ for (const auto& values : m_values)
+ {
+ for (const auto& value : values.second)
+ {
+ if (value.first == id)
+ return value.second;
+ }
+ }
+ return SExtValue("");
+}
+
+const EXT_VALUES& CAddonExtensions::GetValues() const
+{
+ return m_values;
+}
+
+const CAddonExtensions* CAddonExtensions::GetElement(const std::string& id) const
+{
+ for (const auto& child : m_children)
+ {
+ if (child.first == id)
+ return &child.second;
+ }
+
+ return nullptr;
+}
+
+const EXT_ELEMENTS CAddonExtensions::GetElements(const std::string& id) const
+{
+ if (id.empty())
+ return m_children;
+
+ EXT_ELEMENTS children;
+ for (const auto& child : m_children)
+ {
+ if (child.first == id)
+ children.push_back(std::make_pair(child.first, child.second));
+ }
+ return children;
+}
+
+void CAddonExtensions::Insert(const std::string& id, const std::string& value)
+{
+ EXT_VALUE extension;
+ extension.push_back(std::make_pair(id, SExtValue(value)));
+ m_values.push_back(std::make_pair(id, extension));
+}
diff --git a/xbmc/addons/addoninfo/AddonExtensions.h b/xbmc/addons/addoninfo/AddonExtensions.h
new file mode 100644
index 0000000..2ea2011
--- /dev/null
+++ b/xbmc/addons/addoninfo/AddonExtensions.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include <stdlib.h>
+#include <string>
+#include <vector>
+
+namespace ADDON
+{
+
+class CAddonInfoBuilder;
+class CAddonDatabaseSerializer;
+
+struct SExtValue
+{
+ explicit SExtValue(const std::string& strValue) : str(strValue) { }
+ const std::string& asString() const { return str; }
+ bool asBoolean() const;
+ int asInteger() const { return std::atoi(str.c_str()); }
+ float asFloat() const { return static_cast<float>(std::atof(str.c_str())); }
+ bool empty() const { return str.empty(); }
+ const std::string str;
+};
+
+class CExtValues;
+class CAddonExtensions;
+typedef std::vector<std::pair<std::string, CAddonExtensions>> EXT_ELEMENTS;
+typedef std::vector<std::pair<std::string, SExtValue>> EXT_VALUE;
+typedef std::vector<std::pair<std::string, CExtValues>> EXT_VALUES;
+
+class CExtValues : public EXT_VALUE
+{
+public:
+ CExtValues(const EXT_VALUE& values) : EXT_VALUE(values) { }
+
+ const SExtValue GetValue(const std::string& id) const
+ {
+ for (const auto& value : *this)
+ {
+ if (value.first == id)
+ return value.second;
+ }
+ return SExtValue("");
+ }
+};
+
+class CAddonExtensions
+{
+public:
+ CAddonExtensions() = default;
+ ~CAddonExtensions() = default;
+
+ const SExtValue GetValue(const std::string& id) const;
+ const EXT_VALUES& GetValues() const;
+ const CAddonExtensions* GetElement(const std::string& id) const;
+ const EXT_ELEMENTS GetElements(const std::string& id = "") const;
+
+ void Insert(const std::string& id, const std::string& value);
+
+private:
+ friend class CAddonInfoBuilder;
+ friend class CAddonDatabaseSerializer;
+
+ std::string m_point;
+ EXT_VALUES m_values;
+ EXT_ELEMENTS m_children;
+};
+
+} /* namespace ADDON */
diff --git a/xbmc/addons/addoninfo/AddonInfo.cpp b/xbmc/addons/addoninfo/AddonInfo.cpp
new file mode 100644
index 0000000..61bc087
--- /dev/null
+++ b/xbmc/addons/addoninfo/AddonInfo.cpp
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "AddonInfo.h"
+
+#include "FileItem.h"
+#include "LangInfo.h"
+#include "ServiceBroker.h"
+#include "addons/AddonManager.h"
+#include "addons/IAddon.h"
+#include "addons/addoninfo/AddonType.h"
+#include "filesystem/Directory.h"
+#include "guilib/LocalizeStrings.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+
+#include <algorithm>
+#include <array>
+#include <string_view>
+
+namespace ADDON
+{
+
+typedef struct
+{
+ std::string_view name;
+ std::string_view old_name;
+ AddonType type;
+ int pretty;
+ AddonInstanceSupport instance_support;
+ std::string_view icon;
+} TypeMapping;
+
+// clang-format off
+static constexpr const std::array<TypeMapping, 40> types =
+ {{
+ {"unknown", "", AddonType::UNKNOWN, 0, AddonInstanceSupport::SUPPORT_NONE, "" },
+ {"xbmc.metadata.scraper.albums", "", AddonType::SCRAPER_ALBUMS, 24016, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonAlbumInfo.png" },
+ {"xbmc.metadata.scraper.artists", "", AddonType::SCRAPER_ARTISTS, 24017, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonArtistInfo.png" },
+ {"xbmc.metadata.scraper.movies", "", AddonType::SCRAPER_MOVIES, 24007, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonMovieInfo.png" },
+ {"xbmc.metadata.scraper.musicvideos", "", AddonType::SCRAPER_MUSICVIDEOS, 24015, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonMusicVideoInfo.png" },
+ {"xbmc.metadata.scraper.tvshows", "", AddonType::SCRAPER_TVSHOWS, 24014, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonTvInfo.png" },
+ {"xbmc.metadata.scraper.library", "", AddonType::SCRAPER_LIBRARY, 24083, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonInfoLibrary.png" },
+ {"xbmc.ui.screensaver", "", AddonType::SCREENSAVER, 24008, AddonInstanceSupport::SUPPORT_OPTIONAL, "DefaultAddonScreensaver.png" },
+ {"xbmc.player.musicviz", "", AddonType::VISUALIZATION, 24010, AddonInstanceSupport::SUPPORT_OPTIONAL, "DefaultAddonVisualization.png" },
+ {"xbmc.python.pluginsource", "", AddonType::PLUGIN, 24005, AddonInstanceSupport::SUPPORT_NONE, "" },
+ {"xbmc.python.script", "", AddonType::SCRIPT, 24009, AddonInstanceSupport::SUPPORT_NONE, "" },
+ {"xbmc.python.weather", "", AddonType::SCRIPT_WEATHER, 24027, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonWeather.png" },
+ {"xbmc.python.lyrics", "", AddonType::SCRIPT_LYRICS, 24013, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonLyrics.png" },
+ {"xbmc.python.library", "", AddonType::SCRIPT_LIBRARY, 24081, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonHelper.png" },
+ {"xbmc.python.module", "", AddonType::SCRIPT_MODULE, 24082, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonLibrary.png" },
+ {"xbmc.subtitle.module", "", AddonType::SUBTITLE_MODULE, 24012, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonSubtitles.png" },
+ {"kodi.context.item", "", AddonType::CONTEXTMENU_ITEM, 24025, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonContextItem.png" },
+ {"kodi.game.controller", "", AddonType::GAME_CONTROLLER, 35050, AddonInstanceSupport::SUPPORT_OPTIONAL, "DefaultAddonGame.png" },
+ {"xbmc.gui.skin", "", AddonType::SKIN, 166, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonSkin.png" },
+ {"xbmc.webinterface", "", AddonType::WEB_INTERFACE, 199, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonWebSkin.png" },
+ {"xbmc.addon.repository", "", AddonType::REPOSITORY, 24011, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonRepository.png" },
+ {"kodi.pvrclient", "xbmc.pvrclient", AddonType::PVRDLL, 24019, AddonInstanceSupport::SUPPORT_SETTINGS, "DefaultAddonPVRClient.png" },
+ {"kodi.gameclient", "", AddonType::GAMEDLL, 35049, AddonInstanceSupport::SUPPORT_OPTIONAL, "DefaultAddonGame.png" },
+ {"kodi.peripheral", "", AddonType::PERIPHERALDLL, 35010, AddonInstanceSupport::SUPPORT_MANDATORY, "DefaultAddonPeripheral.png" },
+ {"xbmc.addon.video", "", AddonType::VIDEO, 1037, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonVideo.png" },
+ {"xbmc.addon.audio", "", AddonType::AUDIO, 1038, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonMusic.png" },
+ {"xbmc.addon.image", "", AddonType::IMAGE, 1039, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonPicture.png" },
+ {"xbmc.addon.executable", "", AddonType::EXECUTABLE, 1043, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonProgram.png" },
+ {"kodi.addon.game", "", AddonType::GAME, 35049, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonGame.png" },
+ {"kodi.audioencoder", "", AddonType::AUDIOENCODER, 200, AddonInstanceSupport::SUPPORT_MANDATORY, "DefaultAddonAudioEncoder.png" },
+ {"kodi.audiodecoder", "", AddonType::AUDIODECODER, 201, AddonInstanceSupport::SUPPORT_MANDATORY, "DefaultAddonAudioDecoder.png" },
+ {"xbmc.service", "", AddonType::SERVICE, 24018, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonService.png" },
+ {"kodi.resource.images", "", AddonType::RESOURCE_IMAGES, 24035, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonImages.png" },
+ {"kodi.resource.language", "", AddonType::RESOURCE_LANGUAGE, 24026, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonLanguage.png" },
+ {"kodi.resource.uisounds", "", AddonType::RESOURCE_UISOUNDS, 24006, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonUISounds.png" },
+ {"kodi.resource.games", "", AddonType::RESOURCE_GAMES, 35209, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonGame.png" },
+ {"kodi.resource.font", "", AddonType::RESOURCE_FONT, 13303, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonFont.png" },
+ {"kodi.inputstream", "", AddonType::INPUTSTREAM, 24048, AddonInstanceSupport::SUPPORT_MANDATORY, "DefaultAddonInputstream.png" },
+ {"kodi.vfs", "", AddonType::VFS, 39013, AddonInstanceSupport::SUPPORT_MANDATORY, "DefaultAddonVfs.png" },
+ {"kodi.imagedecoder", "", AddonType::IMAGEDECODER, 39015, AddonInstanceSupport::SUPPORT_MANDATORY, "DefaultAddonImageDecoder.png" },
+ }};
+// clang-format on
+
+const std::string& CAddonInfo::OriginName() const
+{
+ if (!m_originName)
+ {
+ ADDON::AddonPtr origin;
+ if (CServiceBroker::GetAddonMgr().GetAddon(m_origin, origin, ADDON::OnlyEnabled::CHOICE_NO))
+ m_originName = std::make_unique<std::string>(origin->Name());
+ else
+ m_originName = std::make_unique<std::string>(); // remember we tried to fetch the name
+ }
+ return *m_originName;
+}
+
+/**
+ * static public helper functions
+ *
+ */
+
+std::string CAddonInfo::TranslateType(AddonType type, bool pretty /*= false*/)
+{
+ for (const TypeMapping& map : types)
+ {
+ if (type == map.type)
+ {
+ if (pretty && map.pretty)
+ return g_localizeStrings.Get(map.pretty);
+ else
+ return std::string(map.name.data(), map.name.size());
+ }
+ }
+ return "";
+}
+
+AddonType CAddonInfo::TranslateType(const std::string& string)
+{
+ for (const TypeMapping& map : types)
+ {
+ if (string == map.name || (!map.old_name.empty() && string == map.old_name))
+ return map.type;
+ }
+
+ return AddonType::UNKNOWN;
+}
+
+std::string CAddonInfo::TranslateIconType(AddonType type)
+{
+ for (const TypeMapping& map : types)
+ {
+ if (type == map.type)
+ return std::string(map.icon.data(), map.icon.size());
+ }
+ return "";
+}
+
+AddonType CAddonInfo::TranslateSubContent(const std::string& content)
+{
+ if (content == "audio")
+ return AddonType::AUDIO;
+ else if (content == "image")
+ return AddonType::IMAGE;
+ else if (content == "executable")
+ return AddonType::EXECUTABLE;
+ else if (content == "video")
+ return AddonType::VIDEO;
+ else if (content == "game")
+ return AddonType::GAME;
+ else
+ return AddonType::UNKNOWN;
+}
+
+AddonInstanceSupport CAddonInfo::InstanceSupportType(AddonType type)
+{
+ const auto it = std::find_if(types.begin(), types.end(),
+ [type](const TypeMapping& entry) { return entry.type == type; });
+ if (it != types.end())
+ return it->instance_support;
+
+ return AddonInstanceSupport::SUPPORT_NONE;
+}
+
+CAddonInfo::CAddonInfo(std::string id, AddonType type) : m_id(std::move(id)), m_mainType(type)
+{
+
+}
+
+const CAddonType* CAddonInfo::Type(AddonType type) const
+{
+ static CAddonType dummy;
+
+ if (!m_types.empty())
+ {
+ if (type == AddonType::UNKNOWN)
+ return &m_types[0];
+
+ for (auto& addonType : m_types)
+ {
+ if (addonType.Type() == type)
+ return &addonType;
+ }
+ }
+
+ return &dummy;
+}
+
+bool CAddonInfo::HasType(AddonType type, bool mainOnly /*= false*/) const
+{
+ return (m_mainType == type ||
+ ProvidesSubContent(type, mainOnly ? m_mainType : AddonType::UNKNOWN));
+}
+
+bool CAddonInfo::ProvidesSubContent(AddonType content, AddonType mainType) const
+{
+ if (content == AddonType::UNKNOWN)
+ return false;
+
+ for (const auto& addonType : m_types)
+ {
+ if ((mainType == AddonType::UNKNOWN || addonType.Type() == mainType) &&
+ addonType.ProvidesSubContent(content))
+ return true;
+ }
+
+ return false;
+}
+
+bool CAddonInfo::ProvidesSeveralSubContents() const
+{
+ int contents = 0;
+ for (const auto& addonType : m_types)
+ contents += addonType.ProvidedSubContents();
+ return contents > 0 ? true : false;
+}
+
+bool CAddonInfo::MeetsVersion(const CAddonVersion& versionMin, const CAddonVersion& version) const
+{
+ return !(versionMin > m_version || version < m_minversion);
+}
+
+const CAddonVersion& CAddonInfo::DependencyMinVersion(const std::string& dependencyID) const
+{
+ auto it = std::find_if(m_dependencies.begin(), m_dependencies.end(),
+ [&](const DependencyInfo& other) { return other.id == dependencyID; });
+
+ if (it != m_dependencies.end())
+ return it->versionMin;
+
+ static CAddonVersion emptyVersion;
+ return emptyVersion;
+}
+
+const CAddonVersion& CAddonInfo::DependencyVersion(const std::string& dependencyID) const
+{
+ auto it = std::find_if(m_dependencies.begin(), m_dependencies.end(), [&](const DependencyInfo& other) { return other.id == dependencyID; });
+
+ if (it != m_dependencies.end())
+ return it->version;
+
+ static CAddonVersion emptyVersion;
+ return emptyVersion;
+}
+
+const std::string& CAddonInfo::GetTranslatedText(const std::unordered_map<std::string, std::string>& locales) const
+{
+ if (locales.size() == 1)
+ return locales.begin()->second;
+ else if (locales.empty())
+ return StringUtils::Empty;
+
+ // find the language from the list that matches the current locale best
+ std::string matchingLanguage = g_langInfo.GetLocale().FindBestMatch(locales);
+ if (matchingLanguage.empty())
+ matchingLanguage = KODI_ADDON_DEFAULT_LANGUAGE_CODE;
+
+ auto const& translatedValue = locales.find(matchingLanguage);
+ if (translatedValue != locales.end())
+ return translatedValue->second;
+ return StringUtils::Empty;
+}
+
+bool CAddonInfo::SupportsMultipleInstances() const
+{
+ switch (m_addonInstanceSupportType)
+ {
+ case AddonInstanceSupport::SUPPORT_MANDATORY:
+ case AddonInstanceSupport::SUPPORT_OPTIONAL:
+ return true;
+ case AddonInstanceSupport::SUPPORT_SETTINGS:
+ return m_supportsInstanceSettings;
+ case AddonInstanceSupport::SUPPORT_NONE:
+ default:
+ return false;
+ }
+}
+
+std::vector<AddonInstanceId> CAddonInfo::GetKnownInstanceIds() const
+{
+ static const std::vector<AddonInstanceId> singletonInstance = {ADDON_SINGLETON_INSTANCE_ID};
+
+ if (!m_supportsInstanceSettings)
+ return singletonInstance;
+
+ const std::string searchPath = StringUtils::Format("special://profile/addon_data/{}/", m_id);
+ CFileItemList items;
+ XFILE::CDirectory::GetDirectory(searchPath, items, ".xml", XFILE::DIR_FLAG_NO_FILE_DIRS);
+
+ std::vector<AddonInstanceId> ret;
+
+ for (const auto& item : items)
+ {
+ const std::string startName = "instance-settings-";
+ std::string filename = URIUtils::GetFileName(item->GetPath());
+ if (StringUtils::StartsWithNoCase(URIUtils::GetFileName(item->GetPath()), startName))
+ {
+ URIUtils::RemoveExtension(filename);
+ const std::string uid = filename.substr(startName.length());
+ if (!uid.empty() && StringUtils::IsInteger(uid))
+ ret.emplace_back(std::atoi(uid.c_str()));
+ }
+ }
+
+ // If no instances are used, create first as default.
+ if (ret.empty())
+ ret.emplace_back(ADDON_FIRST_INSTANCE_ID);
+
+ return ret;
+}
+
+} /* namespace ADDON */
diff --git a/xbmc/addons/addoninfo/AddonInfo.h b/xbmc/addons/addoninfo/AddonInfo.h
new file mode 100644
index 0000000..5998e2d
--- /dev/null
+++ b/xbmc/addons/addoninfo/AddonInfo.h
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "XBDateTime.h"
+#include "addons/AddonVersion.h"
+
+#include <map>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+namespace ADDON
+{
+
+enum class AddonType;
+
+class CAddonBuilder;
+class CAddonInfo;
+class CAddonType;
+typedef std::shared_ptr<CAddonInfo> AddonInfoPtr;
+typedef std::vector<AddonInfoPtr> AddonInfos;
+
+using AddonInstanceId = uint32_t;
+
+/*!
+ * Defines the default language code used as fallback in case the requested language is not
+ * available. Used, for instance, to handle content from addon.xml.
+ */
+constexpr const char* KODI_ADDON_DEFAULT_LANGUAGE_CODE = "en_GB";
+
+enum class AddonDisabledReason
+{
+ /// @brief Special reason for returning all disabled addons.
+ ///
+ /// Only used as an actual value when an addon is enabled.
+ NONE = 0,
+ USER = 1,
+ INCOMPATIBLE = 2,
+ PERMANENT_FAILURE = 3
+};
+
+enum class AddonOriginType
+{
+ /// @brief The type of the origin of an addon.
+ ///
+ /// Represents where an addon was installed from.
+ SYSTEM = 0, /// The addon is a system addon
+ REPOSITORY = 1, /// The addon origin is a repository
+ MANUAL = 2 /// The addon origin is a zip file, package or development build
+};
+
+//! @brief Reasons why an addon is not updateable
+enum class AddonUpdateRule
+{
+ ANY = 0, //!< used internally, not to be explicitly set
+ USER_DISABLED_AUTO_UPDATE = 1, //!< automatic updates disabled via AddonInfo dialog
+ PIN_OLD_VERSION = 2, //!< user downgraded to an older version
+ PIN_ZIP_INSTALL = 3, //!< user installed manually from zip
+};
+
+/*!
+ * @brief Independent add-on instance support.
+ *
+ * Used to be able to find out its instance path for the respective add-on types.
+ */
+enum class AddonInstanceSupport
+{
+ //! If add-on type does not support instances.
+ SUPPORT_NONE = 0,
+
+ //! If add-on type needs support for several instances individually.
+ SUPPORT_MANDATORY = 1,
+
+ //! If add-on type can support several instances individually.
+ SUPPORT_OPTIONAL = 2,
+
+ //! If add-on type supports multiple instances using independent settings.
+ SUPPORT_SETTINGS = 3,
+};
+
+/*!
+ * @brief Add-on state defined within addon.xml to report about the current addon
+ * lifecycle state.
+ *
+ * E.g. the add-on is broken and can no longer be used.
+ *
+ * XML examples:
+ * ~~~~~~~~~~~~~{.xml}
+ * <lifecyclestate type="broken" lang="en_GB">SOME TEXT</lifecyclestate>
+ * ~~~~~~~~~~~~~
+ */
+enum class AddonLifecycleState
+{
+ NORMAL = 0, //!< Used if an add-on has no special lifecycle state which is the default state
+ DEPRECATED = 1, //!< the add-on should be marked as deprecated but is still usable
+ BROKEN = 2, //!< the add-on should marked as broken in the repository
+};
+
+struct DependencyInfo
+{
+ std::string id;
+ CAddonVersion versionMin, version;
+ bool optional;
+ DependencyInfo(std::string id,
+ const CAddonVersion& versionMin,
+ const CAddonVersion& version,
+ bool optional)
+ : id(std::move(id)),
+ versionMin(versionMin.empty() ? version : versionMin),
+ version(version),
+ optional(optional)
+ {
+ }
+
+ bool operator==(const DependencyInfo& rhs) const
+ {
+ return id == rhs.id && versionMin == rhs.versionMin && version == rhs.version &&
+ optional == rhs.optional;
+ }
+
+ bool operator!=(const DependencyInfo& rhs) const
+ {
+ return !(rhs == *this);
+ }
+};
+
+typedef std::map<std::string, std::string> InfoMap;
+typedef std::map<std::string, std::string> ArtMap;
+
+class CAddonInfoBuilder;
+
+class CAddonInfo
+{
+public:
+ CAddonInfo() = default;
+ CAddonInfo(std::string id, AddonType type);
+
+ void SetMainType(AddonType type) { m_mainType = type; }
+ void SetBinary(bool isBinary) { m_isBinary = isBinary; }
+ void SetLibName(const std::string& libname) { m_libname = libname; }
+ void SetPath(const std::string& path) { m_path = path; }
+ void AddExtraInfo(const std::string& idName, const std::string& value) { m_extrainfo[idName] = value; }
+ void SetLastUsed(const CDateTime& dateTime) { m_lastUsed = dateTime; }
+
+ const std::string& ID() const { return m_id; }
+
+ /**
+ * @brief To get the main type of this addon
+ *
+ * This is the first type defined in addon.xml.
+ *
+ * @return The used main type of addon
+ */
+ AddonType MainType() const { return m_mainType; }
+
+ /**
+ * @brief To check addon contains a type
+ *
+ * @param[in] type The to checked type identifier
+ * @param[in] mainOnly to check only in first defined main addon inside addon.xml
+ * @return true in case the wanted type is supported, false if not
+ */
+ bool HasType(AddonType type, bool mainOnly = false) const;
+
+ /**
+ * @brief To get all available types inside the addon
+ *
+ * To have all `<extension point="..." />` defined in addon.xml inside a list.
+ *
+ * @return List of all supported types
+ */
+ const std::vector<CAddonType>& Types() const { return m_types; }
+
+ /**
+ * @brief The get for given addon type information and extension data
+ *
+ * @param[in] type The wanted type data
+ * @return addon type class with @ref CAddonExtensions as information
+ *
+ * @note This function return never a "nullptr", in case the wanted type is
+ * not supported, becomes a dummy of @ref CAddonType given.
+ *
+ * ------------------------------------------------------------------------
+ *
+ * **Example:**
+ * ~~~~~~~~~~~~~{.cpp}
+ * // To get <extension ... name="blablabla" /> from addon.xml
+ * std::string name = Type(ADDON_...)->GetValue("@name").asString();
+ * ~~~~~~~~~~~~~
+ *
+ */
+ const CAddonType* Type(AddonType type) const;
+
+ bool ProvidesSubContent(AddonType content, AddonType mainType) const;
+ bool ProvidesSeveralSubContents() const;
+
+ const CAddonVersion& Version() const { return m_version; }
+ const CAddonVersion& MinVersion() const { return m_minversion; }
+ bool IsBinary() const { return m_isBinary; }
+ const CAddonVersion& DependencyMinVersion(const std::string& dependencyID) const;
+ const CAddonVersion& DependencyVersion(const std::string& dependencyID) const;
+ const std::string& Name() const { return m_name; }
+ const std::string& License() const { return m_license; }
+ const std::string& Summary() const { return GetTranslatedText(m_summary); }
+ const std::string& Description() const { return GetTranslatedText(m_description); }
+ const std::string& LibName() const { return m_libname; }
+ const std::string& Author() const { return m_author; }
+ const std::string& Source() const { return m_source; }
+ const std::string& Website() const { return m_website; }
+ const std::string& Forum() const { return m_forum; }
+ const std::string& EMail() const { return m_email; }
+ const std::string& Path() const { return m_path; }
+ const std::string& ProfilePath() const { return m_profilePath; }
+ const std::string& ChangeLog() const { return GetTranslatedText(m_changelog); }
+ const std::string& Icon() const { return m_icon; }
+ const ArtMap& Art() const { return m_art; }
+ const std::vector<std::string>& Screenshots() const { return m_screenshots; }
+ const std::string& Disclaimer() const { return GetTranslatedText(m_disclaimer); }
+ const std::vector<DependencyInfo>& GetDependencies() const { return m_dependencies; }
+ AddonLifecycleState LifecycleState() const { return m_lifecycleState; }
+ const std::string& LifecycleStateDescription() const
+ {
+ return GetTranslatedText(m_lifecycleStateDescription);
+ }
+ const std::string& Origin() const { return m_origin; }
+ const std::string& OriginName() const;
+
+ const InfoMap& ExtraInfo() const { return m_extrainfo; }
+
+ bool MeetsVersion(const CAddonVersion& versionMin, const CAddonVersion& version) const;
+ uint64_t PackageSize() const { return m_packageSize; }
+ CDateTime InstallDate() const { return m_installDate; }
+ CDateTime LastUpdated() const { return m_lastUpdated; }
+ CDateTime LastUsed() const { return m_lastUsed; }
+
+ bool SupportsMultipleInstances() const;
+ AddonInstanceSupport InstanceUseType() const { return m_addonInstanceSupportType; }
+
+ bool SupportsAddonSettings() const { return m_supportsAddonSettings; }
+ bool SupportsInstanceSettings() const { return m_supportsInstanceSettings; }
+ std::vector<AddonInstanceId> GetKnownInstanceIds() const;
+
+ /*!
+ * @brief Utilities to translate add-on parts to his requested part.
+ */
+ //@{
+ static std::string TranslateType(AddonType type, bool pretty = false);
+ static std::string TranslateIconType(AddonType type);
+ static AddonType TranslateType(const std::string& string);
+ static AddonType TranslateSubContent(const std::string& content);
+ static AddonInstanceSupport InstanceSupportType(AddonType type);
+ //@}
+
+private:
+ friend class CAddonInfoBuilder;
+ friend class CAddonInfoBuilderFromDB;
+
+ std::string m_id;
+ AddonType m_mainType{};
+ std::vector<CAddonType> m_types;
+
+ CAddonVersion m_version;
+ CAddonVersion m_minversion;
+ bool m_isBinary = false;
+ std::string m_name;
+ std::string m_license;
+ std::unordered_map<std::string, std::string> m_summary;
+ std::unordered_map<std::string, std::string> m_description;
+ std::string m_author;
+ std::string m_source;
+ std::string m_website;
+ std::string m_forum;
+ std::string m_email;
+ std::string m_path;
+ std::string m_profilePath;
+ std::unordered_map<std::string, std::string> m_changelog;
+ std::string m_icon;
+ ArtMap m_art;
+ std::vector<std::string> m_screenshots;
+ std::unordered_map<std::string, std::string> m_disclaimer;
+ std::vector<DependencyInfo> m_dependencies;
+ AddonLifecycleState m_lifecycleState = AddonLifecycleState::NORMAL;
+ std::unordered_map<std::string, std::string> m_lifecycleStateDescription;
+ CDateTime m_installDate;
+ CDateTime m_lastUpdated;
+ CDateTime m_lastUsed;
+ std::string m_origin;
+ mutable std::unique_ptr<std::string> m_originName; // @todo use std::optional once we use c++17
+ uint64_t m_packageSize = 0;
+ std::string m_libname;
+ InfoMap m_extrainfo;
+ std::vector<std::string> m_platforms;
+ AddonInstanceSupport m_addonInstanceSupportType{AddonInstanceSupport::SUPPORT_NONE};
+ bool m_supportsAddonSettings{false};
+ bool m_supportsInstanceSettings{false};
+
+ const std::string& GetTranslatedText(const std::unordered_map<std::string, std::string>& locales) const;
+};
+
+} /* namespace ADDON */
diff --git a/xbmc/addons/addoninfo/AddonInfoBuilder.cpp b/xbmc/addons/addoninfo/AddonInfoBuilder.cpp
new file mode 100644
index 0000000..268efa6
--- /dev/null
+++ b/xbmc/addons/addoninfo/AddonInfoBuilder.cpp
@@ -0,0 +1,874 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "AddonInfoBuilder.h"
+
+#include "CompileInfo.h"
+#include "LangInfo.h"
+#include "addons/Repository.h"
+#include "addons/addoninfo/AddonInfo.h"
+#include "addons/addoninfo/AddonType.h"
+#include "filesystem/File.h"
+#include "filesystem/SpecialProtocol.h"
+#include "utils/JSONVariantParser.h"
+#include "utils/JSONVariantWriter.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+#include "utils/Variant.h"
+#include "utils/XBMCTinyXML.h"
+#include "utils/log.h"
+
+#include <algorithm>
+#include <memory>
+#include <regex>
+
+namespace
+{
+// Note that all of these characters are url-safe
+const std::string VALID_ADDON_IDENTIFIER_CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_@!$";
+}
+
+namespace ADDON
+{
+
+CAddonInfoBuilderFromDB::CAddonInfoBuilderFromDB() : m_addonInfo(std::make_shared<CAddonInfo>())
+{
+}
+
+void CAddonInfoBuilderFromDB::SetId(std::string id)
+{
+ m_addonInfo->m_id = std::move(id);
+}
+
+void CAddonInfoBuilderFromDB::SetName(std::string name)
+{
+ m_addonInfo->m_name = std::move(name);
+}
+
+void CAddonInfoBuilderFromDB::SetLicense(std::string license)
+{
+ m_addonInfo->m_license = std::move(license);
+}
+
+void CAddonInfoBuilderFromDB::SetSummary(std::string summary)
+{
+ m_addonInfo->m_summary.insert(std::pair<std::string, std::string>("unk", std::move(summary)));
+}
+
+void CAddonInfoBuilderFromDB::SetDescription(std::string description)
+{
+ m_addonInfo->m_description.insert(
+ std::pair<std::string, std::string>("unk", std::move(description)));
+}
+
+void CAddonInfoBuilderFromDB::SetDisclaimer(std::string disclaimer)
+{
+ m_addonInfo->m_disclaimer.insert(
+ std::pair<std::string, std::string>("unk", std::move(disclaimer)));
+}
+
+void CAddonInfoBuilderFromDB::SetAuthor(std::string author)
+{
+ m_addonInfo->m_author = std::move(author);
+}
+
+void CAddonInfoBuilderFromDB::SetSource(std::string source)
+{
+ m_addonInfo->m_source = std::move(source);
+}
+
+void CAddonInfoBuilderFromDB::SetWebsite(std::string website)
+{
+ m_addonInfo->m_website = std::move(website);
+}
+
+void CAddonInfoBuilderFromDB::SetForum(std::string forum)
+{
+ m_addonInfo->m_forum = std::move(forum);
+}
+
+void CAddonInfoBuilderFromDB::SetEMail(std::string email)
+{
+ m_addonInfo->m_email = std::move(email);
+}
+
+void CAddonInfoBuilderFromDB::SetIcon(std::string icon)
+{
+ m_addonInfo->m_icon = std::move(icon);
+}
+
+void CAddonInfoBuilderFromDB::SetArt(const std::string& type, std::string value)
+{
+ m_addonInfo->m_art[type] = std::move(value);
+}
+
+void CAddonInfoBuilderFromDB::SetArt(std::map<std::string, std::string> art)
+{
+ m_addonInfo->m_art = std::move(art);
+}
+
+void CAddonInfoBuilderFromDB::SetScreenshots(std::vector<std::string> screenshots)
+{
+ m_addonInfo->m_screenshots = std::move(screenshots);
+}
+
+void CAddonInfoBuilderFromDB::SetChangelog(std::string changelog)
+{
+ m_addonInfo->m_changelog.insert(std::pair<std::string, std::string>("unk", std::move(changelog)));
+}
+
+void CAddonInfoBuilderFromDB::SetLifecycleState(AddonLifecycleState state, std::string description)
+{
+ m_addonInfo->m_lifecycleState = state;
+ m_addonInfo->m_lifecycleStateDescription.emplace("unk", std::move(description));
+}
+
+void CAddonInfoBuilderFromDB::SetPath(std::string path)
+{
+ m_addonInfo->m_path = std::move(path);
+}
+
+void CAddonInfoBuilderFromDB::SetLibName(std::string libname)
+{
+ m_addonInfo->m_libname = std::move(libname);
+}
+
+void CAddonInfoBuilderFromDB::SetVersion(CAddonVersion version)
+{
+ m_addonInfo->m_version = std::move(version);
+}
+
+void CAddonInfoBuilderFromDB::SetDependencies(std::vector<DependencyInfo> dependencies)
+{
+ m_addonInfo->m_dependencies = std::move(dependencies);
+}
+
+void CAddonInfoBuilderFromDB::SetExtrainfo(InfoMap extrainfo)
+{
+ m_addonInfo->m_extrainfo = std::move(extrainfo);
+}
+
+void CAddonInfoBuilderFromDB::SetInstallDate(const CDateTime& installDate)
+{
+ m_addonInfo->m_installDate = installDate;
+}
+
+void CAddonInfoBuilderFromDB::SetLastUpdated(const CDateTime& lastUpdated)
+{
+ m_addonInfo->m_lastUpdated = lastUpdated;
+}
+
+void CAddonInfoBuilderFromDB::SetLastUsed(const CDateTime& lastUsed)
+{
+ m_addonInfo->m_lastUsed = lastUsed;
+}
+
+void CAddonInfoBuilderFromDB::SetOrigin(std::string origin)
+{
+ m_addonInfo->m_origin = std::move(origin);
+}
+
+void CAddonInfoBuilderFromDB::SetPackageSize(uint64_t size)
+{
+ m_addonInfo->m_packageSize = size;
+}
+
+void CAddonInfoBuilderFromDB::SetExtensions(CAddonType addonType)
+{
+ if (!addonType.GetValue("provides").empty())
+ addonType.SetProvides(addonType.GetValue("provides").asString());
+
+ m_addonInfo->m_types.push_back(std::move(addonType));
+ m_addonInfo->m_mainType = addonType.m_type;
+}
+
+AddonInfoPtr CAddonInfoBuilder::Generate(const std::string& id, AddonType type)
+{
+ // Check addon identifier for forbidden characters
+ // The identifier is used e.g. in URLs so we shouldn't allow just
+ // any character to go through.
+ if (id.empty() || id.find_first_not_of(VALID_ADDON_IDENTIFIER_CHARACTERS) != std::string::npos)
+ {
+ CLog::Log(LOGERROR, "CAddonInfoBuilder::{}: identifier '{}' is invalid", __FUNCTION__, id);
+ return nullptr;
+ }
+
+ AddonInfoPtr addon = std::make_shared<CAddonInfo>();
+ addon->m_id = id;
+ addon->m_mainType = type;
+ return addon;
+}
+
+AddonInfoPtr CAddonInfoBuilder::Generate(const std::string& addonPath, bool platformCheck /*= true*/)
+{
+ auto addonRealPath = CSpecialProtocol::TranslatePath(addonPath);
+
+ CXBMCTinyXML xmlDoc;
+ if (!xmlDoc.LoadFile(URIUtils::AddFileToFolder(addonRealPath, "addon.xml")))
+ {
+ CLog::Log(LOGERROR, "CAddonInfoBuilder::{}: Unable to load '{}', Line {}\n{}",
+ __FUNCTION__,
+ URIUtils::AddFileToFolder(addonRealPath, "addon.xml"),
+ xmlDoc.ErrorRow(),
+ xmlDoc.ErrorDesc());
+ return nullptr;
+ }
+
+ AddonInfoPtr addon = std::make_shared<CAddonInfo>();
+ if (!ParseXML(addon, xmlDoc.RootElement(), addonRealPath))
+ return nullptr;
+
+ if (!platformCheck || PlatformSupportsAddon(addon))
+ return addon;
+
+ return nullptr;
+}
+
+AddonInfoPtr CAddonInfoBuilder::Generate(const TiXmlElement* baseElement,
+ const RepositoryDirInfo& repo,
+ bool platformCheck /*= true*/)
+{
+ AddonInfoPtr addon = std::make_shared<CAddonInfo>();
+ if (!ParseXML(addon, baseElement, repo.datadir, repo))
+ return nullptr;
+
+ if (!platformCheck || PlatformSupportsAddon(addon))
+ return addon;
+
+ return nullptr;
+}
+
+void CAddonInfoBuilder::SetInstallData(const AddonInfoPtr& addon, const CDateTime& installDate, const CDateTime& lastUpdated,
+ const CDateTime& lastUsed, const std::string& origin)
+{
+ if (!addon)
+ return;
+
+ addon->m_installDate = installDate;
+ addon->m_lastUpdated = lastUpdated;
+ addon->m_lastUsed = lastUsed;
+ addon->m_origin = origin;
+}
+
+bool CAddonInfoBuilder::ParseXML(const AddonInfoPtr& addon,
+ const TiXmlElement* element,
+ const std::string& addonPath)
+{
+ return ParseXML(addon, element, addonPath, {});
+}
+
+bool CAddonInfoBuilder::ParseXML(const AddonInfoPtr& addon,
+ const TiXmlElement* element,
+ const std::string& addonPath,
+ const RepositoryDirInfo& repo)
+{
+ /*
+ * Following values currently not set from creator:
+ * - CDateTime installDate;
+ * - CDateTime lastUpdated;
+ * - CDateTime lastUsed;
+ * - std::string origin;
+ */
+
+ if (!StringUtils::EqualsNoCase(element->Value(), "addon"))
+ {
+ CLog::Log(LOGERROR, "CAddonInfoBuilder::{}: file from '{}' doesn't contain <addon>", __FUNCTION__, addonPath);
+ return false;
+ }
+
+ /*
+ * The function variable "repo" is only used when reading data stored on the internet.
+ * A boolean value is then set here for easier identification.
+ */
+ const bool isRepoXMLContent = !repo.datadir.empty();
+
+ /*
+ * Parse addon.xml:
+ * <addon id="???"
+ * name="???"
+ * version="???"
+ * provider-name="???">
+ */
+ addon->m_id = StringUtils::CreateFromCString(element->Attribute("id"));
+ addon->m_name = StringUtils::CreateFromCString(element->Attribute("name"));
+ addon->m_author = StringUtils::CreateFromCString(element->Attribute("provider-name"));
+
+ const std::string version = StringUtils::CreateFromCString(element->Attribute("version"));
+ addon->m_version = CAddonVersion(version);
+
+ if (addon->m_id.empty() || addon->m_version.empty())
+ {
+ CLog::Log(LOGERROR, "CAddonInfoBuilder::{}: file '{}' doesn't contain required values on <addon ... > id='{}', version='{}'",
+ __FUNCTION__,
+ addonPath,
+ addon->m_id.empty() ? "missing" : addon->m_id,
+ addon->m_version.empty() ? "missing" : addon->m_version.asString());
+ return false;
+ }
+
+ // Check addon identifier for forbidden characters
+ // The identifier is used e.g. in URLs so we shouldn't allow just
+ // any character to go through.
+ if (addon->m_id.find_first_not_of(VALID_ADDON_IDENTIFIER_CHARACTERS) != std::string::npos)
+ {
+ CLog::Log(LOGERROR, "CAddonInfoBuilder::{}: identifier {} is invalid", __FUNCTION__, addon->m_id);
+ return false;
+ }
+
+ /*
+ * Parse addon.xml:
+ * <backwards-compatibility abi="???"/>
+ */
+ const TiXmlElement* backwards = element->FirstChildElement("backwards-compatibility");
+ if (backwards)
+ {
+ const std::string minVersion = StringUtils::CreateFromCString(backwards->Attribute("abi"));
+ addon->m_minversion = CAddonVersion(minVersion);
+ }
+
+ /*
+ * Parse addon.xml:
+ * <requires>
+ * <import addon="???" minversion="???" version="???" optional="???"/>
+ * </requires>
+ */
+ const TiXmlElement* requires = element->FirstChildElement("requires");
+ if (requires)
+ {
+ for (const TiXmlElement* child = requires->FirstChildElement("import"); child != nullptr; child = child->NextSiblingElement("import"))
+ {
+ if (child->Attribute("addon"))
+ {
+ const std::string minVersion =
+ StringUtils::CreateFromCString(child->Attribute("minversion"));
+ const std::string version = StringUtils::CreateFromCString(child->Attribute("version"));
+
+ bool optional = false;
+ child->QueryBoolAttribute("optional", &optional);
+
+ addon->m_dependencies.emplace_back(child->Attribute("addon"), CAddonVersion(minVersion),
+ CAddonVersion(version), optional);
+ }
+ }
+ }
+
+ std::string assetBasePath;
+ if (!isRepoXMLContent && !addonPath.empty())
+ {
+ // Default for add-on information not loaded from repository
+ assetBasePath = addonPath;
+ addon->m_path = addonPath;
+ }
+ else
+ {
+ assetBasePath = URIUtils::AddFileToFolder(repo.artdir, addon->m_id);
+ addon->m_path = URIUtils::AddFileToFolder(repo.datadir, addon->m_id, StringUtils::Format("{}-{}.zip", addon->m_id, addon->m_version.asString()));
+ }
+
+ addon->m_profilePath = StringUtils::Format("special://profile/addon_data/{}/", addon->m_id);
+
+ /*
+ * Parse addon.xml:
+ * <extension>
+ * ...
+ * </extension>
+ */
+ for (const TiXmlElement* child = element->FirstChildElement("extension"); child != nullptr; child = child->NextSiblingElement("extension"))
+ {
+ const std::string point = StringUtils::CreateFromCString(child->Attribute("point"));
+
+ if (point == "kodi.addon.metadata" || point == "xbmc.addon.metadata")
+ {
+ /*
+ * Parse addon.xml "<path">...</path>" (special related to repository path),
+ * do first and if present override the default. Also set assetBasePath to
+ * find screenshots and icons.
+ */
+ element = child->FirstChildElement("path");
+ if (element && element->GetText() != nullptr && !repo.datadir.empty())
+ {
+ addon->m_path = URIUtils::AddFileToFolder(repo.datadir, element->GetText());
+ assetBasePath = URIUtils::GetDirectory(URIUtils::AddFileToFolder(repo.artdir, element->GetText()));
+ }
+
+ /*
+ * Parse addon.xml "<summary lang="..">...</summary>"
+ */
+ GetTextList(child, "summary", addon->m_summary);
+
+ /*
+ * Parse addon.xml "<description lang="..">...</description>"
+ */
+ GetTextList(child, "description", addon->m_description);
+
+ /*
+ * Parse addon.xml "<disclaimer lang="..">...</disclaimer>"
+ */
+ GetTextList(child, "disclaimer", addon->m_disclaimer);
+
+ /*
+ * Parse addon.xml "<assets>...</assets>"
+ */
+ const TiXmlElement* element = child->FirstChildElement("assets");
+ if (element)
+ {
+ for (const TiXmlElement* elementsAssets = element->FirstChildElement(); elementsAssets != nullptr; elementsAssets = elementsAssets->NextSiblingElement())
+ {
+ std::string value = elementsAssets->Value();
+ if (value == "icon")
+ {
+ if (elementsAssets->GetText() != nullptr)
+ addon->m_icon = URIUtils::AddFileToFolder(assetBasePath, elementsAssets->GetText());
+ }
+ else if (value == "screenshot")
+ {
+ if (elementsAssets->GetText() != nullptr)
+ addon->m_screenshots.emplace_back(URIUtils::AddFileToFolder(assetBasePath, elementsAssets->GetText()));
+ }
+ else if (value == "fanart")
+ {
+ if (elementsAssets->GetText() != nullptr)
+ addon->m_art[value] = URIUtils::AddFileToFolder(assetBasePath, elementsAssets->GetText());
+ }
+ else if (value == "banner")
+ {
+ if (elementsAssets->GetText() != nullptr)
+ addon->m_art[value] = URIUtils::AddFileToFolder(assetBasePath, elementsAssets->GetText());
+ }
+ else if (value == "clearlogo")
+ {
+ if (elementsAssets->GetText() != nullptr)
+ addon->m_art[value] = URIUtils::AddFileToFolder(assetBasePath, elementsAssets->GetText());
+ }
+ else if (value == "thumb")
+ {
+ if (elementsAssets->GetText() != nullptr)
+ addon->m_art[value] =
+ URIUtils::AddFileToFolder(assetBasePath, elementsAssets->GetText());
+ }
+ }
+ }
+
+ /* Parse addon.xml "<platform">...</platform>" */
+ element = child->FirstChildElement("platform");
+ if (element && element->GetText() != nullptr)
+ {
+ auto platforms = StringUtils::Split(element->GetText(),
+ {" ", "\t", "\n", "\r"});
+ platforms.erase(std::remove_if(platforms.begin(), platforms.end(),
+ [](const std::string& platform) { return platform.empty(); }),
+ platforms.cend());
+ addon->m_platforms = platforms;
+ }
+
+ /* Parse addon.xml "<license">...</license>" */
+ element = child->FirstChildElement("license");
+ if (element && element->GetText() != nullptr)
+ addon->m_license = element->GetText();
+
+ /* Parse addon.xml "<source">...</source>" */
+ element = child->FirstChildElement("source");
+ if (element && element->GetText() != nullptr)
+ addon->m_source = element->GetText();
+
+ /* Parse addon.xml "<email">...</email>" */
+ element = child->FirstChildElement("email");
+ if (element && element->GetText() != nullptr)
+ addon->m_email = element->GetText();
+
+ /* Parse addon.xml "<website">...</website>" */
+ element = child->FirstChildElement("website");
+ if (element && element->GetText() != nullptr)
+ addon->m_website = element->GetText();
+
+ /* Parse addon.xml "<forum">...</forum>" */
+ element = child->FirstChildElement("forum");
+ if (element && element->GetText() != nullptr)
+ addon->m_forum = element->GetText();
+
+ /* Parse addon.xml "<broken">...</broken>"
+ * NOTE: Replaced with <lifecyclestate>, available for backward compatibility */
+ element = child->FirstChildElement("broken");
+ if (element && element->GetText() != nullptr)
+ {
+ addon->m_lifecycleState = AddonLifecycleState::BROKEN;
+ addon->m_lifecycleStateDescription.emplace(KODI_ADDON_DEFAULT_LANGUAGE_CODE,
+ element->GetText());
+ }
+
+ /* Parse addon.xml "<lifecyclestate">...</lifecyclestate>" */
+ element = child->FirstChildElement("lifecyclestate");
+ if (element && element->GetText() != nullptr)
+ {
+ const char* lang = element->Attribute("type");
+ if (lang)
+ {
+ if (strcmp(lang, "broken") == 0)
+ addon->m_lifecycleState = AddonLifecycleState::BROKEN;
+ else if (strcmp(lang, "deprecated") == 0)
+ addon->m_lifecycleState = AddonLifecycleState::DEPRECATED;
+ else
+ addon->m_lifecycleState = AddonLifecycleState::NORMAL;
+
+ GetTextList(child, "lifecyclestate", addon->m_lifecycleStateDescription);
+ }
+ }
+
+ /* Parse addon.xml "<language">...</language>" */
+ element = child->FirstChildElement("language");
+ if (element && element->GetText() != nullptr)
+ addon->AddExtraInfo("language", element->GetText());
+
+ /* Parse addon.xml "<reuselanguageinvoker">...</reuselanguageinvoker>" */
+ element = child->FirstChildElement("reuselanguageinvoker");
+ if (element && element->GetText() != nullptr)
+ addon->AddExtraInfo("reuselanguageinvoker", element->GetText());
+
+ /* Parse addon.xml "<size">...</size>" */
+ element = child->FirstChildElement("size");
+ if (element && element->GetText() != nullptr)
+ addon->m_packageSize = StringUtils::ToUint64(element->GetText(), 0);
+
+ /* Parse addon.xml "<news lang="..">...</news>"
+ *
+ * In the event that the changelog (news) in addon.xml is empty, check
+ * whether it is an installed addon and read a changelog.txt as a
+ * replacement, if available. */
+ GetTextList(child, "news", addon->m_changelog);
+ if (addon->m_changelog.empty() && !isRepoXMLContent && !addonPath.empty())
+ {
+ using XFILE::CFile;
+
+ const std::string changelog = URIUtils::AddFileToFolder(addonPath, "changelog.txt");
+ if (CFile::Exists(changelog))
+ {
+ CFile file;
+ std::vector<uint8_t> buf;
+ if (file.LoadFile(changelog, buf) > 0)
+ addon->m_changelog[KODI_ADDON_DEFAULT_LANGUAGE_CODE].assign(
+ reinterpret_cast<char*>(buf.data()), buf.size());
+ }
+ }
+ }
+ else
+ {
+ AddonType type = CAddonInfo::TranslateType(point);
+ if (type == AddonType::UNKNOWN || type >= AddonType::MAX_TYPES)
+ {
+ CLog::Log(LOGERROR, "CAddonInfoBuilder::{}: file '{}' doesn't contain a valid add-on type name ({})", __FUNCTION__, addon->m_path, point);
+ return false;
+ }
+
+ CAddonType addonType(type);
+ if (ParseXMLTypes(addonType, addon, child))
+ addon->m_types.emplace_back(std::move(addonType));
+ }
+ }
+
+ /*
+ * If nothing is defined in addon.xml set addon as unknown to have minimum one
+ * instance type present.
+ */
+ if (addon->m_types.empty())
+ {
+ CAddonType addonType(AddonType::UNKNOWN);
+ addon->m_types.emplace_back(std::move(addonType));
+ }
+
+ addon->m_mainType = addon->m_types[0].Type();
+ addon->m_libname = addon->m_types[0].m_libname;
+ if (!addon->m_types[0].GetValue("provides").empty())
+ addon->AddExtraInfo("provides", addon->m_types[0].GetValue("provides").asString());
+
+ // Ensure binary types have a valid library for the platform
+ if (addon->m_mainType == AddonType::VISUALIZATION ||
+ addon->m_mainType == AddonType::SCREENSAVER || addon->m_mainType == AddonType::PVRDLL ||
+ addon->m_mainType == AddonType::AUDIOENCODER ||
+ addon->m_mainType == AddonType::AUDIODECODER || addon->m_mainType == AddonType::VFS ||
+ addon->m_mainType == AddonType::IMAGEDECODER || addon->m_mainType == AddonType::INPUTSTREAM ||
+ addon->m_mainType == AddonType::PERIPHERALDLL || addon->m_mainType == AddonType::GAMEDLL)
+ {
+ if (addon->m_libname.empty())
+ {
+ // Prevent log file entry if data is from repository, there normal on
+ // addons for other OS's
+ if (!isRepoXMLContent)
+ CLog::Log(LOGERROR, "CAddonInfoBuilder::{}: addon.xml from '{}' for binary type '{}' doesn't contain library and addon becomes ignored",
+ __FUNCTION__, addon->ID(), CAddonInfo::TranslateType(addon->m_mainType));
+ return false;
+ }
+ }
+
+ if (!isRepoXMLContent)
+ {
+ using XFILE::CFile;
+ if (CFile::Exists(URIUtils::AddFileToFolder(addonPath, "resources", "settings.xml")))
+ addon->m_supportsAddonSettings = true;
+ if (CFile::Exists(URIUtils::AddFileToFolder(addonPath, "resources", "instance-settings.xml")))
+ addon->m_supportsInstanceSettings = true;
+ }
+
+ addon->m_addonInstanceSupportType = CAddonInfo::InstanceSupportType(addon->m_mainType);
+
+ return true;
+}
+
+bool CAddonInfoBuilder::ParseXMLTypes(CAddonType& addonType,
+ const AddonInfoPtr& info,
+ const TiXmlElement* child)
+{
+ if (child)
+ {
+ addonType.m_path = info->Path();
+
+ // Get add-on library file name (if present)
+ const char* library = child->Attribute("library");
+ if (library == nullptr)
+ library = GetPlatformLibraryName(child);
+ if (library != nullptr)
+ {
+ addonType.m_libname = library;
+
+ try
+ {
+ // linux is different and has the version number after the suffix
+ static const std::regex libRegex("^.*" +
+ CCompileInfo::CCompileInfo::GetSharedLibrarySuffix() +
+ "\\.?[0-9]*\\.?[0-9]*\\.?[0-9]*$");
+ if (std::regex_match(library, libRegex))
+ {
+ info->SetBinary(true);
+ CLog::Log(LOGDEBUG, "CAddonInfoBuilder::{}: Binary addon found: {}", __func__,
+ info->ID());
+ }
+ }
+ catch (const std::regex_error& e)
+ {
+ CLog::Log(LOGERROR, "CAddonInfoBuilder::{}: Regex error caught: {}", __func__,
+ e.what());
+ }
+ }
+
+ if (!ParseXMLExtension(addonType, child))
+ {
+ CLog::Log(LOGERROR, "CAddonInfoBuilder::{}: addon.xml file doesn't contain a valid add-on extensions ({})", __FUNCTION__, info->ID());
+ return false;
+ }
+ if (!addonType.GetValue("provides").empty())
+ addonType.SetProvides(addonType.GetValue("provides").asString());
+ return true;
+ }
+ return false;
+}
+
+bool CAddonInfoBuilder::ParseXMLExtension(CAddonExtensions& addonExt, const TiXmlElement* element)
+{
+ addonExt.m_point = StringUtils::CreateFromCString(element->Attribute("point"));
+
+ EXT_VALUE extension;
+ const TiXmlAttribute* attribute = element->FirstAttribute();
+ while (attribute)
+ {
+ std::string name = attribute->Name();
+ if (name != "point")
+ {
+ const std::string value = StringUtils::CreateFromCString(attribute->Value());
+ if (!value.empty())
+ {
+ name = "@" + name;
+ extension.emplace_back(std::make_pair(name, SExtValue(value)));
+ }
+ }
+ attribute = attribute->Next();
+ }
+ if (!extension.empty())
+ addonExt.m_values.emplace_back(std::pair<std::string, EXT_VALUE>("", std::move(extension)));
+
+ const TiXmlElement* childElement = element->FirstChildElement();
+ while (childElement)
+ {
+ const std::string id = StringUtils::CreateFromCString(childElement->Value());
+ if (!id.empty())
+ {
+ EXT_VALUE extension;
+ const TiXmlAttribute* attribute = childElement->FirstAttribute();
+ while (attribute)
+ {
+ std::string name = attribute->Name();
+ if (name != "point")
+ {
+ const std::string value = StringUtils::CreateFromCString(attribute->Value());
+ if (!value.empty())
+ {
+ name = id + "@" + name;
+ extension.emplace_back(std::make_pair(name, SExtValue(value)));
+ }
+ }
+ attribute = attribute->Next();
+ }
+
+ const std::string childElementText = StringUtils::CreateFromCString(childElement->GetText());
+
+ if (!childElementText.empty())
+ {
+ extension.emplace_back(std::make_pair(id, SExtValue(childElementText)));
+ }
+
+ if (!extension.empty())
+ addonExt.m_values.emplace_back(std::make_pair(id, std::move(extension)));
+
+ if (childElementText.empty())
+ {
+ const TiXmlElement* childSubElement = childElement->FirstChildElement();
+ if (childSubElement)
+ {
+ CAddonExtensions subElement;
+ if (ParseXMLExtension(subElement, childElement))
+ addonExt.m_children.emplace_back(std::make_pair(id, std::move(subElement)));
+ }
+ }
+ }
+ childElement = childElement->NextSiblingElement();
+ }
+
+ return true;
+}
+
+bool CAddonInfoBuilder::GetTextList(const TiXmlElement* element, const std::string& tag, std::unordered_map<std::string, std::string>& translatedValues)
+{
+ if (!element)
+ return false;
+
+ translatedValues.clear();
+
+ for (const TiXmlElement* child = element->FirstChildElement(tag); child != nullptr; child = child->NextSiblingElement(tag))
+ {
+ const char* lang = child->Attribute("lang");
+ const char* text = child->GetText();
+ if (lang != nullptr)
+ {
+ if (strcmp(lang, "no") == 0)
+ translatedValues.insert(std::make_pair("nb_NO", text != nullptr ? text : ""));
+ else
+ translatedValues.insert(std::make_pair(lang, text != nullptr ? text : ""));
+ }
+ else
+ translatedValues.insert(
+ std::make_pair(KODI_ADDON_DEFAULT_LANGUAGE_CODE, text != nullptr ? text : ""));
+ }
+
+ return !translatedValues.empty();
+}
+
+const char* CAddonInfoBuilder::GetPlatformLibraryName(const TiXmlElement* element)
+{
+ const char* libraryName;
+#if defined(TARGET_ANDROID)
+ libraryName = element->Attribute("library_android");
+#elif defined(TARGET_LINUX) || defined(TARGET_FREEBSD)
+#if defined(TARGET_FREEBSD)
+ libraryName = element->Attribute("library_freebsd");
+ if (libraryName == nullptr)
+#endif
+ libraryName = element->Attribute("library_linux");
+#elif defined(TARGET_WINDOWS_DESKTOP)
+ libraryName = element->Attribute("library_windx");
+ if (libraryName == nullptr)
+ libraryName = element->Attribute("library_windows");
+#elif defined(TARGET_WINDOWS_STORE)
+ libraryName = element->Attribute("library_windowsstore");
+#elif defined(TARGET_DARWIN)
+#if defined(TARGET_DARWIN_EMBEDDED)
+ libraryName = element->Attribute("library_darwin_embedded");
+#else
+ libraryName = element->Attribute("library_osx");
+#endif
+#endif
+
+ return libraryName;
+}
+
+bool CAddonInfoBuilder::PlatformSupportsAddon(const AddonInfoPtr& addon)
+{
+ if (addon->m_platforms.empty())
+ return true;
+
+ std::vector<std::string> supportedPlatforms = {
+ "all",
+#if defined(TARGET_ANDROID)
+ "android",
+#if defined(__ARM_ARCH_7A__)
+ "android-armv7",
+#elif defined(__aarch64__)
+ "android-aarch64",
+#elif defined(__i686__)
+ "android-i686",
+#elif defined(__x86_64__)
+ "android-x86_64",
+#else
+ #warning no architecture dependant platform tag
+#endif
+#elif defined(TARGET_FREEBSD)
+ "freebsd",
+#elif defined(TARGET_LINUX)
+ "linux",
+#if defined(__ARM_ARCH_7A__)
+ "linux-armv7",
+#elif defined(__aarch64__)
+ "linux-aarch64",
+#elif defined(__i686__)
+ "linux-i686",
+#elif defined(__x86_64__)
+ "linux-x86_64",
+#else
+ #warning no architecture dependant platform tag
+#endif
+#elif defined(TARGET_WINDOWS_DESKTOP)
+ "windx",
+ "windows",
+#if defined(_M_IX86)
+ "windows-i686",
+#elif defined(_M_AMD64)
+ "windows-x86_64",
+#else
+#error no architecture dependant platform tag
+#endif
+#elif defined(TARGET_WINDOWS_STORE)
+ "windowsstore",
+#elif defined(TARGET_DARWIN_EMBEDDED)
+ "darwin_embedded",
+#if defined(TARGET_DARWIN_IOS)
+ "ios",
+#if defined(__aarch64__)
+ "ios-aarch64",
+#else
+#warning no architecture dependant platform tag
+#endif
+#elif defined(TARGET_DARWIN_TVOS)
+ "tvos",
+ "tvos-aarch64",
+#endif
+#elif defined(TARGET_DARWIN_OSX)
+ "osx",
+#if defined(__x86_64__)
+ "osx64",
+ "osx-x86_64",
+#elif defined(__aarch64__)
+ "osxarm64",
+ "osx-arm64",
+#else
+#warning no architecture dependant platform tag
+#endif
+#endif
+ };
+
+ return std::find_first_of(addon->m_platforms.begin(), addon->m_platforms.end(),
+ supportedPlatforms.begin(), supportedPlatforms.end()) != addon->m_platforms.end();
+}
+
+}
diff --git a/xbmc/addons/addoninfo/AddonInfoBuilder.h b/xbmc/addons/addoninfo/AddonInfoBuilder.h
new file mode 100644
index 0000000..adbbc84
--- /dev/null
+++ b/xbmc/addons/addoninfo/AddonInfoBuilder.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "addons/IAddon.h"
+
+#include <map>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+class CDateTime;
+class TiXmlElement;
+
+namespace ADDON
+{
+enum class AddonLifecycleState;
+enum class AddonType;
+
+class CAddonExtensions;
+class CAddonType;
+
+struct DependencyInfo;
+struct RepositoryDirInfo;
+
+class CAddonInfo;
+using AddonInfoPtr = std::shared_ptr<CAddonInfo>;
+
+class CAddonInfoBuilder
+{
+public:
+ static AddonInfoPtr Generate(const std::string& id, AddonType type);
+ static AddonInfoPtr Generate(const std::string& addonPath, bool platformCheck = true);
+ static AddonInfoPtr Generate(const TiXmlElement* baseElement,
+ const RepositoryDirInfo& repo,
+ bool platformCheck = true);
+
+ /*!
+ * @brief Parts used from CAddonDatabase
+ */
+ //@{
+ static void SetInstallData(const AddonInfoPtr& addon, const CDateTime& installDate,
+ const CDateTime& lastUpdated, const CDateTime& lastUsed, const std::string& origin);
+ //@}
+
+private:
+ static bool ParseXML(const AddonInfoPtr& addon,
+ const TiXmlElement* element,
+ const std::string& addonPath);
+ static bool ParseXML(const AddonInfoPtr& addon,
+ const TiXmlElement* element,
+ const std::string& addonPath,
+ const RepositoryDirInfo& repo);
+ static bool ParseXMLTypes(CAddonType& addonType,
+ const AddonInfoPtr& info,
+ const TiXmlElement* child);
+ static bool ParseXMLExtension(CAddonExtensions& addonExt, const TiXmlElement* element);
+ static bool GetTextList(const TiXmlElement* element, const std::string& tag, std::unordered_map<std::string, std::string>& translatedValues);
+ static const char* GetPlatformLibraryName(const TiXmlElement* element);
+ static bool PlatformSupportsAddon(const AddonInfoPtr& addon);
+};
+
+class CAddonInfoBuilderFromDB
+{
+public:
+ CAddonInfoBuilderFromDB();
+
+ void SetId(std::string id);
+ void SetName(std::string name);
+ void SetLicense(std::string license);
+ void SetSummary(std::string summary);
+ void SetDescription(std::string description);
+ void SetDisclaimer(std::string disclaimer);
+ void SetAuthor(std::string author);
+ void SetSource(std::string source);
+ void SetWebsite(std::string website);
+ void SetForum(std::string forum);
+ void SetEMail(std::string email);
+ void SetIcon(std::string icon);
+ void SetArt(const std::string& type, std::string value);
+ void SetArt(std::map<std::string, std::string> art);
+ void SetScreenshots(std::vector<std::string> screenshots);
+ void SetChangelog(std::string changelog);
+ void SetLifecycleState(AddonLifecycleState state, std::string description);
+ void SetPath(std::string path);
+ void SetLibName(std::string libname);
+ void SetVersion(CAddonVersion version);
+ void SetDependencies(std::vector<DependencyInfo> dependencies);
+ void SetExtrainfo(InfoMap extrainfo);
+ void SetInstallDate(const CDateTime& installDate);
+ void SetLastUpdated(const CDateTime& lastUpdated);
+ void SetLastUsed(const CDateTime& lastUsed);
+ void SetOrigin(std::string origin);
+ void SetPackageSize(uint64_t size);
+ void SetExtensions(CAddonType addonType);
+
+ const AddonInfoPtr& get() { return m_addonInfo; }
+
+private:
+ AddonInfoPtr m_addonInfo;
+};
+}
diff --git a/xbmc/addons/addoninfo/AddonType.cpp b/xbmc/addons/addoninfo/AddonType.cpp
new file mode 100644
index 0000000..875e66f
--- /dev/null
+++ b/xbmc/addons/addoninfo/AddonType.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "AddonType.h"
+
+#include "addons/addoninfo/AddonInfo.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+
+namespace ADDON
+{
+static const std::set<AddonType> dependencyTypes = {
+ AddonType::SCRAPER_LIBRARY,
+ AddonType::SCRIPT_LIBRARY,
+ AddonType::SCRIPT_MODULE,
+};
+} /* namespace ADDON */
+
+using namespace ADDON;
+
+std::string CAddonType::LibPath() const
+{
+ if (m_libname.empty())
+ return "";
+ return URIUtils::AddFileToFolder(m_path, m_libname);
+}
+
+void CAddonType::SetProvides(const std::string& content)
+{
+ if (!content.empty())
+ {
+ /*
+ * Normally the "provides" becomes added from xml scan, but for add-ons
+ * stored in the database (e.g. repository contents) it might not be
+ * available. Since this information is available in add-on metadata for the
+ * main type (see extrainfo) we take the function contents and insert it if
+ * empty.
+ */
+ if (GetValue("provides").empty())
+ Insert("provides", content);
+
+ for (const auto& provide : StringUtils::Split(content, ' '))
+ {
+ AddonType content = CAddonInfo::TranslateSubContent(provide);
+ if (content != AddonType::UNKNOWN)
+ m_providedSubContent.insert(content);
+ }
+ }
+}
+
+bool CAddonType::IsDependencyType(AddonType type)
+{
+ return dependencyTypes.find(type) != dependencyTypes.end();
+}
diff --git a/xbmc/addons/addoninfo/AddonType.h b/xbmc/addons/addoninfo/AddonType.h
new file mode 100644
index 0000000..9a0ee26
--- /dev/null
+++ b/xbmc/addons/addoninfo/AddonType.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "addons/addoninfo/AddonExtensions.h"
+
+#include <set>
+#include <string>
+
+class TiXmlElement;
+
+namespace ADDON
+{
+
+enum class AddonType
+{
+ UNKNOWN = 0,
+ VISUALIZATION,
+ SKIN,
+ PVRDLL,
+ INPUTSTREAM,
+ GAMEDLL,
+ PERIPHERALDLL,
+ SCRIPT,
+ SCRIPT_WEATHER,
+ SUBTITLE_MODULE,
+ SCRIPT_LYRICS,
+ SCRAPER_ALBUMS,
+ SCRAPER_ARTISTS,
+ SCRAPER_MOVIES,
+ SCRAPER_MUSICVIDEOS,
+ SCRAPER_TVSHOWS,
+ SCREENSAVER,
+ PLUGIN,
+ REPOSITORY,
+ WEB_INTERFACE,
+ SERVICE,
+ AUDIOENCODER,
+ CONTEXTMENU_ITEM,
+ AUDIODECODER,
+ RESOURCE_IMAGES,
+ RESOURCE_LANGUAGE,
+ RESOURCE_UISOUNDS,
+ RESOURCE_GAMES,
+ RESOURCE_FONT,
+ VFS,
+ IMAGEDECODER,
+ SCRAPER_LIBRARY,
+ SCRIPT_LIBRARY,
+ SCRIPT_MODULE,
+ GAME_CONTROLLER,
+ VIDEOCODEC,
+
+ /**
+ * @brief virtual addon types
+ */
+ //@{
+ VIDEO,
+ AUDIO,
+ IMAGE,
+ EXECUTABLE,
+ GAME,
+ //@}
+
+ MAX_TYPES
+};
+
+class CAddonInfoBuilder;
+class CAddonDatabaseSerializer;
+
+class CAddonType : public CAddonExtensions
+{
+public:
+ CAddonType(AddonType type = AddonType::UNKNOWN) : m_type(type) {}
+
+ AddonType Type() const { return m_type; }
+ std::string LibPath() const;
+ const std::string& LibName() const { return m_libname; }
+
+ bool ProvidesSubContent(const AddonType& content) const
+ {
+ return content == AddonType::UNKNOWN
+ ? false
+ : m_type == content || m_providedSubContent.count(content) > 0;
+ }
+
+ bool ProvidesSeveralSubContents() const
+ {
+ return m_providedSubContent.size() > 1;
+ }
+
+ size_t ProvidedSubContents() const
+ {
+ return m_providedSubContent.size();
+ }
+
+ /*!
+ * @brief Indicates whether a given type is a dependency type (e.g. addons which the main type is
+ * a script.module)
+ *
+ * @param[in] type the provided type
+ * @return true if type is one of the dependency types
+ */
+ static bool IsDependencyType(AddonType type);
+
+private:
+ friend class CAddonInfoBuilder;
+ friend class CAddonInfoBuilderFromDB;
+ friend class CAddonDatabaseSerializer;
+
+ void SetProvides(const std::string& content);
+
+ AddonType m_type;
+ std::string m_path;
+ std::string m_libname;
+ std::set<AddonType> m_providedSubContent;
+};
+
+} /* namespace ADDON */
diff --git a/xbmc/addons/addoninfo/CMakeLists.txt b/xbmc/addons/addoninfo/CMakeLists.txt
new file mode 100644
index 0000000..eb66dd3
--- /dev/null
+++ b/xbmc/addons/addoninfo/CMakeLists.txt
@@ -0,0 +1,11 @@
+set(SOURCES AddonInfoBuilder.cpp
+ AddonExtensions.cpp
+ AddonInfo.cpp
+ AddonType.cpp)
+
+set(HEADERS AddonInfoBuilder.h
+ AddonExtensions.h
+ AddonInfo.h
+ AddonType.h)
+
+core_add_library(addons_addoninfo)