diff options
-rw-r--r-- | xbmc/addons/Addon.cpp | 13 | ||||
-rw-r--r-- | xbmc/addons/Addon.h | 2 | ||||
-rw-r--r-- | xbmc/addons/AddonBuilder.cpp | 87 | ||||
-rw-r--r-- | xbmc/addons/AddonBuilder.h | 2 | ||||
-rw-r--r-- | xbmc/addons/AddonDatabase.cpp | 85 | ||||
-rw-r--r-- | xbmc/addons/AddonDatabase.h | 1 | ||||
-rw-r--r-- | xbmc/addons/AddonManager.cpp | 289 | ||||
-rw-r--r-- | xbmc/addons/AddonManager.h | 7 | ||||
-rw-r--r-- | xbmc/addons/ContextMenuAddon.cpp | 76 | ||||
-rw-r--r-- | xbmc/addons/ContextMenuAddon.h | 3 | ||||
-rw-r--r-- | xbmc/addons/ImageResource.cpp | 6 | ||||
-rw-r--r-- | xbmc/addons/ImageResource.h | 2 | ||||
-rw-r--r-- | xbmc/addons/LanguageResource.cpp | 60 | ||||
-rw-r--r-- | xbmc/addons/LanguageResource.h | 2 | ||||
-rw-r--r-- | xbmc/addons/PluginSource.cpp | 32 | ||||
-rw-r--r-- | xbmc/addons/Repository.cpp | 71 | ||||
-rw-r--r-- | xbmc/addons/Repository.h | 3 | ||||
-rw-r--r-- | xbmc/addons/Scraper.cpp | 30 | ||||
-rw-r--r-- | xbmc/addons/Skin.cpp | 37 | ||||
-rw-r--r-- | xbmc/addons/Skin.h | 3 | ||||
-rw-r--r-- | xbmc/addons/Webinterface.cpp | 18 | ||||
-rw-r--r-- | xbmc/addons/Webinterface.h | 5 | ||||
-rw-r--r-- | xbmc/games/addons/GameClient.cpp | 34 |
23 files changed, 654 insertions, 214 deletions
diff --git a/xbmc/addons/Addon.cpp b/xbmc/addons/Addon.cpp index c5720d03bf..354b44643b 100644 --- a/xbmc/addons/Addon.cpp +++ b/xbmc/addons/Addon.cpp @@ -356,9 +356,16 @@ CAddonSettings* CAddon::GetSettings() const std::string CAddon::LibPath() const { - if (m_addonInfo->LibName().empty()) - return ""; - return URIUtils::AddFileToFolder(m_addonInfo->Path(), m_addonInfo->LibName()); + // Get library related to given type on construction + std::string libName = m_addonInfo->Type(m_type)->LibName(); + if (libName.empty()) + { + // If not present fallback to master library + libName = m_addonInfo->LibName(); + if (libName.empty()) + return ""; + } + return URIUtils::AddFileToFolder(m_addonInfo->Path(), libName); } AddonVersion CAddon::GetDependencyVersion(const std::string &dependencyID) const diff --git a/xbmc/addons/Addon.h b/xbmc/addons/Addon.h index 0c3a6688b4..6cf38a91a9 100644 --- a/xbmc/addons/Addon.h +++ b/xbmc/addons/Addon.h @@ -39,6 +39,8 @@ public: TYPE Type() const override { return m_addonInfo->MainType(); } TYPE FullType() const override { return Type(); } bool IsType(TYPE type) const override { return type == m_addonInfo->MainType(); } + const CAddonType* Type(TYPE type) const { return m_addonInfo->Type(type); } + std::string ID() const override{ return m_addonInfo->ID(); } std::string Name() const override { return m_addonInfo->Name(); } bool IsInUse() const override{ return false; }; diff --git a/xbmc/addons/AddonBuilder.cpp b/xbmc/addons/AddonBuilder.cpp index e1bbbcf564..a2cae49c95 100644 --- a/xbmc/addons/AddonBuilder.cpp +++ b/xbmc/addons/AddonBuilder.cpp @@ -31,6 +31,93 @@ using namespace KODI; namespace ADDON { +AddonPtr CAddonBuilder::Generate(const AddonInfoPtr& info, TYPE type) +{ + if (!info || info->ID().empty()) + return AddonPtr(); + + if (type == ADDON_UNKNOWN) + type = info->MainType(); + if (type == ADDON_UNKNOWN) + return std::make_shared<CAddon>(info, ADDON_UNKNOWN); + + // Handle screensaver special cases + if (type == ADDON_SCREENSAVER) + { + // built in screensaver or python screensaver + if (StringUtils::StartsWithNoCase(info->ID(), "screensaver.xbmc.builtin.") || + URIUtils::HasExtension(info->LibName(), ".py")) + return std::make_shared<CAddon>(info, type); + } + + // Handle audio encoder special cases + if (type == ADDON_AUDIOENCODER) + { + // built in audio encoder + if (StringUtils::StartsWithNoCase(info->ID(), "audioencoder.kodi.builtin.")) + return std::make_shared<CAddonDll>(info, type); + } + + switch (type) + { + case ADDON_AUDIODECODER: + case ADDON_AUDIOENCODER: + case ADDON_IMAGEDECODER: + case ADDON_INPUTSTREAM: + case ADDON_PERIPHERALDLL: + case ADDON_VFS: + case ADDON_VIZ: + case ADDON_SCREENSAVER: + return std::make_shared<CAddonDll>(info, type); + case ADDON_PVRDLL: + return std::make_shared<PVR::CPVRClient>(info); + case ADDON_GAMEDLL: + return std::make_shared<GAME::CGameClient>(info); + case ADDON_PLUGIN: + case ADDON_SCRIPT: + return std::make_shared<CPluginSource>(info, type); + case ADDON_SCRIPT_LIBRARY: + case ADDON_SCRIPT_LYRICS: + case ADDON_SCRIPT_MODULE: + case ADDON_SUBTITLE_MODULE: + case ADDON_SCRIPT_WEATHER: + return std::make_shared<CAddon>(info, type); + case ADDON_WEB_INTERFACE: + return std::make_shared<CWebinterface>(info); + case ADDON_SERVICE: + return std::make_shared<CService>(info); + case ADDON_SCRAPER_ALBUMS: + case ADDON_SCRAPER_ARTISTS: + case ADDON_SCRAPER_MOVIES: + case ADDON_SCRAPER_MUSICVIDEOS: + case ADDON_SCRAPER_TVSHOWS: + case ADDON_SCRAPER_LIBRARY: + return std::make_shared<CScraper>(info, type); + case ADDON_SKIN: + return std::make_shared<CSkinInfo>(info); + case ADDON_RESOURCE_FONT: + return std::make_shared<CFontResource>(info); + case ADDON_RESOURCE_IMAGES: + return std::make_shared<CImageResource>(info); + case ADDON_RESOURCE_GAMES: + return std::make_shared<CGameResource>(info); + case ADDON_RESOURCE_LANGUAGE: + return std::make_shared<CLanguageResource>(info); + case ADDON_RESOURCE_UISOUNDS: + return std::make_shared<CUISoundsResource>(info); + case ADDON_REPOSITORY: + return std::make_shared<CRepository>(info); + case ADDON_CONTEXT_ITEM: + return std::make_shared<CContextMenuAddon>(info); + case ADDON_GAME_CONTROLLER: + return std::make_shared<GAME::CController>(info); + default: + break; + } + return AddonPtr(); +} + + std::shared_ptr<IAddon> CAddonBuilder::Build() { if (m_built) diff --git a/xbmc/addons/AddonBuilder.h b/xbmc/addons/AddonBuilder.h index c362501f10..f70107d3fd 100644 --- a/xbmc/addons/AddonBuilder.h +++ b/xbmc/addons/AddonBuilder.h @@ -17,6 +17,8 @@ namespace ADDON class CAddonBuilder { public: + static AddonPtr Generate(const AddonInfoPtr& info, TYPE type); + std::shared_ptr<IAddon> Build(); void SetId(std::string id) { m_addonInfo->m_id = std::move(id); } void SetName(std::string name) { m_addonInfo->m_name = std::move(name); } diff --git a/xbmc/addons/AddonDatabase.cpp b/xbmc/addons/AddonDatabase.cpp index 846d756ba1..ec0aec52db 100644 --- a/xbmc/addons/AddonDatabase.cpp +++ b/xbmc/addons/AddonDatabase.cpp @@ -11,6 +11,7 @@ #include "XBDateTime.h" #include "addons/AddonBuilder.h" #include "addons/AddonManager.h" +#include "addons/addoninfo/AddonInfoBuilder.h" #include "dbwrappers/dataset.h" #include "filesystem/SpecialProtocol.h" #include "utils/JSONVariantParser.h" @@ -115,6 +116,50 @@ static void DeserializeMetadata(const std::string& document, CAddonBuilder& buil builder.SetExtrainfo(std::move(extraInfo)); } +static void DeserializeMetadata(const std::string& document, CAddonInfoBuilder::CFromDB& builder) +{ + CVariant variant; + if (!CJSONVariantParser::Parse(document, variant)) + return; + + builder.SetAuthor(variant["author"].asString()); + builder.SetDisclaimer(variant["disclaimer"].asString()); + builder.SetBroken(variant["broken"].asString()); + builder.SetPackageSize(variant["size"].asUnsignedInteger()); + + builder.SetPath(variant["path"].asString()); + builder.SetIcon(variant["icon"].asString()); + + std::map<std::string, std::string> art; + for (auto it = variant["art"].begin_map(); it != variant["art"].end_map(); ++it) + art.emplace(it->first, it->second.asString()); + builder.SetArt(std::move(art)); + + std::vector<std::string> screenshots; + for (auto it = variant["screenshots"].begin_array(); it != variant["screenshots"].end_array(); ++it) + screenshots.push_back(it->asString()); + builder.SetScreenshots(std::move(screenshots)); + + builder.SetType(CAddonInfo::TranslateType(variant["extensions"][0].asString())); + + { + std::vector<DependencyInfo> deps; + for (auto it = variant["dependencies"].begin_array(); it != variant["dependencies"].end_array(); ++it) + { + deps.emplace_back( + (*it)["addonId"].asString(), + AddonVersion((*it)["version"].asString()), + (*it)["optional"].asBoolean()); + } + builder.SetDependencies(std::move(deps)); + } + + InfoMap extraInfo; + for (auto it = variant["extrainfo"].begin_array(); it != variant["extrainfo"].end_array(); ++it) + extraInfo.emplace((*it)["key"].asString(), (*it)["value"].asString()); + builder.SetExtrainfo(std::move(extraInfo)); +} + CAddonDatabase::CAddonDatabase() = default; CAddonDatabase::~CAddonDatabase() = default; @@ -506,7 +551,7 @@ bool CAddonDatabase::FindByAddonId(const std::string& addonId, ADDON::VECADDONS& m_pDS->query(sql.c_str()); while (!m_pDS->eof()) { - CAddonBuilder builder; + CAddonInfoBuilder::CFromDB builder; builder.SetId(addonId); builder.SetVersion(AddonVersion(m_pDS->fv(0).get_asString())); builder.SetName(m_pDS->fv(1).get_asString()); @@ -516,7 +561,7 @@ bool CAddonDatabase::FindByAddonId(const std::string& addonId, ADDON::VECADDONS& builder.SetChangelog(m_pDS->fv(5).get_asString()); builder.SetOrigin(m_pDS->fv(6).get_asString()); - auto addon = builder.Build(); + auto addon = CAddonBuilder::Generate(builder.get(), ADDON_UNKNOWN); if (addon) addons.push_back(std::move(addon)); else @@ -653,15 +698,17 @@ bool CAddonDatabase::GetAddon(int id, AddonPtr &addon) if (m_pDS2->eof()) return false; - CAddonBuilder builder; + CAddonInfoBuilder::CFromDB builder; builder.SetId(m_pDS2->fv("addonID").get_asString()); builder.SetVersion(AddonVersion(m_pDS2->fv("version").get_asString())); builder.SetName(m_pDS2->fv("name").get_asString()); builder.SetSummary(m_pDS2->fv("summary").get_asString()); builder.SetDescription(m_pDS2->fv("description").get_asString()); DeserializeMetadata(m_pDS2->fv("metadata").get_asString(), builder); - addon = builder.Build(); + + addon = CAddonBuilder::Generate(builder.get(), ADDON_UNKNOWN); return addon != nullptr; + } catch (...) { @@ -740,7 +787,7 @@ bool CAddonDatabase::GetRepositoryContent(const std::string& id, VECADDONS& addo continue; } - CAddonBuilder builder; + CAddonInfoBuilder::CFromDB builder; builder.SetId(addonId); builder.SetVersion(version); builder.SetName(m_pDS->fv("name").get_asString()); @@ -748,7 +795,7 @@ bool CAddonDatabase::GetRepositoryContent(const std::string& id, VECADDONS& addo builder.SetDescription(m_pDS->fv("description").get_asString()); DeserializeMetadata(m_pDS->fv("metadata").get_asString(), builder); - auto addon = builder.Build(); + auto addon = CAddonBuilder::Generate(builder.get(), ADDON_UNKNOWN); if (addon) { if (!result.empty() && result.back()->ID() == addonId) @@ -1144,3 +1191,29 @@ void CAddonDatabase::OnPostUnInstall(const std::string& addonId) CLog::Log(LOGERROR, "%s failed on addon %s", __FUNCTION__, addonId.c_str()); } } + +void CAddonDatabase::GetInstallData(const AddonInfoPtr& addon) +{ + try + { + if (nullptr == m_pDB.get()) + return; + if (nullptr == m_pDS.get()) + return; + + m_pDS->query(PrepareSQL("SELECT * FROM installed WHERE addonID='%s'", addon->ID().c_str())); + if (!m_pDS->eof()) + { + CAddonInfoBuilder::SetInstallData(addon, + CDateTime::FromDBDateTime(m_pDS->fv(3).get_asString()), + CDateTime::FromDBDateTime(m_pDS->fv(4).get_asString()), + CDateTime::FromDBDateTime(m_pDS->fv(5).get_asString()), + m_pDS->fv(6).get_asString()); + } + m_pDS->close(); + } + catch (...) + { + CLog::Log(LOGERROR, "CAddonDatabase::{}: failed", __FUNCTION__); + } +} diff --git a/xbmc/addons/AddonDatabase.h b/xbmc/addons/AddonDatabase.h index 1651b58d2d..95d9da7e35 100644 --- a/xbmc/addons/AddonDatabase.h +++ b/xbmc/addons/AddonDatabase.h @@ -138,6 +138,7 @@ public: bool SetOrigin(const std::string& addonId, const std::string& origin); bool SetLastUsed(const std::string& addonId, const CDateTime& dateTime); + void GetInstallData(const ADDON::AddonInfoPtr& addon); protected: void CreateTables() override; diff --git a/xbmc/addons/AddonManager.cpp b/xbmc/addons/AddonManager.cpp index 997c12a3ac..924c883091 100644 --- a/xbmc/addons/AddonManager.cpp +++ b/xbmc/addons/AddonManager.cpp @@ -10,9 +10,11 @@ #include "LangInfo.h" #include "ServiceBroker.h" +#include "addons/addoninfo/AddonInfoBuilder.h" #include "events/AddonManagementEvent.h" #include "events/EventLog.h" #include "events/NotificationEvent.h" +#include "filesystem/Directory.h" #include "filesystem/File.h" #include "filesystem/SpecialProtocol.h" #include "utils/StringUtils.h" @@ -361,8 +363,8 @@ bool CAddonMgr::Init() return false; } - if (!m_database.Open()) - CLog::Log(LOGFATAL, "ADDONS: Failed to open database"); + if (!m_database.Open()) + CLog::Log(LOGFATAL, "ADDONS: Failed to open database"); FindAddons(); @@ -561,16 +563,11 @@ bool CAddonMgr::FindInstallableById(const std::string& addonId, AddonPtr& result bool CAddonMgr::GetInstalledBinaryAddons(BINARY_ADDON_LIST& binaryAddonList) { CSingleLock lock(m_critSection); - if (!m_cp_context) - return false; - std::vector<CAddonBuilder> builders; - m_database.GetInstalled(builders); - - for (auto builder : builders) + for (auto addon : m_installedAddons) { BINARY_ADDON_LIST_ENTRY binaryAddon; - if (GetInstalledBinaryAddon(builder.GetId(), binaryAddon)) + if (GetInstalledBinaryAddon(addon.first, binaryAddon)) binaryAddonList.push_back(std::move(binaryAddon)); } @@ -580,28 +577,14 @@ bool CAddonMgr::GetInstalledBinaryAddons(BINARY_ADDON_LIST& binaryAddonList) bool CAddonMgr::GetInstalledBinaryAddon(const std::string& addonId, BINARY_ADDON_LIST_ENTRY& binaryAddon) { bool ret = false; - cp_status_t status; CSingleLock lock(m_critSection); - cp_plugin_info_t *cp_addon = cp_get_plugin_info(m_cp_context, addonId.c_str(), &status); - if (status == CP_OK && cp_addon) + AddonInfoPtr addon = GetAddonInfo(addonId); + if (addon) { - cp_extension_t* props = GetFirstExtPoint(cp_addon, ADDON_UNKNOWN); - if (props != nullptr) - { - CAddonBuilder builder; - std::string value = GetPlatformLibraryName(props->plugin->extensions->configuration); - if (!value.empty() && - props->plugin->plugin_path && - strcmp(props->plugin->plugin_path, "") != 0 && - Factory(cp_addon, ADDON_UNKNOWN, builder, true)) - { - binaryAddon = BINARY_ADDON_LIST_ENTRY(!IsAddonDisabled(cp_addon->identifier), std::move(builder.GetAddonInfo())); - ret = true; - } - } - cp_release_info(m_cp_context, cp_addon); + binaryAddon = BINARY_ADDON_LIST_ENTRY(!IsAddonDisabled(addonId), addon); + ret = true; } return ret; @@ -610,46 +593,28 @@ bool CAddonMgr::GetInstalledBinaryAddon(const std::string& addonId, BINARY_ADDON bool CAddonMgr::GetAddonsInternal(const TYPE &type, VECADDONS &addons, bool enabledOnly) { CSingleLock lock(m_critSection); - if (!m_cp_context) - return false; - std::vector<CAddonBuilder> builders; - m_database.GetInstalled(builders); - - for (auto& builder : builders) + for (const auto& addonInfo : m_installedAddons) { - cp_status_t status; - cp_plugin_info_t* cp_addon = cp_get_plugin_info(m_cp_context, builder.GetId().c_str(), &status); - if (status == CP_OK && cp_addon) - { - if (enabledOnly && IsAddonDisabled(cp_addon->identifier)) - { - cp_release_info(m_cp_context, cp_addon); - continue; - } + if (type != ADDON_UNKNOWN && !addonInfo.second->IsType(type)) + continue; - //FIXME: hack for skipping special dependency addons (xbmc.python etc.). - //Will break if any extension point is added to them - cp_extension_t *props = GetFirstExtPoint(cp_addon, type); - if (props == nullptr) - { - cp_release_info(m_cp_context, cp_addon); - continue; - } + if (enabledOnly && IsAddonDisabled(addonInfo.second->ID())) + continue; - AddonPtr addon; - if (Factory(cp_addon, type, builder)) - addon = builder.Build(); - cp_release_info(m_cp_context, cp_addon); + //FIXME: hack for skipping special dependency addons (xbmc.python etc.). + //Will break if any extension point is added to them + if (addonInfo.second->MainType() == ADDON_UNKNOWN) + continue; - if (addon) - { - // if the addon has a running instance, grab that - AddonPtr runningAddon = addon->GetRunningInstance(); - if (runningAddon) - addon = runningAddon; - addons.emplace_back(std::move(addon)); - } + AddonPtr addon = CAddonBuilder::Generate(addonInfo.second, type); + if (addon) + { + // if the addon has a running instance, grab that + AddonPtr runningAddon = addon->GetRunningInstance(); + if (runningAddon) + addon = runningAddon; + addons.emplace_back(std::move(addon)); } } return addons.size() > 0; @@ -659,16 +624,13 @@ bool CAddonMgr::GetAddon(const std::string &str, AddonPtr &addon, const TYPE &ty { CSingleLock lock(m_critSection); - cp_status_t status; - cp_plugin_info_t *cpaddon = cp_get_plugin_info(m_cp_context, str.c_str(), &status); - if (status == CP_OK && cpaddon) + AddonInfoPtr addonInfo = GetAddonInfo(str, type); + if (addonInfo) { - addon = Factory(cpaddon, type); - cp_release_info(m_cp_context, cpaddon); - + addon = CAddonBuilder::Generate(addonInfo, type); if (addon) { - if (enabledOnly && IsAddonDisabled(addon->ID())) + if (enabledOnly && IsAddonDisabled(addonInfo->ID())) return false; // if the addon has a running instance, grab that @@ -678,8 +640,6 @@ bool CAddonMgr::GetAddon(const std::string &str, AddonPtr &addon, const TYPE &ty } return NULL != addon.get(); } - if (cpaddon) - cp_release_info(m_cp_context, cpaddon); return false; } @@ -692,47 +652,42 @@ bool CAddonMgr::HasType(const std::string &id, const TYPE &type) bool CAddonMgr::FindAddons() { - bool result = false; + ADDON_INFO_LIST installedAddons; + + FindAddons(installedAddons, "special://xbmcbin/addons"); + FindAddons(installedAddons, "special://xbmc/addons"); + FindAddons(installedAddons, "special://home/addons"); + + std::set<std::string> installed; + for (const auto& addon : installedAddons) + installed.insert(addon.second->ID()); + CSingleLock lock(m_critSection); - if (m_cp_context) + + // Sync with db + m_database.SyncInstalled(installed, m_systemAddons, m_optionalAddons); + for (const auto& addon : installedAddons) { - result = true; - cp_scan_plugins(m_cp_context, CP_SP_UPGRADE); + m_database.GetInstallData(addon.second); + CLog::Log(LOGNOTICE, "CAddonMgr::{}: {} v{} installed", __FUNCTION__, addon.second->ID(), addon.second->Version().asString()); + } - //Sync with db - { - std::set<std::pair<std::string, std::string>> installed; - cp_status_t status; - int n; - cp_plugin_info_t** cp_addons = cp_get_plugins_info(m_cp_context, &status, &n); - for (int i = 0; i < n; ++i) - { - installed.emplace(cp_addons[i]->identifier, cp_addons[i]->version ? cp_addons[i]->version : ""); - } - cp_release_info(m_cp_context, cp_addons); - // Log separately so list is sorted - for (const auto& installedAddon : installed) - { - CLog::Log(LOGNOTICE, "ADDON: {} v{} installed", installedAddon.first, installedAddon.second); - } + m_installedAddons = std::move(installedAddons); - std::set<std::string> installedIdentifiers; - std::transform( - installed.cbegin(), installed.cend(), - std::inserter(installedIdentifiers, installedIdentifiers.begin()), - [](const decltype(installed)::value_type& p) { return p.first; } - ); - m_database.SyncInstalled(installedIdentifiers, m_systemAddons, m_optionalAddons); - } + // Reload caches + std::set<std::string> tmp; + m_database.GetDisabled(tmp); + m_disabled = std::move(tmp); - // Reload caches - std::set<std::string> tmp; - m_database.GetDisabled(tmp); - m_disabled = std::move(tmp); + tmp.clear(); + m_database.GetBlacklisted(tmp); + m_updateBlacklist = std::move(tmp); - tmp.clear(); - m_database.GetBlacklisted(tmp); - m_updateBlacklist = std::move(tmp); + bool result = false; + if (m_cp_context) + { + result = true; + cp_scan_plugins(m_cp_context, CP_SP_UPGRADE); } return result; @@ -745,6 +700,8 @@ bool CAddonMgr::UnloadAddon(const std::string& addonId) if (!IsAddonInstalled(addonId)) return true; + m_installedAddons.erase(addonId); + if (m_cp_context) { if (cp_uninstall_plugin(m_cp_context, addonId.c_str()) == CP_OK) @@ -838,6 +795,9 @@ void CAddonMgr::UpdateLastUsed(const std::string& id) { CSingleLock lock(m_critSection); m_database.SetLastUsed(id, time); + auto addonInfo = GetAddonInfo(id); + if (addonInfo) + addonInfo->SetLastUsed(time); } m_events.Publish(AddonEvents::MetadataChanged(id)); }); @@ -1188,38 +1148,10 @@ std::string CAddonMgr::GetPlatformLibraryName(cp_cfg_element_t *base) const bool CAddonMgr::LoadAddonDescription(const std::string &directory, AddonPtr &addon) { - auto addonXmlPath = CSpecialProtocol::TranslatePath(URIUtils::AddFileToFolder(directory, "addon.xml")); - - XFILE::CFile file; - XFILE::auto_buffer buffer; - if (file.LoadFile(addonXmlPath, buffer) <= 0) - { - CLog::Log(LOGERROR, "Failed to read '%s'", addonXmlPath.c_str()); - return false; - } - - cp_status_t status; - cp_context_t* context = cp_create_context(&status); - if (!context) - return false; - - auto info = cp_load_plugin_descriptor_from_memory(context, buffer.get(), buffer.size(), &status); - if (info) - { - // Correct the path. load_plugin_descriptor_from_memory sets it to 'memory' - info->plugin_path = static_cast<char*>(malloc(directory.length() + 1)); - strncpy(info->plugin_path, directory.c_str(), directory.length()); - info->plugin_path[directory.length()] = '\0'; - addon = Factory(info, ADDON_UNKNOWN); - - free(info->plugin_path); - info->plugin_path = nullptr; - cp_release_info(context, info); - } - else - CLog::Log(LOGERROR, "Failed to parse '%s'", addonXmlPath.c_str()); + auto addonInfo = CAddonInfoBuilder::Generate(directory); + if (addonInfo) + addon = CAddonBuilder::Generate(addonInfo, ADDON_UNKNOWN); - cp_destroy_context(context); return addon != nullptr; } @@ -1228,49 +1160,28 @@ bool CAddonMgr::AddonsFromRepoXML(const CRepository::DirInfo& repo, const std::s CXBMCTinyXML doc; if (!doc.Parse(xml)) { - CLog::Log(LOGERROR, "CAddonMgr: Failed to parse addons.xml."); + CLog::Log(LOGERROR, "CAddonMgr::{}: Failed to parse addons.xml", __FUNCTION__); return false; } if (doc.RootElement() == nullptr || doc.RootElement()->ValueStr() != "addons") { - CLog::Log(LOGERROR, "CAddonMgr: Failed to parse addons.xml. Malformed."); + CLog::Log(LOGERROR, "CAddonMgr::{}: Failed to parse addons.xml. Malformed", __FUNCTION__); return false; } - // create a context for these addons - cp_status_t status; - cp_context_t *context = cp_create_context(&status); - if (!context) - return false; - // each addon XML should have a UTF-8 declaration - TiXmlDeclaration decl("1.0", "UTF-8", ""); auto element = doc.RootElement()->FirstChildElement("addon"); while (element) { - // dump the XML back to text - std::string xml; - xml << decl; - xml << *element; - cp_status_t status; - cp_plugin_info_t *info = cp_load_plugin_descriptor_from_memory(context, xml.c_str(), xml.size(), &status); - if (info) - { - CAddonBuilder builder; - if (Factory(info, ADDON_UNKNOWN, builder, false, repo)) - { - auto addon = builder.Build(); - if (addon) - addons.push_back(std::move(addon)); - } - free(info->plugin_path); - info->plugin_path = nullptr; - cp_release_info(context, info); - } + auto addonInfo = CAddonInfoBuilder::Generate(element, repo); + auto addon = CAddonBuilder::Generate(addonInfo, ADDON_UNKNOWN); + if (addon) + addons.push_back(std::move(addon)); + element = element->NextSiblingElement("addon"); } - cp_destroy_context(context); + return true; } @@ -1360,4 +1271,50 @@ void cp_logger(cp_log_severity_t level, const char *msg, const char *apid, void CLog::Log(cp_to_clog(level), "ADDON: cpluff: '%s' reports '%s'", apid, msg); } +bool CAddonMgr::GetAddonInfos(AddonInfos& addonInfos, TYPE type) +{ + CSingleLock lock(m_critSection); + + bool forUnknown = type == ADDON_UNKNOWN; + for (auto& info : m_installedAddons) + { + if (info.second->MainType() != ADDON_UNKNOWN && (forUnknown || info.second->IsType(type))) + addonInfos.push_back(info.second); + } + + return !addonInfos.empty(); +} + +const AddonInfoPtr CAddonMgr::GetAddonInfo(const std::string& id, TYPE type /*= ADDON_UNKNOWN*/) +{ + CSingleLock lock(m_critSection); + + auto addon = m_installedAddons.find(id); + if (addon != m_installedAddons.end()) + if ((type == ADDON_UNKNOWN || addon->second->IsType(type))) + return addon->second; + + return nullptr; +} + +void CAddonMgr::FindAddons(ADDON_INFO_LIST& addonmap, const std::string& path) +{ + CFileItemList items; + if (XFILE::CDirectory::GetDirectory(path, items, "", XFILE::DIR_FLAG_NO_FILE_DIRS)) + { + for (int i = 0; i < items.Size(); ++i) + { + std::string path = items[i]->GetPath(); + if (XFILE::CFile::Exists(path + "addon.xml")) + { + AddonInfoPtr addonInfo = CAddonInfoBuilder::Generate(path); + if (addonInfo) + { + addonmap[addonInfo->ID()] = addonInfo; + } + } + } + } +} + } /* namespace ADDON */ diff --git a/xbmc/addons/AddonManager.h b/xbmc/addons/AddonManager.h index 9441f8d519..4d0ebc8143 100644 --- a/xbmc/addons/AddonManager.h +++ b/xbmc/addons/AddonManager.h @@ -23,6 +23,7 @@ namespace ADDON { typedef std::map<TYPE, VECADDONS> MAPADDONS; typedef std::map<TYPE, VECADDONS>::iterator IMAPADDONS; + typedef std::map<std::string, AddonInfoPtr> ADDON_INFO_LIST; typedef std::vector<cp_cfg_element_t*> ELEMENTS; /*! @@ -278,6 +279,9 @@ namespace ADDON static bool Factory(const cp_plugin_info_t* plugin, TYPE type, CAddonBuilder& builder, bool ignoreExtensions = false, const CRepository::DirInfo& repo = {}); static void FillCpluffMetadata(const cp_plugin_info_t* plugin, CAddonBuilder& builder, const CRepository::DirInfo& repo); + bool GetAddonInfos(AddonInfos& addonInfos, TYPE type); + const AddonInfoPtr GetAddonInfo(const std::string& id, TYPE type = ADDON_UNKNOWN); + private: CAddonMgr& operator=(CAddonMgr const&) = delete; /* libcpluff */ @@ -293,6 +297,8 @@ namespace ADDON bool GetAddonsInternal(const TYPE &type, VECADDONS &addons, bool enabledOnly); bool EnableSingle(const std::string& id); + void FindAddons(ADDON_INFO_LIST& addonmap, const std::string& path); + std::set<std::string> m_disabled; std::set<std::string> m_updateBlacklist; static std::map<TYPE, IAddonMgrCallback*> m_managers; @@ -302,6 +308,7 @@ namespace ADDON CBlockingEventSource<AddonEvent> m_unloadEvents; std::set<std::string> m_systemAddons; std::set<std::string> m_optionalAddons; + ADDON_INFO_LIST m_installedAddons; }; }; /* namespace ADDON */ diff --git a/xbmc/addons/ContextMenuAddon.cpp b/xbmc/addons/ContextMenuAddon.cpp index aac8328ba2..9aaa77c269 100644 --- a/xbmc/addons/ContextMenuAddon.cpp +++ b/xbmc/addons/ContextMenuAddon.cpp @@ -20,6 +20,82 @@ namespace ADDON { +CContextMenuAddon::CContextMenuAddon(const AddonInfoPtr& addonInfo) + : CAddon(addonInfo, ADDON_CONTEXT_ITEM) +{ + std::vector<CContextMenuItem> items; + + const CAddonExtensions* menu = Type(ADDON_CONTEXT_ITEM)->GetElement("menu"); + if (menu) + { + int tmp = 0; + ParseMenu(menu, "", tmp); + } + else + { + //backwards compatibility. add first item definition + const CAddonExtensions* elem = Type(ADDON_CONTEXT_ITEM)->GetElement("item"); + if (elem) + { + std::string visCondition = elem->GetValue("visible").asString(); + if (visCondition.empty()) + visCondition = "false"; + + std::string parent = elem->GetValue("parent").asString() == "kodi.core.manage" + ? CContextMenuManager::MANAGE.m_groupId : CContextMenuManager::MAIN.m_groupId; + + auto label = elem->GetValue("label").asString(); + if (StringUtils::IsNaturalNumber(label)) + label = g_localizeStrings.GetAddonString(ID(), atoi(label.c_str())); + + CContextMenuItem menuItem = CContextMenuItem::CreateItem(label, parent, + URIUtils::AddFileToFolder(Path(), Type(ADDON_CONTEXT_ITEM)->LibName()), visCondition, ID()); + + m_items.push_back(menuItem); + } + } +} + +void CContextMenuAddon::ParseMenu( + const CAddonExtensions* elem, + const std::string& parent, + int& anonGroupCount) +{ + auto menuId = elem->GetValue("@id").asString(); + auto menuLabel = elem->GetValue("label").asString(); + if (StringUtils::IsNaturalNumber(menuLabel)) + menuLabel = g_localizeStrings.GetAddonString(ID(), std::stoi(menuLabel)); + + if (menuId.empty()) + { + //anonymous group. create a new unique internal id. + std::stringstream ss; + ss << ID() << ++anonGroupCount; + menuId = ss.str(); + } + + m_items.push_back(CContextMenuItem::CreateGroup(menuLabel, parent, menuId, ID())); + + for (const auto subMenu : elem->GetElements("menu")) + ParseMenu(&subMenu.second, menuId, anonGroupCount); + + for (const auto element : elem->GetElements("item")) + { + std::string visCondition = element.second.GetValue("visible").asString(); + std::string library = element.second.GetValue("@library").asString(); + std::string label = element.second.GetValue("label").asString(); + if (StringUtils::IsNaturalNumber(label)) + label = g_localizeStrings.GetAddonString(ID(), atoi(label.c_str())); + + if (!label.empty() && !library.empty() && !visCondition.empty()) + { + auto menu = CContextMenuItem::CreateItem(label, menuId, + URIUtils::AddFileToFolder(Path(), library), visCondition, ID()); + m_items.push_back(menu); + } + } +} + void CContextMenuAddon::ParseMenu( const AddonInfoPtr& addonInfo, cp_cfg_element_t* elem, diff --git a/xbmc/addons/ContextMenuAddon.h b/xbmc/addons/ContextMenuAddon.h index 4c1999bb9c..e0da3dcd31 100644 --- a/xbmc/addons/ContextMenuAddon.h +++ b/xbmc/addons/ContextMenuAddon.h @@ -26,12 +26,13 @@ namespace ADDON public: static std::unique_ptr<CContextMenuAddon> FromExtension(const AddonInfoPtr& addonInfo, const cp_extension_t* ext); - explicit CContextMenuAddon(const AddonInfoPtr& addonInfo) : CAddon(addonInfo, ADDON_CONTEXT_ITEM) {} + explicit CContextMenuAddon(const AddonInfoPtr& addonInfo); CContextMenuAddon(const AddonInfoPtr& addonInfo, std::vector<CContextMenuItem> items); const std::vector<CContextMenuItem>& GetItems() const { return m_items; }; private: + void ParseMenu(const CAddonExtensions* elem, const std::string& parent, int& anonGroupCount); static void ParseMenu(const AddonInfoPtr& addonInfo, cp_cfg_element_t* elem, const std::string& parent, int& anonGroupCount, std::vector<CContextMenuItem>& items); diff --git a/xbmc/addons/ImageResource.cpp b/xbmc/addons/ImageResource.cpp index efc24a22af..8e4b7b7468 100644 --- a/xbmc/addons/ImageResource.cpp +++ b/xbmc/addons/ImageResource.cpp @@ -29,6 +29,12 @@ CImageResource::CImageResource(const AddonInfoPtr& addonInfo, std::string type) { } +CImageResource::CImageResource(const AddonInfoPtr& addonInfo) + : CResource(addonInfo, ADDON_RESOURCE_IMAGES) +{ + m_type = Type(ADDON_RESOURCE_IMAGES)->GetValue("@type").asString(); +} + void CImageResource::OnPreUnInstall() { CURL xbtUrl; diff --git a/xbmc/addons/ImageResource.h b/xbmc/addons/ImageResource.h index 51ceb1f0e1..5cb2f76261 100644 --- a/xbmc/addons/ImageResource.h +++ b/xbmc/addons/ImageResource.h @@ -23,7 +23,7 @@ class CImageResource : public CResource public: static std::unique_ptr<CImageResource> FromExtension(const AddonInfoPtr& addonInfo, const cp_extension_t* ext); - explicit CImageResource(const AddonInfoPtr& addonInfo) : CResource(addonInfo, ADDON_RESOURCE_IMAGES) {}; + explicit CImageResource(const AddonInfoPtr& addonInfo); CImageResource(const AddonInfoPtr& addonInfo, std::string type); void OnPreUnInstall() override; diff --git a/xbmc/addons/LanguageResource.cpp b/xbmc/addons/LanguageResource.cpp index ca419b6efb..3e839ebfbb 100644 --- a/xbmc/addons/LanguageResource.cpp +++ b/xbmc/addons/LanguageResource.cpp @@ -117,6 +117,66 @@ CLanguageResource::CLanguageResource( m_sortTokens(sortTokens) { } +CLanguageResource::CLanguageResource(const AddonInfoPtr& addonInfo) + : CResource(addonInfo, ADDON_RESOURCE_LANGUAGE) +{ + // parse <extension> attributes + m_locale = CLocale::FromString(Type(ADDON_RESOURCE_LANGUAGE)->GetValue("@locale").asString()); + + // parse <charsets> + const CAddonExtensions* charsetsElement = Type(ADDON_RESOURCE_LANGUAGE)->GetElement("charsets"); + if (charsetsElement != nullptr) + { + m_charsetGui = charsetsElement->GetValue("gui").asString(); + m_forceUnicodeFont = charsetsElement->GetValue("gui@unicodefont").asBoolean(); + m_charsetSubtitle = charsetsElement->GetValue("subtitle").asString(); + } + + // parse <dvd> + const CAddonExtensions* dvdElement = Type(ADDON_RESOURCE_LANGUAGE)->GetElement("dvd"); + if (dvdElement != nullptr) + { + m_dvdLanguageMenu = dvdElement->GetValue("menu").asString(); + m_dvdLanguageAudio = dvdElement->GetValue("audio").asString(); + m_dvdLanguageSubtitle = dvdElement->GetValue("subtitle").asString(); + } + // fall back to the language of the addon if a DVD language is not defined + if (m_dvdLanguageMenu.empty()) + m_dvdLanguageMenu = m_locale.GetLanguageCode(); + if (m_dvdLanguageAudio.empty()) + m_dvdLanguageAudio = m_locale.GetLanguageCode(); + if (m_dvdLanguageSubtitle.empty()) + m_dvdLanguageSubtitle = m_locale.GetLanguageCode(); + + // parse <sorttokens> + const CAddonExtensions* sorttokensElement = Type(ADDON_RESOURCE_LANGUAGE)->GetElement("sorttokens"); + if (sorttokensElement != nullptr) + { + /* First loop goes around rows e.g. + * <token separators="'">L</token> + * <token>Le</token> + * ... + */ + for (auto values : sorttokensElement->GetValues()) + { + /* Second loop goes around the row parts, e.g. + * separators = "'" + * token = Le + */ + std::string token = values.second.GetValue("token").asString(); + std::string separators = values.second.GetValue("token@separators").asString(); + if (!token.empty()) + { + if (separators.empty()) + separators = " ._"; + + for (auto separator : separators) + m_sortTokens.insert(token + separator); + } + } + } +} + bool CLanguageResource::IsInUse() const { return StringUtils::EqualsNoCase(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOCALE_LANGUAGE), ID()); diff --git a/xbmc/addons/LanguageResource.h b/xbmc/addons/LanguageResource.h index 5b44a2dc56..1cc5f06cc3 100644 --- a/xbmc/addons/LanguageResource.h +++ b/xbmc/addons/LanguageResource.h @@ -20,7 +20,7 @@ class CLanguageResource : public CResource public: static std::unique_ptr<CLanguageResource> FromExtension(const AddonInfoPtr& addonInfo, const cp_extension_t* ext); - explicit CLanguageResource(const AddonInfoPtr& addonInfo) : CResource(addonInfo, ADDON_RESOURCE_LANGUAGE), m_forceUnicodeFont(false) {}; + explicit CLanguageResource(const AddonInfoPtr& addonInfo); CLanguageResource(const AddonInfoPtr& addonInfo, const CLocale& locale, diff --git a/xbmc/addons/PluginSource.cpp b/xbmc/addons/PluginSource.cpp index d4b2b4a704..0ab04b674a 100644 --- a/xbmc/addons/PluginSource.cpp +++ b/xbmc/addons/PluginSource.cpp @@ -48,13 +48,33 @@ std::unique_ptr<CPluginSource> CPluginSource::FromExtension(const AddonInfoPtr& return std::unique_ptr<CPluginSource>(p); } -CPluginSource::CPluginSource(const AddonInfoPtr& addonInfo, TYPE addonType) - : CAddon(addonInfo, addonType) +CPluginSource::CPluginSource(const AddonInfoPtr& addonInfo, TYPE addonType) : CAddon(addonInfo, addonType) { - std::string provides; - InfoMap::const_iterator i = m_addonInfo->ExtraInfo().find("provides"); - if (i != m_addonInfo->ExtraInfo().end()) - provides = i->second; + std::string provides = addonInfo->Type(addonType)->GetValue("provides").asString(); + if (!provides.empty()) + addonInfo->AddExtraInfo("provides", provides); + else + { + const auto& i = addonInfo->ExtraInfo().find("provides"); + if (i != addonInfo->ExtraInfo().end()) + provides = i->second; + } + + for (auto values : addonInfo->Type(addonType)->GetValues()) + { + if (values.first != "medialibraryscanpath") + continue; + + std::string url = "plugin://" + ID() + '/'; + std::string content = values.second.GetValue("medialibraryscanpath@content").asString(); + std::string path = values.second.GetValue("medialibraryscanpath").asString(); + if (!path.empty() && path.front() == '/') + path.erase(0, 1); + if (path.compare(0, url.size(), url)) + path.insert(0, url); + m_mediaLibraryScanPaths[content].push_back(CURL(path).GetFileName()); + } + SetProvides(provides); } diff --git a/xbmc/addons/Repository.cpp b/xbmc/addons/Repository.cpp index 5a77bc82b7..8e41dc8943 100644 --- a/xbmc/addons/Repository.cpp +++ b/xbmc/addons/Repository.cpp @@ -178,6 +178,40 @@ CRepository::CRepository(const AddonInfoPtr& addonInfo, DirList dirs) } } +CRepository::CRepository(const AddonInfoPtr& addonInfo) + : CAddon(addonInfo, ADDON_REPOSITORY) +{ + DirList dirs; + AddonVersion version("0.0.0"); + AddonInfoPtr addonver = CServiceBroker::GetAddonMgr().GetAddonInfo("xbmc.addon"); + if (addonver) + version = addonver->Version(); + + for (auto element : Type(ADDON_REPOSITORY)->GetElements("dir")) + { + DirInfo dir = ParseDirConfiguration(element.second); + if (dir.version <= version) + m_dirs.push_back(std::move(dir)); + } + if (!Type(ADDON_REPOSITORY)->GetValue("info").empty()) + { + m_dirs.push_back(ParseDirConfiguration(*Type(ADDON_REPOSITORY))); + } + + for (auto const& dir : m_dirs) + { + CURL datadir(dir.datadir); + if (datadir.IsProtocol("http")) + { + CLog::Log(LOGWARNING, "Repository {} uses plain HTTP for add-on downloads - this is insecure and will make your Kodi installation vulnerable to attacks if enabled!", Name()); + } + else if (datadir.IsProtocol("https") && datadir.HasProtocolOption("verifypeer") && datadir.GetProtocolOption("verifypeer") == "false") + { + CLog::Log(LOGWARNING, "Repository {} disabled peer verification for add-on downloads - this is insecure and will make your Kodi installation vulnerable to attacks if enabled!", Name()); + } + } +} + bool CRepository::FetchChecksum(const std::string& url, std::string& checksum) noexcept { CFile file; @@ -272,6 +306,43 @@ CRepository::FetchStatus CRepository::FetchIfChanged(const std::string& oldCheck return STATUS_OK; } +CRepository::DirInfo CRepository::ParseDirConfiguration(const CAddonExtensions& configuration) +{ + DirInfo dir; + dir.checksum = configuration.GetValue("checksum").asString(); + std::string checksumStr = configuration.GetValue("checksum@verify").asString(); + if (!checksumStr.empty()) + { + dir.checksumType = CDigest::TypeFromString(checksumStr); + } + dir.info = configuration.GetValue("info").asString(); + dir.datadir = configuration.GetValue("datadir").asString(); + dir.artdir = configuration.GetValue("artdir").asString(); + if (dir.artdir.empty()) + { + dir.artdir = dir.datadir; + } + + std::string hashStr = configuration.GetValue("hashes").asString(); + StringUtils::ToLower(hashStr); + if (hashStr == "true") + { + // Deprecated alias + hashStr = "md5"; + } + if (!hashStr.empty() && hashStr != "false") + { + dir.hashType = CDigest::TypeFromString(hashStr); + if (dir.hashType == CDigest::Type::MD5) + { + CLog::Log(LOGWARNING, "CRepository::{}: Repository has MD5 hashes enabled - this hash function is broken and will only guard against unintentional data corruption", __FUNCTION__); + } + } + + dir.version = AddonVersion{configuration.GetValue("@minversion").asString()}; + return dir; +} + CRepositoryUpdateJob::CRepositoryUpdateJob(const RepositoryPtr& repo) : m_repo(repo) {} bool CRepositoryUpdateJob::DoWork() diff --git a/xbmc/addons/Repository.h b/xbmc/addons/Repository.h index 7fe36090e3..132955a00c 100644 --- a/xbmc/addons/Repository.h +++ b/xbmc/addons/Repository.h @@ -38,7 +38,7 @@ namespace ADDON static std::unique_ptr<CRepository> FromExtension(const AddonInfoPtr& addonInfo, const cp_extension_t* ext); - explicit CRepository(const AddonInfoPtr& addonInfo) : CAddon(addonInfo, ADDON_REPOSITORY) {}; + explicit CRepository(const AddonInfoPtr& addonInfo); CRepository(const AddonInfoPtr& addonInfo, DirList dirs); enum FetchStatus @@ -62,6 +62,7 @@ namespace ADDON static bool FetchIndex(const DirInfo& repo, std::string const& digest, VECADDONS& addons) noexcept; static DirInfo ParseDirConfiguration(cp_cfg_element_t* configuration); + static DirInfo ParseDirConfiguration(const CAddonExtensions& configuration); DirList m_dirs; }; diff --git a/xbmc/addons/Scraper.cpp b/xbmc/addons/Scraper.cpp index 39376d9a00..fbbbd7c449 100644 --- a/xbmc/addons/Scraper.cpp +++ b/xbmc/addons/Scraper.cpp @@ -160,7 +160,35 @@ CScraper::CScraper(const AddonInfoPtr& addonInfo, TYPE addonType) , m_requiressettings(false) , m_pathContent(CONTENT_NONE) { - m_isPython = URIUtils::GetExtension(LibPath()) == ".py"; + m_requiressettings = addonInfo->Type(addonType)->GetValue("@requiressettings").asBoolean(); + + CDateTimeSpan persistence; + std::string tmp = addonInfo->Type(addonType)->GetValue("@cachepersistence").asString(); + if (!tmp.empty()) + m_persistence.SetFromTimeString(tmp); + + switch (addonType) + { + case ADDON_SCRAPER_ALBUMS: + m_pathContent = CONTENT_ALBUMS; + break; + case ADDON_SCRAPER_ARTISTS: + m_pathContent = CONTENT_ARTISTS; + break; + case ADDON_SCRAPER_MOVIES: + m_pathContent = CONTENT_MOVIES; + break; + case ADDON_SCRAPER_MUSICVIDEOS: + m_pathContent = CONTENT_MUSICVIDEOS; + break; + case ADDON_SCRAPER_TVSHOWS: + m_pathContent = CONTENT_TVSHOWS; + break; + default: + break; + } + + m_isPython = URIUtils::GetExtension(addonInfo->Type(addonType)->LibPath()) == ".py"; } CScraper::CScraper(const AddonInfoPtr& addonInfo, diff --git a/xbmc/addons/Skin.cpp b/xbmc/addons/Skin.cpp index 39554a25c2..5c7639f966 100644 --- a/xbmc/addons/Skin.cpp +++ b/xbmc/addons/Skin.cpp @@ -218,6 +218,43 @@ CSkinInfo::CSkinInfo( LoadStartupWindows(nullptr); } +CSkinInfo::CSkinInfo(const AddonInfoPtr& addonInfo) : CAddon(addonInfo, ADDON_SKIN) +{ + for (auto values : Type(ADDON_SKIN)->GetValues()) + { + if (values.first != "res") + continue; + + int width = values.second.GetValue("res@width").asInteger(); + int height = values.second.GetValue("res@height").asInteger(); + bool defRes = values.second.GetValue("res@default").asBoolean(); + std::string folder = values.second.GetValue("res@folder").asString(); + std::string strAspect = values.second.GetValue("res@aspect").asString(); + float aspect = 0; + + std::vector<std::string> fracs = StringUtils::Split(strAspect, ':'); + if (fracs.size() == 2) + aspect = (float)(atof(fracs[0].c_str())/atof(fracs[1].c_str())); + if (width > 0 && height > 0) + { + RESOLUTION_INFO res(width, height, aspect, folder); + res.strId = strAspect; // for skin usage, store aspect string in strId + if (defRes) + m_defaultRes = res; + m_resolutions.push_back(res); + } + } + + m_effectsSlowDown = Type(ADDON_SKIN)->GetValue("@effectslowdown").asFloat(); + if (m_effectsSlowDown == 0.0f) + m_effectsSlowDown = 1.f; + + m_debugging = Type(ADDON_SKIN)->GetValue("@debugging").asBoolean(); + + m_settingsUpdateHandler.reset(new CSkinSettingUpdateHandler(*this)); + LoadStartupWindows(nullptr); +} + CSkinInfo::~CSkinInfo() = default; struct closestRes diff --git a/xbmc/addons/Skin.h b/xbmc/addons/Skin.h index ff03faa0b5..fb7721db73 100644 --- a/xbmc/addons/Skin.h +++ b/xbmc/addons/Skin.h @@ -97,10 +97,11 @@ public: static std::unique_ptr<CSkinInfo> FromExtension(const AddonInfoPtr& addonInfo, const cp_extension_t* ext); + explicit CSkinInfo(const AddonInfoPtr& addonInfo); //FIXME: CAddonCallbacksGUI/WindowXML hack explicit CSkinInfo( const AddonInfoPtr& addonInfo, - const RESOLUTION_INFO& resolution = RESOLUTION_INFO()); + const RESOLUTION_INFO& resolution); CSkinInfo( const AddonInfoPtr& addonInfo, diff --git a/xbmc/addons/Webinterface.cpp b/xbmc/addons/Webinterface.cpp index 2cc6abe541..77b35e8fbe 100644 --- a/xbmc/addons/Webinterface.cpp +++ b/xbmc/addons/Webinterface.cpp @@ -39,6 +39,24 @@ CWebinterface::CWebinterface(const AddonInfoPtr& addonInfo, WebinterfaceType typ const std::string &entryPoint) : CAddon(addonInfo, ADDON_WEB_INTERFACE), m_type(type), m_entryPoint(entryPoint) { } +CWebinterface::CWebinterface(const AddonInfoPtr& addonInfo) + : CAddon(addonInfo, ADDON_WEB_INTERFACE), + m_type(WebinterfaceTypeStatic), + m_entryPoint(WEBINTERFACE_DEFAULT_ENTRY_POINT) +{ + // determine the type of the webinterface + std::string webinterfaceType = Type(ADDON_WEB_INTERFACE)->GetValue("@type").asString(); + if (StringUtils::EqualsNoCase(webinterfaceType, "wsgi")) + m_type = WebinterfaceTypeWsgi; + else if (!webinterfaceType.empty() && !StringUtils::EqualsNoCase(webinterfaceType, "static") && !StringUtils::EqualsNoCase(webinterfaceType, "html")) + CLog::Log(LOGWARNING, "CWebinterface::{}: Addon \"{}\" has specified an unsupported type \"{}\"", ID(), webinterfaceType); + + // determine the entry point of the webinterface + std::string entry = Type(ADDON_WEB_INTERFACE)->GetValue("@entry").asString(); + if (!entry.empty()) + m_entryPoint = entry; +} + std::string CWebinterface::GetEntryPoint(const std::string &path) const { if (m_type == WebinterfaceTypeWsgi) diff --git a/xbmc/addons/Webinterface.h b/xbmc/addons/Webinterface.h index 398629afaa..4a3520a1ff 100644 --- a/xbmc/addons/Webinterface.h +++ b/xbmc/addons/Webinterface.h @@ -25,10 +25,7 @@ namespace ADDON public: static std::unique_ptr<CWebinterface> FromExtension(const AddonInfoPtr& addonInfo, const cp_extension_t* ext); - explicit CWebinterface(const AddonInfoPtr& addonInfo) - : CAddon(addonInfo, ADDON_WEB_INTERFACE), - m_type(WebinterfaceTypeStatic), - m_entryPoint(WEBINTERFACE_DEFAULT_ENTRY_POINT) {} + explicit CWebinterface(const AddonInfoPtr& addonInfo); CWebinterface(const AddonInfoPtr& addonInfo, WebinterfaceType type, const std::string &entryPoint); WebinterfaceType GetType() const { return m_type; } diff --git a/xbmc/games/addons/GameClient.cpp b/xbmc/games/addons/GameClient.cpp index 7c093e7476..75949586f0 100644 --- a/xbmc/games/addons/GameClient.cpp +++ b/xbmc/games/addons/GameClient.cpp @@ -100,38 +100,26 @@ std::unique_ptr<CGameClient> CGameClient::FromExtension(const ADDON::AddonInfoPt CGameClient::CGameClient(const ADDON::AddonInfoPtr& addonInfo) : CAddonDll(addonInfo, ADDON::ADDON_GAMEDLL), m_subsystems(CGameClientSubsystem::CreateSubsystems(*this, m_struct, m_critSection)), - m_bSupportsVFS(false), - m_bSupportsStandalone(false), m_bSupportsAllExtensions(false), m_bIsPlaying(false), m_serializeSize(0), m_region(GAME_REGION_UNKNOWN) { - const ADDON::InfoMap& extraInfo = m_addonInfo->ExtraInfo(); - ADDON::InfoMap::const_iterator it; + using namespace ADDON; - it = extraInfo.find(GAME_PROPERTY_EXTENSIONS); - if (it != extraInfo.end()) - { - std::vector<std::string> extensions = StringUtils::Split(it->second, EXTENSION_SEPARATOR); - std::transform(extensions.begin(), extensions.end(), - std::inserter(m_extensions, m_extensions.begin()), NormalizeExtension); + std::vector<std::string> extensions = StringUtils::Split(Type(ADDON_GAMEDLL)->GetValue(GAME_PROPERTY_EXTENSIONS).asString(), EXTENSION_SEPARATOR); + std::transform(extensions.begin(), extensions.end(), + std::inserter(m_extensions, m_extensions.begin()), NormalizeExtension); - // Check for wildcard extension - if (m_extensions.find(EXTENSION_WILDCARD) != m_extensions.end()) - { - m_bSupportsAllExtensions = true; - m_extensions.clear(); - } + // Check for wildcard extension + if (m_extensions.find(EXTENSION_WILDCARD) != m_extensions.end()) + { + m_bSupportsAllExtensions = true; + m_extensions.clear(); } - it = extraInfo.find(GAME_PROPERTY_SUPPORTS_VFS); - if (it != extraInfo.end()) - m_bSupportsVFS = (it->second == "true"); - - it = extraInfo.find(GAME_PROPERTY_SUPPORTS_STANDALONE); - if (it != extraInfo.end()) - m_bSupportsStandalone = (it->second == "true"); + m_bSupportsVFS = addonInfo->Type(ADDON_GAMEDLL)->GetValue(GAME_PROPERTY_SUPPORTS_VFS).asBoolean(); + m_bSupportsStandalone = addonInfo->Type(ADDON_GAMEDLL)->GetValue(GAME_PROPERTY_SUPPORTS_STANDALONE).asBoolean(); } CGameClient::~CGameClient(void) |