From 64a933b29acd42e094815b95b088336dbc540056 Mon Sep 17 00:00:00 2001 From: peak3d Date: Mon, 4 Feb 2019 09:01:46 +0100 Subject: [PATCH] [Binary Addon] provide API versions to addon for better downgrade handling --- xbmc/DynamicDll.h | 6 ++- xbmc/addons/binary-addons/AddonDll.cpp | 38 ++++++++++++----- xbmc/addons/binary-addons/DllAddon.h | 8 ++++ .../include/kodi/AddonBase.h | 41 ++++++++++++++++--- .../include/kodi/xbmc_addon_dll.h | 5 +++ 5 files changed, 82 insertions(+), 16 deletions(-) diff --git a/xbmc/DynamicDll.h b/xbmc/DynamicDll.h index 4f51a99b7e287..3a7c535a3b813 100644 --- a/xbmc/DynamicDll.h +++ b/xbmc/DynamicDll.h @@ -125,7 +125,7 @@ public: \ public: \ virtual result name args override \ { \ - return m_##name args2; \ + return m_##name ? m_##name args2 : (result) 0; \ } #define DEFINE_METHOD_LINKAGE0(result, linkage, name) \ @@ -403,6 +403,10 @@ public: \ if (!m_dll->ResolveExport( #dllmethod , & m_##method##_ptr )) \ return false; +#define RESOLVE_METHOD_RENAME_OPTIONAL(dllmethod, method) \ + m_##method##_ptr = nullptr; \ + m_dll->ResolveExport( #dllmethod , & m_##method##_ptr ); + #define RESOLVE_METHOD_RENAME_FP(dllmethod, method) \ if (!m_dll->ResolveExport( #dllmethod , & method##_ptr )) \ return false; diff --git a/xbmc/addons/binary-addons/AddonDll.cpp b/xbmc/addons/binary-addons/AddonDll.cpp index 0562cc1570435..2fb2c2cd843fc 100644 --- a/xbmc/addons/binary-addons/AddonDll.cpp +++ b/xbmc/addons/binary-addons/AddonDll.cpp @@ -215,7 +215,10 @@ ADDON_STATUS CAddonDll::Create(ADDON_TYPE type, void* funcTable, void* info) /* Call Create to make connections, initializing data or whatever is needed to become the AddOn running */ - ADDON_STATUS status = m_pDll->Create(m_pHelpers->GetCallbacks(), info); + ADDON_STATUS status = m_pDll->CreateEx_available() + ? m_pDll->CreateEx(m_pHelpers->GetCallbacks(), kodi::addon::GetTypeVersion(ADDON_GLOBAL_MAIN), info) + : m_pDll->Create(m_pHelpers->GetCallbacks(), info); + if (status == ADDON_STATUS_OK) { m_initialized = true; @@ -263,7 +266,10 @@ ADDON_STATUS CAddonDll::Create(KODI_HANDLE firstKodiInstance) /* Call Create to make connections, initializing data or whatever is needed to become the AddOn running */ - ADDON_STATUS status = m_pDll->Create(&m_interface, nullptr); + ADDON_STATUS status = m_pDll->CreateEx_available() + ? m_pDll->CreateEx(&m_interface, kodi::addon::GetTypeVersion(ADDON_GLOBAL_MAIN), nullptr) + : m_pDll->Create(&m_interface, nullptr); + if (status == ADDON_STATUS_OK) { m_initialized = true; @@ -323,7 +329,11 @@ ADDON_STATUS CAddonDll::CreateInstance(ADDON_TYPE instanceType, const std::strin return ADDON_STATUS_PERMANENT_FAILURE; KODI_HANDLE addonInstance; - status = m_interface.toAddon->create_instance(instanceType, instanceID.c_str(), instance, &addonInstance, parentInstance); + if (!m_interface.toAddon->create_instance_ex) + status = m_interface.toAddon->create_instance(instanceType, instanceID.c_str(), instance, &addonInstance, parentInstance); + else + status = m_interface.toAddon->create_instance_ex(instanceType, instanceID.c_str(), instance, &addonInstance, parentInstance, kodi::addon::GetTypeVersion(instanceType)); + if (status == ADDON_STATUS_OK) { m_usedInstances[instanceID] = std::make_pair(instanceType, addonInstance); @@ -334,6 +344,9 @@ ADDON_STATUS CAddonDll::CreateInstance(ADDON_TYPE instanceType, const std::strin void CAddonDll::DestroyInstance(const std::string& instanceID) { + if (m_usedInstances.empty()) + return; + auto it = m_usedInstances.find(instanceID); if (it != m_usedInstances.end()) { @@ -450,6 +463,9 @@ bool CAddonDll::CheckAPIVersion(int type) /* check the API version */ AddonVersion kodiMinVersion(kodi::addon::GetTypeMinVersion(type)); AddonVersion addonVersion(m_pDll->GetAddonTypeVersion(type)); + AddonVersion addonMinVersion = m_pDll->GetAddonTypeMinVersion_available() + ? AddonVersion(m_pDll->GetAddonTypeMinVersion(type)) + : addonVersion; /* Check the global usage from addon * if not used from addon becomes "0.0.0" returned @@ -461,13 +477,15 @@ bool CAddonDll::CheckAPIVersion(int type) * present. */ if (kodiMinVersion > addonVersion || - addonVersion > AddonVersion(kodi::addon::GetTypeVersion(type))) - { - CLog::Log(LOGERROR, "Add-on '%s' is using an incompatible API version for type '%s'. Kodi API min version = '%s', add-on API version '%s'", - Name().c_str(), - kodi::addon::GetTypeName(type), - kodiMinVersion.asString().c_str(), - addonVersion.asString().c_str()); + addonMinVersion > AddonVersion(kodi::addon::GetTypeVersion(type))) + { + CLog::Log(LOGERROR, "Add-on '{}' is using an incompatible API version for type '{}'. Kodi API min version = '{}/{}', add-on API version '{}/{}'", + Name(), + kodi::addon::GetTypeName(type), + kodi::addon::GetTypeVersion(type), + kodiMinVersion.asString(), + addonMinVersion.asString(), + addonVersion.asString()); CEventLog &eventLog = CServiceBroker::GetEventLog(); eventLog.AddWithNotification(EventPtr(new CNotificationEvent(Name(), 24152, EventLevel::Error))); diff --git a/xbmc/addons/binary-addons/DllAddon.h b/xbmc/addons/binary-addons/DllAddon.h index 7350b2ee2ff65..806c0f49a8768 100644 --- a/xbmc/addons/binary-addons/DllAddon.h +++ b/xbmc/addons/binary-addons/DllAddon.h @@ -17,10 +17,12 @@ class DllAddonInterface virtual ~DllAddonInterface() = default; virtual void GetAddon(void* pAddon) =0; virtual ADDON_STATUS Create(void *cb, void *info) =0; + virtual ADDON_STATUS CreateEx(void *cb, const char* globalApiVersion, void *info) = 0; virtual void Destroy() =0; virtual ADDON_STATUS GetStatus() =0; virtual ADDON_STATUS SetSetting(const char *settingName, const void *settingValue) =0; virtual const char* GetAddonTypeVersion(int type)=0; + virtual const char* GetAddonTypeMinVersion(int type) = 0; }; class DllAddon : public DllDynamic, public DllAddonInterface @@ -28,18 +30,24 @@ class DllAddon : public DllDynamic, public DllAddonInterface public: DECLARE_DLL_WRAPPER_TEMPLATE(DllAddon) DEFINE_METHOD2(ADDON_STATUS, Create, (void* p1, void* p2)) + DEFINE_METHOD3(ADDON_STATUS, CreateEx, (void* p1, const char* p2, void* p3)) + bool CreateEx_available() { return m_CreateEx != nullptr; } DEFINE_METHOD0(void, Destroy) DEFINE_METHOD0(ADDON_STATUS, GetStatus) DEFINE_METHOD2(ADDON_STATUS, SetSetting, (const char *p1, const void *p2)) DEFINE_METHOD1(void, GetAddon, (void* p1)) DEFINE_METHOD1(const char*, GetAddonTypeVersion, (int p1)) + DEFINE_METHOD1(const char*, GetAddonTypeMinVersion, (int p1)) + bool GetAddonTypeMinVersion_available() { return m_GetAddonTypeMinVersion != nullptr; } BEGIN_METHOD_RESOLVE() RESOLVE_METHOD_RENAME(get_addon,GetAddon) RESOLVE_METHOD_RENAME(ADDON_Create, Create) + RESOLVE_METHOD_RENAME_OPTIONAL(ADDON_CreateEx, CreateEx) RESOLVE_METHOD_RENAME(ADDON_Destroy, Destroy) RESOLVE_METHOD_RENAME(ADDON_GetStatus, GetStatus) RESOLVE_METHOD_RENAME(ADDON_SetSetting, SetSetting) RESOLVE_METHOD_RENAME(ADDON_GetTypeVersion, GetAddonTypeVersion) + RESOLVE_METHOD_RENAME_OPTIONAL(ADDON_GetTypeMinVersion, GetAddonTypeMinVersion) END_METHOD_RESOLVE() }; diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/AddonBase.h b/xbmc/addons/kodi-addon-dev-kit/include/kodi/AddonBase.h index 8e45693a16b69..5325ebfea2ade 100644 --- a/xbmc/addons/kodi-addon-dev-kit/include/kodi/AddonBase.h +++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/AddonBase.h @@ -193,6 +193,7 @@ typedef struct KodiToAddonFuncTable_Addon ADDON_STATUS (*create_instance)(int instanceType, const char* instanceID, KODI_HANDLE instance, KODI_HANDLE* addonInstance, KODI_HANDLE parent); void (*destroy_instance)(int instanceType, KODI_HANDLE instance); ADDON_STATUS (*set_setting)(const char *settingName, const void *settingValue); + ADDON_STATUS(*create_instance_ex)(int instanceType, const char* instanceID, KODI_HANDLE instance, KODI_HANDLE* addonInstance, KODI_HANDLE parent, const char* version); } KodiToAddonFuncTable_Addon; /* @@ -251,6 +252,11 @@ class IAddonInstance return ADDON_STATUS_NOT_IMPLEMENTED; } + virtual ADDON_STATUS CreateInstanceEx(int instanceType, std::string instanceID, KODI_HANDLE instance, KODI_HANDLE& addonInstance, const std::string &version) + { + return CreateInstance(instanceType, instanceID, instance, addonInstance); + } + const ADDON_TYPE m_type; }; } /* namespace addon */ @@ -292,6 +298,9 @@ class ATTRIBUTE_HIDDEN CAddonBase CAddonBase::m_interface->toAddon->create_instance = ADDONBASE_CreateInstance; CAddonBase::m_interface->toAddon->destroy_instance = ADDONBASE_DestroyInstance; CAddonBase::m_interface->toAddon->set_setting = ADDONBASE_SetSetting; + // If version is present, we know that kodi has create_instance_ex implemented + if (!CAddonBase::m_strGlobalApiVersion.empty()) + CAddonBase::m_interface->toAddon->create_instance_ex = ADDONBASE_CreateInstanceEx; } virtual ~CAddonBase() = default; @@ -320,8 +329,14 @@ class ATTRIBUTE_HIDDEN CAddonBase return ADDON_STATUS_UNKNOWN; } + virtual ADDON_STATUS CreateInstanceEx(int instanceType, std::string instanceID, KODI_HANDLE instance, KODI_HANDLE& addonInstance, const std::string &version) + { + return CreateInstance(instanceType, instanceID, instance, addonInstance); + } + /* Global variables of class */ static AddonGlobalInterface* m_interface; // Interface function table to hold addresses on add-on and from kodi + static std::string m_strGlobalApiVersion; /*private:*/ /* Needed public as long the old call functions becomes used! */ static inline void ADDONBASE_Destroy() @@ -338,17 +353,22 @@ class ATTRIBUTE_HIDDEN CAddonBase } static inline ADDON_STATUS ADDONBASE_CreateInstance(int instanceType, const char* instanceID, KODI_HANDLE instance, KODI_HANDLE* addonInstance, KODI_HANDLE parent) + { + return ADDONBASE_CreateInstanceEx(instanceType, instanceID, instance, addonInstance, parent, nullptr); + } + + static inline ADDON_STATUS ADDONBASE_CreateInstanceEx(int instanceType, const char* instanceID, KODI_HANDLE instance, KODI_HANDLE* addonInstance, KODI_HANDLE parent, const char* version) { ADDON_STATUS status = ADDON_STATUS_NOT_IMPLEMENTED; if (parent != nullptr) - status = static_cast(parent)->CreateInstance(instanceType, instanceID, instance, *addonInstance); + status = static_cast(parent)->CreateInstanceEx(instanceType, instanceID, instance, *addonInstance, version); if (status == ADDON_STATUS_NOT_IMPLEMENTED) - status = CAddonBase::m_interface->addonBase->CreateInstance(instanceType, instanceID, instance, *addonInstance); + status = CAddonBase::m_interface->addonBase->CreateInstanceEx(instanceType, instanceID, instance, *addonInstance, version); if (*addonInstance == nullptr) - throw std::logic_error("kodi::addon::CAddonBase CreateInstance returns a empty instance pointer!"); + throw std::logic_error("kodi::addon::CAddonBase CreateInstanceEx returns a empty instance pointer!"); if (static_cast<::kodi::addon::IAddonInstance*>(*addonInstance)->m_type != instanceType) - throw std::logic_error("kodi::addon::CAddonBase CreateInstance with difference on given and returned instance type!"); + throw std::logic_error("kodi::addon::CAddonBase CreateInstanceEx with difference on given and returned instance type!"); return status; } @@ -650,6 +670,11 @@ inline void* GetInterface(const std::string &name, const std::string &version) kodi::addon::CAddonBase::m_interface->addonBase = new AddonClass; \ return kodi::addon::CAddonBase::m_interface->addonBase->Create(); \ } \ + extern "C" __declspec(dllexport) ADDON_STATUS ADDON_CreateEx(KODI_HANDLE addonInterface, const char* globalApiVersion, void *unused) \ + { \ + kodi::addon::CAddonBase::m_strGlobalApiVersion = globalApiVersion; \ + return ADDON_Create(addonInterface, unused); \ + } \ extern "C" __declspec(dllexport) void ADDON_Destroy() \ { \ kodi::addon::CAddonBase::ADDONBASE_Destroy(); \ @@ -666,4 +691,10 @@ inline void* GetInterface(const std::string &name, const std::string &version) { \ return kodi::addon::GetTypeVersion(type); \ } \ - AddonGlobalInterface* kodi::addon::CAddonBase::m_interface = nullptr; + extern "C" __declspec(dllexport) const char* ADDON_GetTypeMinVersion(int type) \ + { \ + return kodi::addon::GetTypeMinVersion(type); \ + } \ + AddonGlobalInterface* kodi::addon::CAddonBase::m_interface = nullptr; \ + std::string kodi::addon::CAddonBase::m_strGlobalApiVersion; + diff --git a/xbmc/addons/kodi-addon-dev-kit/include/kodi/xbmc_addon_dll.h b/xbmc/addons/kodi-addon-dev-kit/include/kodi/xbmc_addon_dll.h index e9e7d9ac871c7..ce2bc9801ec0e 100644 --- a/xbmc/addons/kodi-addon-dev-kit/include/kodi/xbmc_addon_dll.h +++ b/xbmc/addons/kodi-addon-dev-kit/include/kodi/xbmc_addon_dll.h @@ -15,6 +15,7 @@ extern "C" { #endif ADDON_STATUS __declspec(dllexport) ADDON_Create(void *callbacks, void* props); + ADDON_STATUS __declspec(dllexport) ADDON_CreateEx(void *callbacks, const char* globalApiVersion, void* props); void __declspec(dllexport) ADDON_Destroy(); ADDON_STATUS __declspec(dllexport) ADDON_GetStatus(); ADDON_STATUS __declspec(dllexport) ADDON_SetSetting(const char *settingName, const void *settingValue); @@ -22,6 +23,10 @@ extern "C" { { return kodi::addon::GetTypeVersion(type); } + __declspec(dllexport) const char* ADDON_GetTypeMinVersion(int type) + { + return kodi::addon::GetTypeMinVersion(type); + } #ifdef __cplusplus };